Skip to main content

muffy_document/
xml.rs

1//! XML documents.
2
3use crate::document::Document;
4use markup5ever_rcdom::RcDom;
5use std::io;
6use xml5ever::{driver::parse_document, tendril::TendrilSink};
7
8/// Parses an XML document.
9pub fn parse(source: &str) -> Result<Document, io::Error> {
10    parse_bytes(source.as_bytes())
11}
12
13/// Parses an XML document from bytes.
14pub fn parse_bytes(mut source: &[u8]) -> Result<Document, io::Error> {
15    parse_document(RcDom::default(), Default::default())
16        .from_utf8()
17        .read_from(&mut source)
18        .map(|dom| Document::from_markup5ever(&dom.document))
19}
20
21#[cfg(test)]
22mod tests {
23    use super::*;
24    use crate::document::{Element, Node};
25    use alloc::sync::Arc;
26    use pretty_assertions::assert_eq;
27
28    #[test]
29    fn parse_empty_string() {
30        assert_eq!(parse("").unwrap(), Document::new(vec![]));
31    }
32
33    #[test]
34    fn parse_svg_document() {
35        assert_eq!(
36            parse(concat!(
37                r#"<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">"#,
38                r#"<a href="/foo"><rect/></a>"#,
39                r#"<image xlink:href="/bar.png"/>"#,
40                "</svg>"
41            ))
42            .unwrap(),
43            Document::new(vec![Arc::new(Node::Element(Element::new(
44                "svg".to_string(),
45                // XML parsers consume namespace declarations.
46                vec![],
47                vec![
48                    Arc::new(Node::Element(Element::new(
49                        "a".to_string(),
50                        vec![("href".to_string(), "/foo".to_string())],
51                        vec![Arc::new(Node::Element(Element::new(
52                            "rect".to_string(),
53                            vec![],
54                            vec![]
55                        )))],
56                    ))),
57                    Arc::new(Node::Element(Element::new(
58                        "image".to_string(),
59                        vec![("href".to_string(), "/bar.png".to_string())],
60                        vec![],
61                    ))),
62                ],
63            )))])
64        );
65    }
66
67    #[test]
68    fn preserve_element_name_case() {
69        assert_eq!(
70            parse("<svg><foreignObject/></svg>").unwrap(),
71            Document::new(vec![Arc::new(Node::Element(Element::new(
72                "svg".to_string(),
73                vec![],
74                vec![Arc::new(Node::Element(Element::new(
75                    "foreignObject".to_string(),
76                    vec![],
77                    vec![]
78                )))],
79            )))])
80        );
81    }
82
83    #[test]
84    fn keep_html_element_in_svg_element() {
85        assert_eq!(
86            parse("<svg><p>foo</p></svg>").unwrap(),
87            Document::new(vec![Arc::new(Node::Element(Element::new(
88                "svg".to_string(),
89                vec![],
90                vec![Arc::new(Node::Element(Element::new(
91                    "p".to_string(),
92                    vec![],
93                    vec![Arc::new(Node::Text("foo".to_string()))],
94                )))],
95            )))])
96        );
97    }
98
99    #[test]
100    fn ignore_processing_instructions() {
101        assert_eq!(
102            parse(r#"<?xml version="1.0"?><svg/>"#).unwrap(),
103            Document::new(vec![Arc::new(Node::Element(Element::new(
104                "svg".to_string(),
105                vec![],
106                vec![],
107            )))])
108        );
109    }
110}