1use quick_xml::events::{BytesStart, Event};
7use quick_xml::name::{QName, ResolveResult};
8use quick_xml::{NsReader, XmlVersion};
9
10use super::{Attribute, Element, Name, Node, Ns};
11use crate::Error;
12
13pub fn parse(bytes: &[u8], part: &str) -> Result<Element, Error> {
22 let text = std::str::from_utf8(bytes).map_err(|e| Error::Xml {
27 part: part.to_owned(),
28 detail: format!("not UTF-8: {e}"),
29 })?;
30 let text = text.strip_prefix('\u{feff}').unwrap_or(text);
32
33 let mut reader = NsReader::from_str(text);
34 let config = reader.config_mut();
35 config.expand_empty_elements = false;
39 config.trim_text_start = false;
42 config.trim_text_end = false;
43 config.check_end_names = true;
44
45 let mut stack: Vec<Element> = Vec::new();
46 let mut root: Option<Element> = None;
47
48 loop {
49 let event = reader.read_event().map_err(|e| Error::Xml {
50 part: part.to_owned(),
51 detail: e.to_string(),
52 })?;
53 match event {
54 Event::Start(ref start) => {
55 let element = element_of(start, &reader, false);
56 stack.push(element);
57 }
58 Event::Empty(ref start) => {
59 let element = element_of(start, &reader, true);
60 push_node(&mut stack, &mut root, Node::Element(element), part)?;
61 }
62 Event::End(_) => {
63 let done = stack.pop().ok_or_else(|| Error::Xml {
64 part: part.to_owned(),
65 detail: "an end tag with nothing open".to_owned(),
66 })?;
67 push_node(&mut stack, &mut root, Node::Element(done), part)?;
68 }
69 Event::Text(ref t) => {
70 push_text(&mut stack, &t.xml10_content());
71 }
72 Event::GeneralRef(ref r) => {
73 let text = reference(r, part)?;
74 push_text(&mut stack, &text);
75 }
76 Event::CData(ref c) => {
77 let node = Node::CData(c.xml10_content().into_owned());
78 push_node(&mut stack, &mut root, node, part)?;
79 }
80 Event::Comment(ref c) => {
81 let node = Node::Comment(c.xml10_content().into_owned());
82 push_node(&mut stack, &mut root, node, part)?;
83 }
84 Event::PI(p) => {
85 let node = Node::ProcessingInstruction(p.into_inner().into_owned());
88 push_node(&mut stack, &mut root, node, part)?;
89 }
90 Event::Decl(_) | Event::DocType(_) => {}
96 Event::Eof => break,
97 }
98 }
99
100 root.ok_or(Error::Xml {
101 part: part.to_owned(),
102 detail: "no root element".to_owned(),
103 })
104}
105
106fn reference(r: &quick_xml::events::BytesRef<'_>, part: &str) -> Result<String, Error> {
118 let name = r.xml10_content();
119 let resolved = if r.is_char_ref() {
120 r.resolve_char_ref().map_err(|e| Error::Xml {
121 part: part.to_owned(),
122 detail: e.to_string(),
123 })?
124 } else {
125 match &*name {
126 "amp" => Some('&'),
127 "lt" => Some('<'),
128 "gt" => Some('>'),
129 "quot" => Some('"'),
130 "apos" => Some('\''),
131 _ => None,
132 }
133 };
134 Ok(match resolved {
135 Some(c) => c.to_string(),
136 None => format!("&{name};"),
137 })
138}
139
140fn push_text(stack: &mut [Element], text: &str) {
145 if let Some(open) = stack.last_mut() {
146 if let Some(Node::Text(existing)) = open.children.last_mut() {
147 existing.push_str(text);
148 } else {
149 open.children.push(Node::Text(text.to_owned()));
150 }
151 }
152}
153
154fn push_node(
155 stack: &mut [Element],
156 root: &mut Option<Element>,
157 node: Node,
158 part: &str,
159) -> Result<(), Error> {
160 if let Some(open) = stack.last_mut() {
161 open.children.push(node);
162 return Ok(());
163 }
164 match node {
165 Node::Element(e) if root.is_none() => {
166 *root = Some(e);
167 Ok(())
168 }
169 Node::Element(_) => Err(Error::Xml {
170 part: part.to_owned(),
171 detail: "a second root element".to_owned(),
172 }),
173 _ => Ok(()),
176 }
177}
178
179fn element_of(start: &BytesStart<'_>, reader: &NsReader<&[u8]>, self_closing: bool) -> Element {
180 let name = resolved_name(reader, start.name(), false);
181 let mut attrs = Vec::new();
182 for attr in start.attributes() {
183 let Ok(attr) = attr else { continue };
184 let value = attr
189 .normalized_value(XmlVersion::Implicit1_0)
190 .unwrap_or_default()
191 .into_owned();
192 attrs.push(Attribute {
193 name: resolved_name(reader, attr.key, true),
194 value,
195 });
196 }
197 Element {
198 name,
199 attrs,
200 children: Vec::new(),
201 self_closing,
202 }
203}
204
205fn resolved_name(reader: &NsReader<&[u8]>, qname: QName<'_>, is_attribute: bool) -> Name {
206 let prefix = qname.prefix().map(|p| p.as_ref().into());
207 let local: Box<str> = qname.local_name().as_ref().into();
208
209 if prefix.as_deref() == Some("xmlns") {
214 return Name {
215 prefix,
216 local,
217 ns: Ns::Xmlns,
218 };
219 }
220 if prefix.is_none() && &*local == "xmlns" {
221 return Name {
222 prefix: None,
223 local,
224 ns: Ns::Xmlns,
225 };
226 }
227
228 let (resolved, _) = if is_attribute {
229 reader.resolver().resolve_attribute(qname)
230 } else {
231 reader.resolver().resolve_element(qname)
232 };
233 let ns = match resolved {
234 ResolveResult::Bound(uri) => Ns::from_uri(uri.as_ref()),
235 ResolveResult::Unbound | ResolveResult::Unknown(_) => Ns::None,
239 };
240 Name { prefix, local, ns }
241}