1
use crate::dom;
2

            
3
pub struct Parser {
4
    cursor: usize,
5
    data: String,
6
}
7

            
8
impl Parser {
9
1141
    fn next_char(&self) -> char {
10
1141
        self.data[self.cursor..].chars().next().unwrap()
11
1141
    }
12

            
13
151
    fn starts_with(&self, s: &str) -> bool {
14
151
        self.data[self.cursor..].starts_with(s)
15
151
    }
16

            
17
1151
    fn eof(&self) -> bool {
18
1151
        self.cursor >= self.data.len()
19
1151
    }
20

            
21
1033
    fn consume_char(&mut self) -> char {
22
1033
        let mut iter = self.data[self.cursor..].char_indices();
23
1033
        let (_, current_char) = iter.next().unwrap();
24
1033
        let (next_cursor, _) = iter.next().unwrap_or((1, ' '));
25
1033
        self.cursor += next_cursor;
26
1033

            
27
1033
        current_char
28
1033
    }
29

            
30
191
    fn consume_while<F>(&mut self, test: F) -> String
31
191
    where
32
191
        F: Fn(char) -> bool,
33
191
    {
34
191
        let mut result = String::new();
35
1072
        while !self.eof() && test(self.next_char()) {
36
881
            result.push(self.consume_char());
37
881
        }
38
191
        result
39
191
    }
40

            
41
111
    fn consume_whitespace(&mut self) {
42
111
        self.consume_while(char::is_whitespace);
43
111
    }
44

            
45
60
    fn parse_tag_name(&mut self) -> String {
46
217
        self.consume_while(|c| matches!(c, 'a'..='z' | 'A'..='Z' | '0'..='9'))
47
60
    }
48

            
49
44
    fn parse_node(&mut self) -> dom::Node {
50
44
        match self.next_char() {
51
28
            '<' => self.parse_element(),
52
16
            _ => self.parse_text(),
53
        }
54
44
    }
55

            
56
16
    fn parse_text(&mut self) -> dom::Node {
57
98
        dom::text(&self.consume_while(|c| c != '<'))
58
16
    }
59

            
60
28
    fn parse_element(&mut self) -> dom::Node {
61
28
        assert!(self.consume_char() == '<');
62
28
        let tag_name = self.parse_tag_name();
63
28
        let attrs = self.parse_attributes();
64
28
        assert!(self.consume_char() == '>');
65

            
66
28
        let children = self.parse_nodes();
67
28

            
68
28
        assert!(self.consume_char() == '<');
69
28
        assert!(self.consume_char() == '/');
70
28
        assert!(self.parse_tag_name() == tag_name);
71
28
        assert!(self.consume_char() == '>');
72

            
73
28
        dom::elem(&tag_name).add_attrs(attrs).add_children(children)
74
28
    }
75

            
76
4
    fn parse_attr(&mut self) -> (String, String) {
77
4
        let name = self.parse_tag_name();
78
4
        assert!(self.consume_char() == '=');
79
4
        let value = self.parse_attr_value();
80
4
        (name, value)
81
4
    }
82

            
83
4
    fn parse_attr_value(&mut self) -> String {
84
4
        let open_quote = self.consume_char();
85
4
        assert!(open_quote == '"' || open_quote == '\'');
86
15
        let value = self.consume_while(|c| c != open_quote);
87
4
        assert!(self.consume_char() == open_quote);
88
4
        value
89
4
    }
90

            
91
28
    fn parse_attributes(&mut self) -> Vec<(String, String)> {
92
28
        let mut attributes = vec![];
93
        loop {
94
32
            self.consume_whitespace();
95
32
            if self.next_char() == '>' {
96
28
                break;
97
4
            }
98
4
            let (name, value) = self.parse_attr();
99
4
            attributes.push((name, value));
100
        }
101
28
        attributes
102
28
    }
103

            
104
35
    fn parse_nodes(&mut self) -> Vec<dom::Node> {
105
35
        let mut nodes = Vec::new();
106
        loop {
107
79
            self.consume_whitespace();
108
79

            
109
79
            if self.starts_with("<!") {
110
                self.consume_while(|c| c != '>');
111
                continue
112
79
            }
113
79

            
114
79
            if self.eof() || self.starts_with("</") {
115
35
                break;
116
44
            }
117
44
            nodes.push(self.parse_node());
118
        }
119
35
        nodes
120
35
    }
121

            
122
7
    pub fn parse_no_root(source: String) -> Vec<dom::Node> {
123
7
        Parser {
124
7
            cursor: 0,
125
7
            data: source,
126
7
        }
127
7
        .parse_nodes()
128
7
    }
129

            
130
4
    pub fn parse(source: String) -> dom::Node {
131
4
        let mut nodes = Parser::parse_no_root(source);
132
4

            
133
4
        if nodes.len() == 1 {
134
4
            nodes.pop().unwrap()
135
        } else {
136
            dom::elem("html").add_children(nodes)
137
        }
138
4
    }
139
}
140

            
141
impl From<String> for dom::Node {
142
    fn from(s: String) -> dom::Node {
143
        Parser::parse(s)
144
    }
145
}
146

            
147
impl From<&str> for dom::Node {
148
4
    fn from(s: &str) -> dom::Node {
149
4
        Parser::parse(s.to_owned())
150
4
    }
151
}
152

            
153
#[cfg(test)]
154
mod tests {
155
    use crate::dom::{elem, Node};
156

            
157
1
    #[test]
158
1
    fn test_from_string() {
159
1
        let expected = elem("html")
160
1
            .add_attr("lang", "NL")
161
1
            .add_child(elem("head").add_child(elem("title").add_text("Hello, world!")))
162
1
            .add_child(
163
1
                elem("body")
164
1
                    .add_child(elem("h1").add_text("Hi!"))
165
1
                    .add_child(elem("p").add_text("Bye!")),
166
1
            );
167
1
        let actual = "
168
1
            <html lang=\"NL\">
169
1
                <head>
170
1
                    <title>Hello, world!</title>
171
1
                </head>
172
1
                <body>
173
1
                    <h1>Hi!</h1>
174
1
                    <p>Bye!</p>
175
1
                </body>
176
1
            </html>
177
1
        ";
178
1
        assert_eq!(Node::from(actual), expected);
179
1
    }
180
}