1use pith_digest::{Error, Result};
22
23#[derive(Clone, Debug, PartialEq)]
25pub enum XmlEvent<'a> {
26 Start {
29 name: &'a str,
31 attrs: Vec<(&'a str, String)>,
33 },
34 End {
36 name: &'a str,
38 },
39 Text(String),
42}
43
44pub struct XmlReader<'a> {
49 input: &'a str,
51 stack: Vec<&'a str>,
53 deferred_end: Option<&'a str>,
57 root_seen: bool,
59 done: bool,
61}
62
63impl<'a> XmlReader<'a> {
64 pub fn new(input: &'a str) -> Self {
67 XmlReader {
68 input,
69 stack: Vec::new(),
70 deferred_end: None,
71 root_seen: false,
72 done: false,
73 }
74 }
75
76 pub fn next_event(&mut self) -> Result<Option<XmlEvent<'a>>> {
79 if self.done {
80 return Ok(None);
81 }
82 match self.step() {
83 Ok(e) => Ok(e),
84 Err(e) => {
85 self.done = true;
86 Err(e)
87 }
88 }
89 }
90
91 fn step(&mut self) -> Result<Option<XmlEvent<'a>>> {
93 if let Some(name) = self.deferred_end.take() {
94 return Ok(Some(XmlEvent::End { name }));
95 }
96 if self.input.is_empty() {
97 if self.stack.is_empty() {
98 return Ok(None);
99 }
100 return Err(Error::truncated("xml document", 1, 0));
101 }
102 if let Some(rest) = self.input.strip_prefix('<') {
103 self.input = rest;
104 self.markup()
105 } else {
106 self.take_text()
107 }
108 }
109
110 fn markup(&mut self) -> Result<Option<XmlEvent<'a>>> {
113 if self.input.starts_with("!--") {
114 self.input = &self.input[3..];
115 self.skip_past("-->", "xml comment")?;
116 return self.step();
117 }
118 if self.input.starts_with('?') {
119 self.input = &self.input[1..];
120 self.skip_past("?>", "xml processing instruction")?;
121 return self.step();
122 }
123 if self.input.starts_with("![CDATA[") {
124 self.input = &self.input[8..];
125 let end = self.input.find("]]>").ok_or_else(|| {
126 Error::truncated("xml cdata", self.input.len() + 3, self.input.len())
127 })?;
128 let text = self.input[..end].to_string();
129 self.input = &self.input[end + 3..];
130 return Ok(Some(XmlEvent::Text(text)));
131 }
132 if self.input.starts_with('!') {
133 self.input = &self.input[1..];
136 self.skip_declaration()?;
137 return self.step();
138 }
139 if self.input.starts_with('/') {
140 self.input = &self.input[1..];
141 return self.end_tag();
142 }
143 self.start_tag()
144 }
145
146 fn end_tag(&mut self) -> Result<Option<XmlEvent<'a>>> {
149 let name = self.scan_name("xml end tag name")?;
150 self.skip_ws();
151 match self.take_char() {
152 Some('>') => {}
153 Some(_) => return Err(Error::BadValue("xml end tag")),
154 None => return Err(Error::truncated("xml end tag", 1, 0)),
155 }
156 match self.stack.last() {
157 Some(&open) if open == name => {
158 self.stack.pop();
159 Ok(Some(XmlEvent::End { name }))
160 }
161 _ => Err(Error::BadValue("xml end tag mismatch")),
162 }
163 }
164
165 fn start_tag(&mut self) -> Result<Option<XmlEvent<'a>>> {
167 if self.stack.is_empty() && self.root_seen {
168 return Err(Error::BadValue("xml second root element"));
169 }
170 let name = self.scan_name("xml tag name")?;
171 let mut attrs: Vec<(&'a str, String)> = Vec::new();
172 let self_closing;
173 loop {
174 self.skip_ws();
175 match self.input.as_bytes().first() {
176 Some(b'>') => {
177 self.input = &self.input[1..];
178 self_closing = false;
179 break;
180 }
181 Some(b'/') => {
182 self.input = &self.input[1..];
183 match self.take_char() {
184 Some('>') => {
185 self_closing = true;
186 break;
187 }
188 Some(_) => return Err(Error::BadValue("xml self-closing tag")),
189 None => return Err(Error::truncated("xml tag", 1, 0)),
190 }
191 }
192 Some(_) => {
193 let attr_name = self.scan_name("xml attribute name")?;
194 self.skip_ws();
195 match self.take_char() {
196 Some('=') => {}
197 Some(_) => return Err(Error::BadValue("xml attribute")),
198 None => return Err(Error::truncated("xml attribute", 1, 0)),
199 }
200 self.skip_ws();
201 let quote = match self.take_char() {
202 Some('"') => '"',
203 Some('\'') => '\'',
204 Some(_) => return Err(Error::BadValue("xml attribute quote")),
205 None => return Err(Error::truncated("xml attribute", 1, 0)),
206 };
207 let value = self.take_attr_value(quote)?;
208 if attrs.iter().any(|(n, _)| *n == attr_name) {
209 return Err(Error::BadValue("xml duplicate attribute"));
210 }
211 attrs.push((attr_name, value));
212 }
213 None => return Err(Error::truncated("xml tag", 1, 0)),
214 }
215 }
216 if self_closing {
217 self.deferred_end = Some(name);
218 } else {
219 self.stack.push(name);
220 }
221 if self.stack.len() == 1 && !self_closing {
222 self.root_seen = true;
223 }
224 if self_closing && self.stack.is_empty() {
225 self.root_seen = true;
227 }
228 Ok(Some(XmlEvent::Start { name, attrs }))
229 }
230
231 fn take_text(&mut self) -> Result<Option<XmlEvent<'a>>> {
233 if self.stack.is_empty() {
234 if self.input[..self.text_run_end()].trim().is_empty() {
237 let run = self.text_run_end();
238 self.input = &self.input[run..];
239 return self.step();
240 }
241 return Err(Error::BadValue("xml text outside root element"));
242 }
243 let run = self.text_run_end();
244 let chunk = &self.input[..run];
245 let mut out = String::with_capacity(chunk.len());
246 let mut rest = chunk;
247 while let Some(amp) = rest.find('&') {
248 out.push_str(&rest[..amp]);
249 rest = &rest[amp..];
250 self.decode_entity(&mut rest, &mut out)?;
251 }
252 out.push_str(rest);
253 self.input = &self.input[run..];
254 Ok(Some(XmlEvent::Text(out)))
255 }
256
257 fn text_run_end(&self) -> usize {
260 self.input.find('<').unwrap_or(self.input.len())
261 }
262
263 fn take_attr_value(&mut self, quote: char) -> Result<String> {
266 let mut out = String::new();
267 let mut rest = self.input;
268 loop {
269 let next = rest.find([quote, '<', '&']);
270 match next {
271 None => {
272 return Err(Error::truncated(
273 "xml attribute value",
274 rest.len() + 1,
275 rest.len(),
276 ));
277 }
278 Some(i) => {
279 out.push_str(&rest[..i]);
280 match rest.as_bytes()[i] {
281 b'<' => return Err(Error::BadValue("xml '<' in attribute value")),
282 b'&' => {
283 rest = &rest[i..];
284 self.decode_entity(&mut rest, &mut out)?;
285 }
286 _ => {
287 self.input = &rest[i + 1..];
288 return Ok(out);
289 }
290 }
291 }
292 }
293 }
294 }
295
296 fn decode_entity(&self, rest: &mut &'a str, out: &mut String) -> Result<()> {
299 let semi = rest
300 .find(';')
301 .ok_or(Error::BadValue("xml unterminated entity"))?;
302 let body = &rest[1..semi];
303 if body.is_empty() || body.contains(|c: char| c.is_whitespace() || c == '<' || c == '&') {
304 return Err(Error::BadValue("xml entity"));
305 }
306 let decoded: char = match body {
307 "amp" => '&',
308 "lt" => '<',
309 "gt" => '>',
310 "quot" => '"',
311 "apos" => '\'',
312 _ if body.starts_with("#x") || body.starts_with("#X") => {
313 let digits = &body[2..];
314 let v = u32::from_str_radix(digits, 16)
315 .map_err(|_| Error::BadValue("xml numeric character reference"))?;
316 char::from_u32(v).ok_or(Error::BadValue("xml numeric character reference"))?
317 }
318 _ if body.starts_with('#') => {
319 let digits = &body[1..];
320 let v = digits
321 .parse::<u32>()
322 .map_err(|_| Error::BadValue("xml numeric character reference"))?;
323 char::from_u32(v).ok_or(Error::BadValue("xml numeric character reference"))?
324 }
325 _ => return Err(Error::BadValue("xml unknown entity")),
326 };
327 out.push(decoded);
328 *rest = &rest[semi + 1..];
329 Ok(())
330 }
331
332 fn scan_name(&mut self, what: &'static str) -> Result<&'a str> {
335 let end = self
336 .input
337 .find(|c: char| !(c.is_alphanumeric() || c == '_' || c == '-' || c == '.' || c == ':'))
338 .unwrap_or(self.input.len());
339 if end == 0 {
340 if self.input.is_empty() {
341 return Err(Error::truncated(what, 1, 0));
342 }
343 return Err(Error::BadValue(what));
344 }
345 let (name, rest) = self.input.split_at(end);
346 self.input = rest;
347 Ok(name)
348 }
349
350 fn skip_ws(&mut self) {
352 self.input = self.input.trim_start();
353 }
354
355 fn take_char(&mut self) -> Option<char> {
359 let c = self.input.chars().next()?;
360 self.input = &self.input[c.len_utf8()..];
361 Some(c)
362 }
363
364 fn skip_past(&mut self, needle: &str, what: &'static str) -> Result<()> {
366 match self.input.find(needle) {
367 Some(i) => {
368 self.input = &self.input[i + needle.len()..];
369 Ok(())
370 }
371 None => Err(Error::truncated(
372 what,
373 self.input.len() + needle.len(),
374 self.input.len(),
375 )),
376 }
377 }
378
379 fn skip_declaration(&mut self) -> Result<()> {
383 let mut depth = 0usize;
384 let mut quote: Option<char> = None;
385 for (i, c) in self.input.char_indices() {
386 match quote {
387 Some(q) => {
388 if c == q {
389 quote = None;
390 }
391 }
392 None => match c {
393 '"' | '\'' => quote = Some(c),
394 '[' => depth += 1,
395 ']' => depth = depth.saturating_sub(1),
396 '>' if depth == 0 => {
397 self.input = &self.input[i + 1..];
398 return Ok(());
399 }
400 _ => {}
401 },
402 }
403 }
404 Err(Error::truncated(
405 "xml declaration",
406 self.input.len() + 1,
407 self.input.len(),
408 ))
409 }
410}
411
412impl<'a> Iterator for XmlReader<'a> {
413 type Item = Result<XmlEvent<'a>>;
414
415 fn next(&mut self) -> Option<Self::Item> {
416 self.next_event().transpose()
417 }
418}