svg_simple_parser/
parse.rs

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/// remove whitespace ` \t\r\n`
18#[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/// parse a text wrapped in `"` or `'`
28/// ## Example
29/// ``` ignore
30///  "100" -> "100"
31///  '100' -> "100"
32/// ```
33#[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/// parse a text with key-value format`
43/// ## Example
44/// ``` ignore
45/// width = "100" -> ("width","100")
46/// width = '100' -> ("width","100")
47/// ```
48#[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/// parse a text what is base on a lot oof key-value's format text`
57/// ## Example
58/// ``` ignore
59/// width = "100" height = "200"
60///
61/// // ↓↓↓↓↓↓↓↓ transform ↓↓↓↓↓↓↓↓
62///
63/// use std::collections::HashMap;
64///
65/// HashMap::from([
66///     ("width".to_owned(),"100"),
67///     ("height".to_owned(),"200"),
68/// ]);
69/// ```
70#[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/// parse the preix of the element
92///
93/// ## Example
94/// ``` ignore
95/// <svg  -> "svg"
96/// ```
97#[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/// parse a single element
108///
109/// ## Example
110/// ``` ignore
111/// <rect width="100"/>
112///
113/// // ↓↓↓↓↓↓↓↓ transform ↓↓↓↓↓↓↓↓
114///
115/// use std::collections::HashMap;
116/// use std::cell::RefCell;
117///
118/// Element{
119///   ele_type:"rect",
120///   attributes:RefCell::new(HashMap::from([
121///     ("width","100"),
122///   ])),
123///   children:vec![],
124/// }
125/// ```
126#[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/// parse a double element
141///
142/// ## Example
143/// ``` ignore
144/// <rect width="100">
145///     <rect width="100"/>
146/// </rect>
147///
148/// // ↓↓↓↓↓↓↓↓ transform ↓↓↓↓↓↓↓↓
149///
150/// use std::collections::HashMap;
151/// use std::cell::RefCell;
152///
153/// Element{
154///   ele_type:"rect",
155///   attributes:RefCell::new(HashMap::from([
156///     ("width".to_owned(),"100"),
157///   ])),
158///   children:vec![
159///     Element{
160///       ele_type:"rect",
161///       attributes:RefCell::new(HashMap::from([
162///         ("width".to_owned(),"100"),
163///       ])),
164///       children:vec![],
165///     },
166///   ],
167/// }
168/// ```
169#[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
185/// parse a double element or a single element
186fn 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
196/// parse a list of the element
197fn 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}
203/// transform svg to a Element(AST struct)
204///
205/// return a result.
206/// if transformed successfullly,return a tulp which includes the rest input text and a element;
207/// if transformed Error.return `ParseError`
208///
209///
210/// ## Example
211/// ```rust
212/// use svg_simple_parser::parse;
213/// use std::collections::HashMap;
214///
215/// let svg = r#"
216///     <svg xmlns="http://www.w3.org/2000/svg" version="1.1">
217///         <circle cx="100" cy="50" r="40" />
218///     </svg>
219/// "#;
220/// let (_, root) = parse(svg).unwrap();
221/// assert_eq!(root.ele_type, "svg");
222/// assert_eq!(*root.attributes.borrow(), HashMap::from([
223///     ("xmlns".to_owned(), "http://www.w3.org/2000/svg"),
224///     ("version".to_owned(), "1.1"),
225/// ]));
226/// let child = &*root.children.borrow()[0];
227/// assert_eq!(child.ele_type, "circle");
228/// assert_eq!(*child.attributes.borrow(), HashMap::from([
229///     ("cx".to_owned(), "100"),
230///     ("cy".to_owned(), "50"),
231///     ("r".to_owned(), "40"),
232/// ]));
233/// ```
234///
235pub 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}