markdown/parser/block/
mod.rs

1use parser::Block;
2use parser::Block::Paragraph;
3use parser::Span::{Text, Break};
4use parser::span::parse_spans;
5
6mod atx_header;
7mod setext_header;
8mod hr;
9mod code_block;
10mod blockquote;
11mod unordered_list;
12mod ordered_list;
13use self::atx_header::parse_atx_header;
14use self::setext_header::parse_setext_header;
15use self::hr::parse_hr;
16use self::code_block::parse_code_block;
17use self::blockquote::parse_blockquote;
18use self::unordered_list::parse_unordered_list;
19use self::ordered_list::parse_ordered_list;
20
21pub fn parse_blocks (md : &str) -> Vec<Block> {
22    let mut blocks = vec![];
23    let mut t = vec![];
24    let lines : Vec<&str> = md.lines().collect();
25    let mut i = 0;
26    while i < lines.len() {
27        match parse_block(&lines[i .. lines.len()]){
28            // if a block is found
29            Some((block, consumed_lines)) => {
30                // the current paragraph has ended,
31                // push it to our blocks
32                if !t.is_empty(){
33                    blocks.push(Paragraph(t));
34                    t = Vec::new();
35                }
36                blocks.push(block);
37                i += consumed_lines;
38            }
39            // no known element, let's make this a paragraph
40            None => {
41
42                // empty linebreak => new paragraph
43                if lines[i].is_empty() && !t.is_empty(){
44                    blocks.push(Paragraph(t));
45                    t = Vec::new();
46                }
47
48                let spans = parse_spans(lines[i]);
49
50                // add a whitespace between linebreaks
51                // except when we have a break element or nothing
52                match (t.last(), spans.first()) {
53                    (Some(&Break), _) => {},
54                    (_, None) => {},
55                    (None, _) => {},
56                    _ => t.push(Text(" ".to_owned()))
57                }
58
59                t.extend_from_slice(&spans);
60                i += 1;
61            }
62        }
63    }
64    if !t.is_empty(){
65        blocks.push(Paragraph(t));
66    }
67    blocks
68}
69
70fn parse_block (lines: &[&str]) -> Option<(Block, usize)>{
71    pipe_opt!(
72        lines
73        => parse_hr
74        => parse_atx_header
75        => parse_setext_header
76        => parse_code_block
77        => parse_blockquote
78        => parse_unordered_list
79        => parse_ordered_list
80        )
81}
82
83#[cfg(test)]
84mod test {
85    use super::parse_blocks;
86    use parser::Block::{Header, Hr, CodeBlock, Paragraph, Blockquote};
87    use parser::Span::Text;
88
89    #[test]
90    fn finds_atx_header() {
91        assert_eq!(
92            parse_blocks("### Test"),
93            vec![Header(vec![Text("Test".to_owned())], 3)]
94            );
95    }
96
97    #[test]
98    fn finds_setext_header() {
99        assert_eq!(
100            parse_blocks("Test\n-------"),
101            vec![Header(vec![Text("Test".to_owned())], 2)]
102            );
103        assert_eq!(
104            parse_blocks("Test\n======="),
105            vec![Header(vec![Text("Test".to_owned())], 1)]
106            );
107    }
108
109    #[test]
110    fn finds_hr() {
111        assert_eq!(
112            parse_blocks("-------"),
113            vec![Hr]
114            );
115        assert_eq!(
116            parse_blocks("======="),
117            vec![Hr]
118            );
119    }
120
121    #[test]
122    fn finds_code_block() {
123        assert_eq!(
124            parse_blocks("    this is code\n    and this as well"),
125            vec![CodeBlock(None, "this is code\nand this as well".to_owned())]
126        );
127
128        assert_eq!(
129            parse_blocks("```\nthis is code\nand this as well\n```"),
130            vec![CodeBlock(Some(String::new()), "this is code\nand this as well".to_owned())]
131        );
132    }
133
134    #[test]
135    fn finds_blockquotes() {
136        assert_eq!(
137            parse_blocks("> One Paragraph\n>\n> ## H2 \n>\n"),
138            vec![Blockquote(vec![Paragraph(vec![Text("One Paragraph".to_owned())]), Header(vec![Text("H2".to_owned())], 2)])]
139            );
140
141        assert_eq!(
142            parse_blocks("> One Paragraph\n>\n> > Another blockquote\n>\n"),
143            vec![Blockquote(vec![Paragraph(vec![Text("One Paragraph".to_owned())]),
144            Blockquote(vec![Paragraph(vec![Text("Another blockquote".to_owned())])])])]
145            );
146
147        assert_eq!(
148            parse_blocks("> > One Paragraph\n> >\n> > Another blockquote\n>\n"),
149            vec![Blockquote(vec![Blockquote(vec![Paragraph(vec![Text("One Paragraph".to_owned())]),
150            Paragraph(vec![Text("Another blockquote".to_owned())])])])]
151            );
152
153        assert_eq!(
154            parse_blocks("> One Paragraph, just > text \n>\n"),
155            vec![Blockquote(vec![Paragraph(vec![Text("One Paragraph, just > text".to_owned())])])]
156            );
157
158        assert_eq!(
159            parse_blocks("> One Paragraph\n>\n> just > text \n>\n"),
160            vec![Blockquote(vec![Paragraph(vec![Text("One Paragraph".to_owned())]),Paragraph(vec![Text("just > text".to_owned())])])]
161            );
162    }
163}
164
165