1use std::collections::HashMap;
2use std::rc::Rc;
3
4use nom::{
5 branch::alt,
6 bytes::complete::{is_not, tag, take_till, take_until, take_while},
7 character::complete::{alphanumeric1, one_of, space1},
8 combinator::{cut, map, opt},
9 error::{context, ContextError, ParseError},
10 multi::{many0, separated_list0},
11 sequence::{delimited, pair, preceded, separated_pair, terminated, tuple},
12 IResult,
13};
14
15use crate::ast::Element;
16
17#[inline(always)]
19fn sp<'a, E>(i: &'a str) -> IResult<&'a str, &'a str, E>
20where
21 E: ParseError<&'a str>,
22{
23 let chars = " \t\r\n";
24 take_while(move |c| chars.contains(c))(i)
25}
26
27#[inline(always)]
34fn attribute_value<'a, E>(input: &'a str) -> IResult<&'a str, &'a str, E>
35where
36 E: ParseError<&'a str>,
37{
38 let mark = "\"\'";
39 delimited(one_of(mark), take_till(|c| mark.contains(c)), one_of(mark))(input)
40}
41
42#[inline(always)]
49fn attribute<'a, E>(input: &'a str) -> IResult<&'a str, (&'a str, &'a str), E>
50where
51 E: ParseError<&'a str>,
52{
53 separated_pair(is_not(" ="), tag("="), attribute_value)(input)
54}
55
56#[inline(always)]
71pub fn attribute_hash<'a, E>(input: &'a str) -> IResult<&'a str, HashMap<String, &'a str>, E>
72where
73 E: ParseError<&'a str> + ContextError<&'a str>,
74{
75 context(
76 "attribute_hash",
77 preceded(
78 sp,
79 cut(terminated(
80 map(separated_list0(space1, attribute), |tuple_vec| {
81 tuple_vec
82 .into_iter()
83 .map(|(k, v)| (String::from(k), v))
84 .collect()
85 }),
86 sp,
87 )),
88 ),
89 )(input)
90}
91#[inline(always)]
98fn element_start<'a, E>(input: &'a str) -> IResult<&'a str, &'a str, E>
99where
100 E: ParseError<&'a str> + ContextError<&'a str>,
101{
102 context(
103 "element_start",
104 preceded(tag("<"), preceded(sp, alphanumeric1)),
105 )(input)
106}
107#[inline(always)]
127pub fn single_element<'a, E>(input: &'a str) -> IResult<&'a str, Rc<Element>, E>
128where
129 E: ParseError<&'a str> + ContextError<&'a str>,
130{
131 context(
132 "single_element",
133 map(
134 pair(element_start, terminated(attribute_hash, tag("/>"))),
135 Element::new,
136 ),
137 )(input)
138}
139
140#[inline(always)]
170pub fn double_element<'a, E>(input: &'a str) -> IResult<&'a str, Rc<Element>, E>
171where
172 E: ParseError<&'a str> + ContextError<&'a str>,
173{
174 let attributes_pattern = terminated(attribute_hash, tag(">"));
175 let children_pattern = terminated(element_list, terminated(take_until(">"), tag(">")));
176 context(
177 "double_element",
178 map(
179 tuple((element_start, attributes_pattern, children_pattern)),
180 Element::new_width_children,
181 ),
182 )(input)
183}
184
185fn element<'a, E>(input: &'a str) -> IResult<&'a str, Rc<Element>, E>
187where
188 E: ParseError<&'a str> + ContextError<&'a str>,
189{
190 context(
191 "element",
192 delimited(sp, alt((double_element, single_element)), opt(sp)),
193 )(input)
194}
195
196fn element_list<'a, E>(input: &'a str) -> IResult<&'a str, Vec<Rc<Element>>, E>
198where
199 E: ParseError<&'a str> + ContextError<&'a str>,
200{
201 context("element_list", many0(element))(input)
202}
203pub fn parse<'a>(input: &'a str) -> IResult<&'a str, Rc<Element>> {
236 element(input)
237}
238
239#[cfg(test)]
240mod tests {
241 use nom::error::ErrorKind;
242 use std::collections::HashMap;
243
244 use crate::parse::{
245 attribute, attribute_hash, attribute_value, double_element, element_list, single_element,
246 };
247
248 #[test]
249 fn test_elements() {
250 let (_, v) = element_list::<(&str, ErrorKind)>(
251 r#"<svg xmlns="http://www.w3.org/2000/svg" version="1.1"/>"#,
252 )
253 .unwrap();
254 let one = &v[0];
255 assert_eq!(one.ele_type, "svg");
256 assert_eq!(
257 *one.attributes.borrow(),
258 HashMap::from([
259 ("xmlns".to_owned(), "http://www.w3.org/2000/svg"),
260 ("version".to_owned(), "1.1"),
261 ])
262 );
263 }
264
265 #[test]
266 fn test_double_element() {
267 let (_, root) = double_element::<(&str, ErrorKind)>(
268 r#"<svg xmlns="http://www.w3.org/2000/svg" version="1.1"></svg>"#,
269 )
270 .unwrap();
271 assert_eq!(root.ele_type, "svg");
272 assert_eq!(
273 *root.attributes.borrow(),
274 HashMap::from([
275 ("xmlns".to_owned(), "http://www.w3.org/2000/svg"),
276 ("version".to_owned(), "1.1"),
277 ])
278 );
279 }
280
281 #[test]
282 fn test_single_element() {
283 let (_, root) = single_element::<(&str, ErrorKind)>(
284 r#"<svg xmlns="http://www.w3.org/2000/svg" version="1.1" />"#,
285 )
286 .unwrap();
287 assert_eq!(root.ele_type, "svg");
288 assert_eq!(
289 *root.attributes.borrow(),
290 HashMap::from([
291 ("xmlns".to_owned(), "http://www.w3.org/2000/svg"),
292 ("version".to_owned(), "1.1"),
293 ])
294 );
295 }
296
297 #[test]
298 fn test_attribute_hash() {
299 assert_eq!(
300 attribute_hash::<(&str, ErrorKind)>("a=\"123\" b=\"456\" "),
301 Ok((
302 "",
303 HashMap::from([("a".to_owned(), "123"), ("b".to_owned(), "456")])
304 ))
305 );
306 assert_eq!(
307 attribute_hash::<(&str, ErrorKind)>("b=\'123\' c=\'456\' "),
308 Ok((
309 "",
310 HashMap::from([("b".to_owned(), "123"), ("c".to_owned(), "456")])
311 ))
312 );
313 }
314
315 #[test]
316 fn test_attribute() {
317 assert_eq!(
318 attribute::<(&str, ErrorKind)>("a=\"123\""),
319 Ok(("", ("a", "123")))
320 );
321 assert_eq!(
322 attribute::<(&str, ErrorKind)>("b=\'123\'"),
323 Ok(("", ("b", "123")))
324 );
325 }
326
327 #[test]
328 fn test_attribute_value() {
329 assert_eq!(
330 attribute_value::<(&str, ErrorKind)>("\'123\'"),
331 Ok(("", "123"))
332 );
333 assert_eq!(attribute_value::<(&str, ErrorKind)>("\'\'"), Ok(("", "")));
334 assert_eq!(
335 attribute_value::<(&str, ErrorKind)>("\"123\""),
336 Ok(("", "123"))
337 );
338 assert_eq!(attribute_value::<(&str, ErrorKind)>("\"\""), Ok(("", "")));
339 }
340}