1use crate::document::Document;
4use html5ever::{parse_document, tendril::TendrilSink};
5use markup5ever_rcdom::RcDom;
6use std::io;
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| 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 const XHTML_NAMESPACE: &str = "http://www.w3.org/1999/xhtml";
29 const SVG_NAMESPACE: &str = "http://www.w3.org/2000/svg";
30
31 fn element(
32 namespace: &str,
33 name: &str,
34 attributes: Vec<(&str, &str)>,
35 children: Vec<Arc<Node>>,
36 ) -> Arc<Node> {
37 Arc::new(Node::Element(
38 Element::new(
39 name.into(),
40 attributes
41 .into_iter()
42 .map(|(name, value)| (name.into(), value.into()))
43 .collect(),
44 children,
45 )
46 .set_namespace(Some(namespace.into())),
47 ))
48 }
49
50 fn text(value: &str) -> Arc<Node> {
51 Arc::new(Node::Text(value.into()))
52 }
53
54 #[test]
55 fn parse_empty_string() {
56 assert_eq!(
57 parse("").unwrap(),
58 Document::new(vec![element(
59 XHTML_NAMESPACE,
60 "html",
61 vec![],
62 vec![
63 element(XHTML_NAMESPACE, "head", vec![], vec![]),
64 element(XHTML_NAMESPACE, "body", vec![], vec![]),
65 ],
66 )])
67 );
68 }
69
70 #[test]
71 fn parse_simple_html() {
72 assert_eq!(
73 parse("<html><body><p>Hello</p></body></html>").unwrap(),
74 Document::new(vec![element(
75 XHTML_NAMESPACE,
76 "html",
77 vec![],
78 vec![
79 element(XHTML_NAMESPACE, "head", vec![], vec![]),
80 element(
81 XHTML_NAMESPACE,
82 "body",
83 vec![],
84 vec![element(XHTML_NAMESPACE, "p", vec![], vec![text("Hello")])],
85 ),
86 ],
87 )])
88 );
89 }
90
91 #[test]
92 fn parse_with_attributes() {
93 assert_eq!(
94 parse("<html><body><p class=\"foo\">Hello</p></body></html>").unwrap(),
95 Document::new(vec![element(
96 XHTML_NAMESPACE,
97 "html",
98 vec![],
99 vec![
100 element(XHTML_NAMESPACE, "head", vec![], vec![]),
101 element(
102 XHTML_NAMESPACE,
103 "body",
104 vec![],
105 vec![element(
106 XHTML_NAMESPACE,
107 "p",
108 vec![("class", "foo")],
109 vec![text("Hello")],
110 )],
111 ),
112 ],
113 )])
114 );
115 }
116
117 #[test]
118 fn parse_svg_document() {
119 assert_eq!(
120 parse(concat!(
121 r#"<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">"#,
122 r#"<a href="/foo"><rect/></a>"#,
123 r#"<image xlink:href="/bar.png"/>"#,
124 "</svg>"
125 ))
126 .unwrap(),
127 Document::new(vec![element(
128 XHTML_NAMESPACE,
129 "html",
130 vec![],
131 vec![
132 element(XHTML_NAMESPACE, "head", vec![], vec![]),
133 element(
134 XHTML_NAMESPACE,
135 "body",
136 vec![],
137 vec![element(
138 SVG_NAMESPACE,
139 "svg",
140 vec![],
141 vec![
142 element(
143 SVG_NAMESPACE,
144 "a",
145 vec![("href", "/foo")],
146 vec![element(SVG_NAMESPACE, "rect", vec![], vec![])],
147 ),
148 element(
149 SVG_NAMESPACE,
150 "image",
151 vec![("xlink:href", "/bar.png")],
152 vec![],
153 ),
154 ],
155 )],
156 ),
157 ],
158 )])
159 );
160 }
161
162 #[test]
163 fn ignore_comments() {
164 assert_eq!(
165 parse("<html><body><!-- comment --><p>Hello</p></body></html>").unwrap(),
166 Document::new(vec![element(
167 XHTML_NAMESPACE,
168 "html",
169 vec![],
170 vec![
171 element(XHTML_NAMESPACE, "head", vec![], vec![]),
172 element(
173 XHTML_NAMESPACE,
174 "body",
175 vec![],
176 vec![element(XHTML_NAMESPACE, "p", vec![], vec![text("Hello")])],
177 ),
178 ],
179 )])
180 );
181 }
182}