-
Notifications
You must be signed in to change notification settings - Fork 4
/
extractors.rs
150 lines (129 loc) · 4.43 KB
/
extractors.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
use ::std::iter::Peekable;
use ::pulldown_cmark::{Event, Tag};
use ::types::*;
use ::errors::ParseError;
use ::to_md::md;
macro_rules! not {
(start $tag:pat) => (not!(Event::Start($tag)));
(end $tag:pat) => (not!(Event::End($tag)));
($tag:pat) => (
|event| {
if let $tag = *event {
false
} else {
true
}
}
);
}
pub fn teaser<'a, I>(events: &mut I) -> Result<String, ParseError> where
I: Iterator<Item=Event<'a>>,
{
if let Some(Event::Start(Tag::Paragraph)) = events.next() {
Ok(md(events.take_while(not!(end Tag::Paragraph))))
} else {
Err(ParseError::NoTeaser)
}
}
pub fn description<'a, I>(events: &mut I) -> Result<Option<String>, ParseError> where
I: Iterator<Item=Event<'a>>,
{
let description = md(events.take_while(not!(start Tag::Header(1))));
if description.is_empty() {
return Ok(None);
}
Ok(Some(description))
}
pub fn sections<'a, I>(events: &mut Peekable<I>) -> Result<Vec<DocSection>, ParseError> where
I: Iterator<Item=Event<'a>>,
{
let mut sections: Vec<DocSection> = vec![];
loop {
// Assume the previous item was the start of an `Header(1)`, so a
// section begins witht he text content of that header.
//
// TODO: Handle that more nicely (see note on `take_while` above)
// TODO: Deal with headlines not starting with text (i.e., those
// starting with inline markdown).
let go_on = {
// Peek next item without consuming
//
// This is inside a block to ahve the handle on events is dropped
// sooner (so we can use it in `events.next()` below).
if let Some(&Event::Text(_)) = events.peek() {
true
} else {
false
}
};
if go_on {
sections.push(try!(section(events)));
continue;
}
match events.next() {
None => break,
// Next section
Some(Event::Start(Tag::Header(1))) =>
sections.push(try!(section(events))),
Some(unexpected) =>
return Err(ParseError::UnexpectedMarkdown(
"Sections".into(), format!("{:?}", unexpected))),
}
}
Ok(sections)
}
fn section<'a, I>(events: &mut I) -> Result<DocSection, ParseError> where
I: Iterator<Item=Event<'a>>,
{
// the next item is the value after a `Event::Start(Tag::Header(1))`
let headline = md(events.take_while(not!(end Tag::Header(1))));
// What kind of headline are we dealing with?
let section = match &headline.trim().to_lowercase()[..] {
"parameters" =>
DocSection::Parameters(try!(list(events, &headline))),
"type parameters" =>
DocSection::TypeParameters(try!(list(events, &headline))),
"lifetime parameters" | "lifetimes" =>
DocSection::LifetimeParameters(try!(list(events, &headline))),
"returns" =>
DocSection::Returns(
md(events.take_while(not!(start Tag::List(_)))),
try!(list(events, &headline))),
_ =>
DocSection::Custom(
headline,
md(events.take_while(not!(start Tag::Header(1))))),
};
Ok(section)
}
fn list<'a, I>(events: &mut I, section: &str) -> Result<Vec<(Identifier, Documentation)>, ParseError> where
I: Iterator<Item=Event<'a>>,
{
let mut list = vec![];
loop {
match events.next() {
Some(Event::Start(Tag::List(_))) => continue,
None | Some(Event::End(Tag::List(_))) => break,
Some(Event::Start(Tag::Item)) => list.push(try!(list_item(events))),
Some(unexpected) => return Err(ParseError::UnexpectedMarkdown(
section.into(), format!("{:?}", unexpected))),
}
}
Ok(list)
}
fn list_item<'a, I>(events: &mut I) -> Result<(Identifier, Documentation), ParseError> where
I: Iterator<Item=Event<'a>>,
{
let ident = if let Some(Event::Start(Tag::Code)) = events.next() {
md(events.take_while(not!(end Tag::Code)))
} else {
return Err(ParseError::NoIdent);
};
let mut docs = md(events.take_while(not!(end Tag::Item)));
if docs.starts_with(": ") {
docs = docs.trim_left_matches(": ").into();
} else {
return Err(ParseError::WrongIdentDocsSeparator);
}
Ok((ident, docs))
}