Added a depth-first chapter iterator

This commit is contained in:
Michael Bryan 2017-07-07 21:00:15 +08:00
parent 1b5a58902f
commit b925c7c41c
2 changed files with 89 additions and 1 deletions

View File

@ -1,6 +1,7 @@
#![allow(missing_docs, unused_variables, unused_imports, dead_code)] #![allow(missing_docs, unused_variables, unused_imports, dead_code)]
use std::path::Path; use std::path::Path;
use std::collections::VecDeque;
use std::fs::File; use std::fs::File;
use std::io::Read; use std::io::Read;
@ -22,6 +23,11 @@ impl Book {
pub fn new() -> Self { pub fn new() -> Self {
Default::default() 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. /// Enum representing any type of item which can be added to a book.
@ -117,6 +123,29 @@ fn load_chapter<P: AsRef<Path>>(link: &Link, src_dir: P) -> Result<Chapter> {
Ok(ch) 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<Self::Item> {
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)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@ -231,4 +260,63 @@ And here is some more text.
assert_eq!(got, should_be); 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<String> = 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);
}
} }

View File

@ -49,7 +49,7 @@ mod summary;
mod book; mod book;
pub use self::summary::{Summary, Link, SummaryItem, parse_summary, SectionNumber}; 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 /// The object in charge of parsing the source directory into a usable