From b925c7c41cf266805547c6120589f53b902ecc23 Mon Sep 17 00:00:00 2001 From: Michael Bryan Date: Fri, 7 Jul 2017 21:00:15 +0800 Subject: [PATCH] Added a depth-first chapter iterator --- src/loader/book.rs | 88 ++++++++++++++++++++++++++++++++++++++++++++++ src/loader/mod.rs | 2 +- 2 files changed, 89 insertions(+), 1 deletion(-) diff --git a/src/loader/book.rs b/src/loader/book.rs index 7721b44b..4c2e6ba2 100644 --- a/src/loader/book.rs +++ b/src/loader/book.rs @@ -1,6 +1,7 @@ #![allow(missing_docs, unused_variables, unused_imports, dead_code)] use std::path::Path; +use std::collections::VecDeque; use std::fs::File; use std::io::Read; @@ -22,6 +23,11 @@ impl Book { pub fn new() -> Self { Default::default() } + + /// Get a depth-first iterator over the items in the book. + pub fn iter(&self) -> BookItems { + BookItems { items: self.sections.iter().collect() } + } } /// Enum representing any type of item which can be added to a book. @@ -117,6 +123,29 @@ fn load_chapter>(link: &Link, src_dir: P) -> Result { Ok(ch) } +/// A depth-first iterator over the items in a book. +pub struct BookItems<'a> { + items: VecDeque<&'a BookItem>, +} + +impl<'a> Iterator for BookItems<'a> { + type Item = &'a BookItem; + + fn next(&mut self) -> Option { + let item = self.items.pop_front(); + + if let Some(&BookItem::Chapter(ref ch)) = item { + // if we wanted a breadth-first iterator we'd `extend()` here + for sub_item in ch.sub_items.iter().rev() { + self.items.push_front(sub_item); + } + } + + item + } +} + + #[cfg(test)] mod tests { use super::*; @@ -231,4 +260,63 @@ And here is some more text. assert_eq!(got, should_be); } + + #[test] + fn book_iter_iterates_over_sequential_items() { + let book = Book { + sections: vec![ + BookItem::Chapter(Chapter { + name: String::from("Chapter 1"), + content: String::from(DUMMY_SRC), + ..Default::default() + }), + BookItem::Separator, + ], + }; + + let should_be: Vec<_> = book.sections.iter().collect(); + + let got: Vec<_> = book.iter().collect(); + + assert_eq!(got, should_be); + } + + #[test] + fn iterate_over_nested_book_items() { + let book = Book { + sections: vec![ + BookItem::Chapter(Chapter { + name: String::from("Chapter 1"), + content: String::from(DUMMY_SRC), + number: None, + sub_items: vec![ + BookItem::Chapter(Chapter::new("Hello World", String::new())), + BookItem::Separator, + BookItem::Chapter(Chapter::new("Goodbye World", String::new())), + ], + }), + BookItem::Separator, + ], + }; + + + let got: Vec<_> = book.iter().collect(); + + assert_eq!(got.len(), 5); + + // checking the chapter names are in the order should be sufficient here... + let chapter_names: Vec = got.into_iter() + .filter_map(|i| match *i { + BookItem::Chapter(ref ch) => Some(ch.name.clone()), + _ => None, + }) + .collect(); + let should_be: Vec<_> = vec![ + String::from("Chapter 1"), + String::from("Hello World"), + String::from("Goodbye World"), + ]; + + assert_eq!(chapter_names, should_be); + } } \ No newline at end of file diff --git a/src/loader/mod.rs b/src/loader/mod.rs index eff82505..145aa8b0 100644 --- a/src/loader/mod.rs +++ b/src/loader/mod.rs @@ -49,7 +49,7 @@ mod summary; mod book; pub use self::summary::{Summary, Link, SummaryItem, parse_summary, SectionNumber}; -pub use self::book::{Book, load_book_from_disk, BookItem, Chapter}; +pub use self::book::{Book, BookItems, load_book_from_disk, BookItem, Chapter}; /// The object in charge of parsing the source directory into a usable