markdown/parser/block/
mod.rs1use 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 Some((block, consumed_lines)) => {
30 if !t.is_empty(){
33 blocks.push(Paragraph(t));
34 t = Vec::new();
35 }
36 blocks.push(block);
37 i += consumed_lines;
38 }
39 None => {
41
42 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 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