lssg_lib/lmarkdown/
mod.rs

1use std::io::Read;
2
3use crate::{char_reader::CharReader, parse_error::ParseError};
4
5mod lexer;
6pub use lexer::*;
7
8pub fn parse_lmarkdown(input: impl Read) -> Result<Vec<Token>, ParseError> {
9    let mut reader = CharReader::new(input);
10
11    let mut tokens = vec![];
12
13    loop {
14        match lexer::read_token(&mut reader)? {
15            Token::EOF => break,
16            t => tokens.push(t),
17        }
18    }
19    // add paragraphs and texts together
20    let mut reduced_tokens = vec![];
21    for mut token in tokens.into_iter() {
22        if let Some(Token::Paragraph { tokens: a }) = reduced_tokens.last_mut() {
23            if let Token::Paragraph { tokens: b } = &mut token {
24                if let Some(Token::Text { text: text_a }) = a.first_mut() {
25                    if let Some(Token::Text { text: text_b }) = b.first_mut() {
26                        text_a.push('\n');
27                        *text_a += text_b;
28                        b.drain(0..1);
29                    }
30                }
31                a.append(b);
32                continue;
33            }
34        }
35        reduced_tokens.push(token)
36    }
37
38    Ok(reduced_tokens)
39}
40
41#[cfg(test)]
42mod tests {
43    use std::{collections::HashMap, io::Cursor};
44
45    use toml::Table;
46
47    use crate::html::to_attributes;
48
49    use super::*;
50
51    #[test]
52    fn test_text_that_looks_like_html() {
53        let input = r#"# Rust > c++
54Lots of people say Rust > c++. even though it might be
55< then c++. Who knows? 
56<nonclosing>
57This should be text
58"#;
59        let expected = vec![
60            Token::Heading {
61                depth: 1,
62                tokens: vec![Token::Text {
63                    text: "Rust > c++".into(),
64                }],
65            },
66            Token::Paragraph {
67                tokens: vec![Token::Text {
68                    text: "Lots of people say Rust > c++. even though it might be
69< then c++. Who knows?
70<nonclosing>
71This should be text"
72                        .into(),
73                }],
74            },
75        ];
76
77        let reader: Box<dyn Read> = Box::new(Cursor::new(input));
78        let tokens = parse_lmarkdown(reader).unwrap();
79        assert_eq!(tokens, expected);
80    }
81
82    #[test]
83    fn test_comments() {
84        let input = r#"<!--[default]
85title="asdf"
86-->
87<!-- another comment -->
88paragraph <!-- inline comment -->
89<!--
90another comment
91-->
92"#;
93        let mut attributes_table = Table::new();
94        let mut default_table = Table::new();
95        default_table.insert("title".into(), "asdf".into());
96        attributes_table.insert("default".into(), toml::Value::Table(default_table));
97        let expected = vec![
98            Token::Attributes {
99                table: attributes_table,
100            },
101            Token::Comment {
102                raw: " another comment ".into(),
103            },
104            Token::Paragraph {
105                tokens: vec![
106                    Token::Text {
107                        text: "paragraph ".into(),
108                    },
109                    Token::Comment {
110                        raw: " inline comment ".into(),
111                    },
112                ],
113            },
114            Token::Comment {
115                raw: "\nanother comment\n".into(),
116            },
117        ];
118
119        let reader: Box<dyn Read> = Box::new(Cursor::new(input));
120        let tokens = parse_lmarkdown(reader).unwrap();
121        assert_eq!(expected, tokens);
122    }
123
124    #[test]
125    fn test_links() {
126        let input = r#"# A [test](test.com)
127<div>
128[<b>bold</b>](bold.com)
129<a href="link.com">[other](other.com)</a>
130</div>"#;
131        let mut attributes_table = Table::new();
132        let mut default_table = Table::new();
133        default_table.insert("title".into(), "asdf".into());
134        attributes_table.insert("default".into(), toml::Value::Table(default_table));
135        let expected = vec![
136            Token::Heading {
137                depth: 1,
138                tokens: vec![
139                    Token::Text { text: "A ".into() },
140                    Token::Link {
141                        tokens: vec![Token::Text {
142                            text: "test".into(),
143                        }],
144                        href: "test.com".into(),
145                    },
146                ],
147            },
148            Token::Html {
149                tag: "div".into(),
150                attributes: HashMap::new(),
151                tokens: vec![
152                    Token::Link {
153                        tokens: vec![Token::Html {
154                            tag: "b".into(),
155                            attributes: HashMap::new(),
156                            tokens: vec![Token::Text {
157                                text: "bold".into(),
158                            }],
159                        }],
160                        href: "bold.com".into(),
161                    },
162                    Token::Text { text: "\n".into() },
163                    Token::Html {
164                        tag: "a".into(),
165                        attributes: to_attributes([("href", "link.com")]),
166                        tokens: vec![Token::Link {
167                            tokens: vec![Token::Text {
168                                text: "other".into(),
169                            }],
170                            href: "other.com".into(),
171                        }],
172                    },
173                ],
174            },
175        ];
176
177        let reader: Box<dyn Read> = Box::new(Cursor::new(input));
178        let tokens = parse_lmarkdown(reader).unwrap();
179        assert_eq!(expected, tokens);
180    }
181}