1use crate::document::Document;
4use markup5ever_rcdom::RcDom;
5use std::io;
6use xml5ever::{driver::parse_document, tendril::TendrilSink};
7
8pub fn parse(source: &str) -> Result<Document, io::Error> {
10 parse_bytes(source.as_bytes())
11}
12
13pub 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| {
19 Document::from_markup5ever(&dom.document).set_errors(
20 dom.errors
21 .borrow()
22 .iter()
23 .map(ToString::to_string)
24 .collect(),
25 )
26 })
27}
28
29#[cfg(test)]
30mod tests {
31 use super::*;
32 use crate::document::{Element, Node};
33 use alloc::sync::Arc;
34 use pretty_assertions::assert_eq;
35
36 const SVG_NAMESPACE: &str = "http://www.w3.org/2000/svg";
37
38 fn element(
39 namespace: Option<&str>,
40 name: &str,
41 attributes: Vec<(&str, &str)>,
42 children: Vec<Arc<Node>>,
43 ) -> Arc<Node> {
44 Arc::new(Node::Element(
45 Element::new(
46 name.into(),
47 attributes
48 .into_iter()
49 .map(|(name, value)| (name.into(), value.into()))
50 .collect(),
51 children,
52 )
53 .set_namespace(namespace.map(Into::into)),
54 ))
55 }
56
57 #[test]
58 fn parse_empty_string() {
59 assert_eq!(
60 parse("").unwrap(),
61 Document::new(vec![]).set_errors(vec!["Unexpected EOF in start phase".into()])
62 );
63 }
64
65 #[test]
66 fn parse_svg_document() {
67 assert_eq!(
68 parse(concat!(
69 r#"<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">"#,
70 r#"<a href="/foo"><rect/></a>"#,
71 r#"<image xlink:href="/bar.png"/>"#,
72 "</svg>"
73 ))
74 .unwrap(),
75 Document::new(vec![element(
76 Some(SVG_NAMESPACE),
77 "svg",
78 vec![],
79 vec![
80 element(
81 Some(SVG_NAMESPACE),
82 "a",
83 vec![("href", "/foo")],
84 vec![element(Some(SVG_NAMESPACE), "rect", vec![], vec![])],
85 ),
86 element(
87 Some(SVG_NAMESPACE),
88 "image",
89 vec![("xlink:href", "/bar.png")],
90 vec![],
91 ),
92 ],
93 )])
94 );
95 }
96
97 #[test]
98 fn collect_error_on_multiple_root_elements() {
99 let document = parse("<svg/><svg/>").unwrap();
100
101 assert_eq!(document.children().count(), 1);
102 assert_eq!(
103 document.errors().collect::<Vec<_>>(),
104 vec!["Unexpected element in end phase"]
105 );
106 }
107
108 #[test]
109 fn parse_element_without_namespace() {
110 assert_eq!(
111 parse("<svg><foreignObject/></svg>").unwrap(),
112 Document::new(vec![element(
113 None,
114 "svg",
115 vec![],
116 vec![element(None, "foreignObject", vec![], vec![])],
117 )])
118 );
119 }
120
121 #[test]
122 fn parse_prefixed_element() {
123 assert_eq!(
124 parse(concat!(
125 r#"<svg xmlns="http://www.w3.org/2000/svg">"#,
126 r#"<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"/>"#,
127 "</svg>"
128 ))
129 .unwrap(),
130 Document::new(vec![element(
131 Some(SVG_NAMESPACE),
132 "svg",
133 vec![],
134 vec![element(
135 Some("http://www.w3.org/1999/02/22-rdf-syntax-ns#"),
136 "rdf:RDF",
137 vec![],
138 vec![],
139 )],
140 )])
141 );
142 }
143
144 #[test]
145 fn keep_html_element_in_svg_element() {
146 assert_eq!(
147 parse(r#"<svg xmlns="http://www.w3.org/2000/svg"><p>foo</p></svg>"#).unwrap(),
148 Document::new(vec![element(
149 Some(SVG_NAMESPACE),
150 "svg",
151 vec![],
152 vec![element(
153 Some(SVG_NAMESPACE),
154 "p",
155 vec![],
156 vec![Arc::new(Node::Text("foo".into()))],
157 )],
158 )])
159 );
160 }
161
162 #[test]
163 fn ignore_processing_instructions() {
164 assert_eq!(
165 parse(r#"<?xml version="1.0"?><svg/>"#).unwrap(),
166 Document::new(vec![element(None, "svg", vec![], vec![])])
167 );
168 }
169}