Encoded the first state in the SummaryParser
This commit is contained in:
parent
dacb3e082e
commit
dcc8368543
|
@ -25,7 +25,8 @@ use pulldown_cmark::{self, Event, Tag};
|
|||
/// [Title of prefix element](relative/path/to/markdown.md)
|
||||
/// ```
|
||||
///
|
||||
/// **Numbered Chapter:** Numbered chapters are the main content of the book, they
|
||||
/// **Numbered Chapter:** Numbered chapters are the main content of the book,
|
||||
/// they
|
||||
/// will be numbered and can be nested, resulting in a nice hierarchy (chapters,
|
||||
/// sub-chapters, etc.)
|
||||
///
|
||||
|
@ -69,6 +70,16 @@ enum SummaryItem {
|
|||
Separator,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
enum State {
|
||||
Begin,
|
||||
PrefixChapters,
|
||||
/// Numbered chapters, including the nesting level.
|
||||
NumberedChapters(u32),
|
||||
SuffixChapters,
|
||||
End,
|
||||
}
|
||||
|
||||
/// A stateful parser for parsing a `SUMMARY.md` file.
|
||||
///
|
||||
/// # Grammar
|
||||
|
@ -76,7 +87,8 @@ enum SummaryItem {
|
|||
/// The `SUMMARY.md` file has a grammar which looks something like this:
|
||||
///
|
||||
/// ```text
|
||||
/// summary ::= title prefix_chapters numbered_chapters suffix_chapters
|
||||
/// summary ::= title prefix_chapters numbered_chapters
|
||||
/// suffix_chapters
|
||||
/// title ::= "# " TEXT
|
||||
/// | EPSILON
|
||||
/// prefix_chapters ::= item*
|
||||
|
@ -96,6 +108,7 @@ enum SummaryItem {
|
|||
struct SummaryParser<'a> {
|
||||
stream: pulldown_cmark::Parser<'a>,
|
||||
summary: Summary,
|
||||
state: State,
|
||||
}
|
||||
|
||||
/// Reads `Events` from the provided stream until the corresponding
|
||||
|
@ -127,8 +140,7 @@ macro_rules! collect_events {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> SummaryParser<'a>
|
||||
{
|
||||
impl<'a> SummaryParser<'a> {
|
||||
fn new(text: &str) -> SummaryParser {
|
||||
let pulldown_parser = pulldown_cmark::Parser::new(text);
|
||||
let intermediate_summary = Summary::default();
|
||||
|
@ -136,6 +148,7 @@ impl<'a> SummaryParser<'a>
|
|||
SummaryParser {
|
||||
stream: pulldown_parser,
|
||||
summary: intermediate_summary,
|
||||
state: State::Begin,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -146,6 +159,27 @@ impl<'a> SummaryParser<'a>
|
|||
Ok(self.summary)
|
||||
}
|
||||
|
||||
fn step(&mut self) -> Result<(), Box<Error>> {
|
||||
let next_event = self.stream.next().expect("TODO: error-chain");
|
||||
trace!("[*] Current state = {:?}, Next Event = {:?}", self.state, next_event);
|
||||
|
||||
match self.state {
|
||||
State::Begin => self.step_start(next_event),
|
||||
other => unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
/// The very first state, we should see a `BeginParagraph` token or
|
||||
/// it's an error...
|
||||
fn step_start(&mut self, event: Event<'a>) -> Result<(), Box<Error>> {
|
||||
match event {
|
||||
Event::Start(Tag::Paragraph) => self.state = State::PrefixChapters,
|
||||
other => panic!("Unexpected tag! {:?}", other),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_title(&mut self) -> Option<String> {
|
||||
if let Some(Event::Start(Tag::Header(1))) = self.stream.next() {
|
||||
debug!("[*] Found a h1 in the SUMMARY");
|
||||
|
@ -179,15 +213,15 @@ impl<'a> SummaryParser<'a>
|
|||
}
|
||||
}
|
||||
|
||||
/// Extract just the text from a bunch of events and concatenate it into a
|
||||
/// single string.
|
||||
/// Extracts the text from formatted markdown.
|
||||
fn stringify_events<'a>(events: Vec<Event<'a>>) -> String {
|
||||
events.into_iter()
|
||||
.filter_map(|t| match t {
|
||||
Event::Text(text) => Some(text),
|
||||
_ => None,
|
||||
})
|
||||
.collect()
|
||||
events
|
||||
.into_iter()
|
||||
.filter_map(|t| match t {
|
||||
Event::Text(text) => Some(text.into_owned()),
|
||||
_ => None,
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// A section number like "1.2.3", basically just a newtype'd `Vec<u32>`.
|
||||
|
@ -196,9 +230,11 @@ struct SectionNumber(Vec<u32>);
|
|||
|
||||
impl Display for SectionNumber {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
let dotted_number: String = self.0.iter().map(|i| format!("{}", i))
|
||||
.collect::<Vec<String>>()
|
||||
.join(".");
|
||||
let dotted_number: String = self.0
|
||||
.iter()
|
||||
.map(|i| format!("{}", i))
|
||||
.collect::<Vec<String>>()
|
||||
.join(".");
|
||||
|
||||
write!(f, "{}", dotted_number)
|
||||
}
|
||||
|
@ -274,4 +310,27 @@ mod tests {
|
|||
|
||||
assert_eq!(got, should_be);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn convert_markdown_events_to_a_string() {
|
||||
let src = "Hello *World*, `this` is some text [and a link](./path/to/link)";
|
||||
let should_be = "Hello World, this is some text and a link";
|
||||
|
||||
let events = pulldown_cmark::Parser::new(src).collect();
|
||||
let got = stringify_events(events);
|
||||
|
||||
assert_eq!(got, should_be);
|
||||
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_step_past_first_token() {
|
||||
let src = "hello world";
|
||||
let should_be = State::PrefixChapters;
|
||||
|
||||
let mut parser = SummaryParser::new(src);
|
||||
assert_eq!(parser.state, State::Begin);
|
||||
parser.step().unwrap();
|
||||
assert_eq!(parser.state, should_be);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue