Added some of the infrastructure for parsing the nested section and updating the summary ergonomically

This commit is contained in:
Michael Bryan 2017-06-29 00:06:43 +08:00
parent bfc9112c06
commit 56dad889db
1 changed files with 140 additions and 7 deletions

View File

@ -2,7 +2,7 @@
use std::fmt::{self, Formatter, Display}; use std::fmt::{self, Formatter, Display};
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use std::path::PathBuf; use std::path::{Path, PathBuf};
use pulldown_cmark::{self, Event, Tag}; use pulldown_cmark::{self, Event, Tag};
use errors::*; use errors::*;
@ -70,12 +70,32 @@ struct Link {
nested_items: Vec<SummaryItem>, nested_items: Vec<SummaryItem>,
} }
impl Link {
fn new<S: Into<String>, P: AsRef<Path>>(name: S, location: P) -> Link {
Link {
name: name.into(),
location: location.as_ref().to_path_buf(),
number: None,
nested_items: Vec::new(),
}
}
}
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
enum SummaryItem { enum SummaryItem {
Link(Link), Link(Link),
Separator, Separator,
} }
impl SummaryItem {
fn maybe_link_mut(&mut self) -> Option<&mut Link> {
match *self {
SummaryItem::Link(ref mut l) => Some(l),
_ => None,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq)] #[derive(Debug, Copy, Clone, PartialEq)]
enum State { enum State {
Begin, Begin,
@ -169,11 +189,12 @@ impl<'a> SummaryParser<'a> {
let next_event = self.stream.next().expect("TODO: error-chain"); let next_event = self.stream.next().expect("TODO: error-chain");
trace!("[*] Current state = {:?}, Next Event = {:?}", self.state, next_event); trace!("[*] Current state = {:?}, Next Event = {:?}", self.state, next_event);
println!("{:?}", next_event); println!("{:?}", next_event);
match self.state { match self.state {
State::Begin => self.step_start(next_event)?, State::Begin => self.step_start(next_event)?,
State::PrefixChapters => self.step_prefix(next_event)?, State::PrefixChapters => self.step_prefix(next_event)?,
_ => unimplemented!() State::NumberedChapters(n) => self.step_numbered(next_event, n)?,
_ => unimplemented!(),
} }
Ok(()) Ok(())
@ -184,7 +205,7 @@ impl<'a> SummaryParser<'a> {
fn step_start(&mut self, event: Event<'a>) -> Result<()> { fn step_start(&mut self, event: Event<'a>) -> Result<()> {
match event { match event {
Event::Start(Tag::Paragraph) => self.state = State::PrefixChapters, Event::Start(Tag::Paragraph) => self.state = State::PrefixChapters,
other => bail!("Unexpected tag! {:?}", other), other => bail!("Expected a start of paragraph but got {:?}", other),
} }
Ok(()) Ok(())
@ -210,15 +231,19 @@ impl<'a> SummaryParser<'a> {
debug!("[*] Found a prefix chapter, {:?}", link.name); debug!("[*] Found a prefix chapter, {:?}", link.name);
self.summary.prefix_chapters.push(SummaryItem::Link(link)); self.summary.prefix_chapters.push(SummaryItem::Link(link));
} },
Event::End(Tag::Rule) => { Event::End(Tag::Rule) => {
debug!("[*] Found a prefix chapter separator"); debug!("[*] Found a prefix chapter separator");
self.summary.prefix_chapters.push(SummaryItem::Separator); self.summary.prefix_chapters.push(SummaryItem::Separator);
} },
Event::Start(Tag::List(_)) => {
debug!("[*] Changing from prefix chapters to numbered chapters");
self.state = State::NumberedChapters(0);
},
other => { other => {
debug!("[*] Skipping unexpected token in summary: {:?}", other); debug!("[*] Skipping unexpected token in summary: {:?}", other);
} },
} }
Ok(()) Ok(())
@ -256,8 +281,69 @@ impl<'a> SummaryParser<'a> {
bail!("Expected a link, got {:?}", next) bail!("Expected a link, got {:?}", next)
} }
} }
/// Parse the numbered chapters.
///
/// If the event is the start of a list item, consume the entire item and
/// add a new link to the summary with `push_numbered_section`.
///
/// If the event is the start of a new list, bump the nesting level.
///
/// If the event is the end of a list, decrement the nesting level. When
/// the nesting level would go negative, we've finished the numbered
/// section and need to parse the suffix section.
///
/// Otherwise, ignore the event.
fn step_numbered(&mut self, event: Event, nesting: u32) -> Result<()> {
match event {
Event::Start(Tag::Item) => unimplemented!(),
other => unimplemented!()
}
}
/// Push a new section at the end of the current nesting level.
fn push_numbered_section(&mut self, item: SummaryItem) {
if let State::NumberedChapters(level) = self.state {
unimplemented!()
} else {
// this method should only ever be called when parsing a numbered
// section, therefore if we ever get here something has gone
// hideously wrong...
error!("Calling push_numbered_section() when not in a numbered section");
error!("Current state: {:?}", self.state);
error!("Item: {:?}", item);
error!("Summary:");
error!("{:#?}", self.summary);
panic!("Entered unreachable code, this is a bug");
}
}
} }
fn push_item_at_nesting_level(root: &mut Link, item: SummaryItem, level: usize) -> Result<SectionNumber> {
if level == 0 {
root.nested_items.push(item);
Ok(SectionNumber(vec![root.nested_items.len() as u32]))
} else {
let next_level = level - 1;
let index_for_item = root.nested_items.len() + 1;
let (index, last_link) =
root.nested_items
.iter_mut()
.enumerate()
.filter_map(|(i, item)| item.maybe_link_mut().map(|l| (i, l)))
.rev()
.next()
.ok_or(format!("Can't recurse any further, summary needs to be {} more levels deep", level))?;
let mut section_number = push_item_at_nesting_level(last_link, item, level - 1)?;
section_number.insert(0, index as u32 + 1);
println!("{:?}\t{:?}", section_number, last_link);
Ok(section_number)
}
}
/// Extracts the text from formatted markdown. /// Extracts the text from formatted markdown.
fn stringify_events<'a>(events: Vec<Event<'a>>) -> String { fn stringify_events<'a>(events: Vec<Event<'a>>) -> String {
events events
@ -427,4 +513,51 @@ mod tests {
assert_eq!(parser.summary.prefix_chapters[0], should_be); assert_eq!(parser.summary.prefix_chapters[0], should_be);
assert_eq!(parser.state, State::PrefixChapters); assert_eq!(parser.state, State::PrefixChapters);
} }
#[test]
fn step_from_prefix_chapters_to_numbered() {
let src = "- foo";
let mut parser = SummaryParser::new(src);
parser.state = State::PrefixChapters;
// let _ = parser.stream.next(); // manually step past the Start Paragraph
parser.step().unwrap();
assert_eq!(parser.state, State::NumberedChapters(0));
}
#[test]
fn push_item_onto_empty_link() {
let mut root = Link::new("First", "/");
assert!(root.nested_items.is_empty());
let got = push_item_at_nesting_level(&mut root, SummaryItem::Separator, 0).unwrap();
assert_eq!(root.nested_items.len(), 1);
assert_eq!(*got, vec![1]);
}
#[test]
fn push_item_onto_complex_link() {
let mut root = Link::new("First", "/first");
root.nested_items.push(SummaryItem::Separator);
let mut child = Link::new("Second", "/first/second");
child.nested_items.push(SummaryItem::Link(
Link::new("Third", "/first/second/third"),
));
root.nested_items.push(SummaryItem::Link(child));
root.nested_items.push(SummaryItem::Separator);
assert_eq!(root.nested_items[1].maybe_link_mut()
.unwrap()
.nested_items[0].maybe_link_mut()
.unwrap()
.nested_items.len() , 0);
let got = push_item_at_nesting_level(&mut root, SummaryItem::Link(Link::new("Dummy", "")), 2).unwrap();
println!("{:#?}", root);
assert_eq!(root.nested_items[1].maybe_link_mut().unwrap().nested_items[0].maybe_link_mut().unwrap().nested_items.len(), 1);
assert_eq!(*got, vec![2, 1, 1]);
}
} }