1use alloc::{string::String, vec::Vec};
4
5#[derive(Clone, Debug, PartialEq, Eq)]
7pub struct Name {
8 pub ns: String,
9 pub local: String,
10}
11#[derive(Clone, Debug, PartialEq, Eq)]
13pub struct Node {
14 pub name: Name,
15 pub attrs: Vec<(Name, String)>,
16 pub text: String,
17 pub children: Vec<Node>,
18}
19impl Node {
20 pub fn is(&self, ns: &str, local: &str) -> bool {
22 self.name.ns == ns && self.name.local == local
23 }
24 pub fn attr(&self, ns: &str, local: &str) -> Option<&str> {
26 self.attrs
27 .iter()
28 .find(|(n, _)| n.ns == ns && n.local == local)
29 .map(|(_, v)| v.as_str())
30 }
31}
32#[derive(Clone, Copy, Debug, PartialEq, Eq)]
34#[non_exhaustive]
35pub enum Error {
36 Syntax,
37 Limit,
38 Unsupported,
39}
40pub fn valid_char(c: char) -> bool {
42 matches!(c as u32, 9 | 10 | 13 | 0x20..=0xd7ff | 0xe000..=0xfffd | 0x10000..=0x10ffff)
43}
44pub fn escape(s: &str) -> Result<String, Error> {
46 let mut out = String::new();
47 for c in s.chars() {
48 if !valid_char(c) {
49 return Err(Error::Syntax);
50 }
51 match c {
52 '&' => out.push_str("&"),
53 '<' => out.push_str("<"),
54 '>' => out.push_str(">"),
55 '"' => out.push_str("""),
56 '\'' => out.push_str("'"),
57 '\r' => out.push_str(" "),
58 '\n' => out.push_str(" "),
59 '\t' => out.push_str("	"),
60 _ => out.push(c),
61 }
62 }
63 Ok(out)
64}
65fn unescape(s: &str) -> Result<String, Error> {
66 if s.contains('<') || s.contains("]]>") {
67 return Err(Error::Syntax);
68 }
69 let mut out = String::new();
70 let mut rest = s;
71 while let Some(at) = rest.find('&') {
72 out.push_str(&rest[..at]);
73 rest = &rest[at + 1..];
74 let end = rest.find(';').ok_or(Error::Syntax)?;
75 let e = &rest[..end];
76 let c = match e {
77 "amp" => '&',
78 "lt" => '<',
79 "gt" => '>',
80 "quot" => '"',
81 "apos" => '\'',
82 _ => {
83 let (digits, radix) = if let Some(d) = e.strip_prefix("#x") {
84 (d, 16)
85 } else if let Some(d) = e.strip_prefix('#') {
86 (d, 10)
87 } else {
88 return Err(Error::Unsupported);
89 };
90 if digits.is_empty()
91 || !digits.bytes().all(|b| {
92 if radix == 16 {
93 b.is_ascii_hexdigit()
94 } else {
95 b.is_ascii_digit()
96 }
97 })
98 {
99 return Err(Error::Syntax);
100 }
101 char::from_u32(u32::from_str_radix(digits, radix).map_err(|_| Error::Syntax)?)
102 .ok_or(Error::Syntax)?
103 }
104 };
105 if !valid_char(c) {
106 return Err(Error::Syntax);
107 }
108 out.push(c);
109 rest = &rest[end + 1..];
110 }
111 out.push_str(rest);
112 Ok(out)
113}
114type Namespaces = Vec<(String, String)>;
115fn expand(q: &str, namespaces: &Namespaces, attr: bool) -> Result<Name, Error> {
116 let (prefix, local) = q.split_once(':').unwrap_or(("", q));
117 if local.is_empty() || local.contains(':') {
118 return Err(Error::Syntax);
119 }
120 let ns = if prefix.is_empty() && attr {
121 ""
122 } else if prefix == "xml" {
123 "http://www.w3.org/XML/1998/namespace"
124 } else {
125 match namespaces.iter().rev().find(|(p, _)| p == prefix) {
126 Some((_, n)) => n.as_str(),
127 None if prefix.is_empty() => "",
128 _ => return Err(Error::Syntax),
129 }
130 };
131 Ok(Name {
132 ns: String::from(ns),
133 local: String::from(local),
134 })
135}
136struct Parser<'a> {
137 s: &'a str,
138 pos: usize,
139 nodes: usize,
140}
141impl<'a> Parser<'a> {
142 fn rest(&self) -> &'a str {
143 &self.s[self.pos..]
144 }
145 fn eat(&mut self, s: &str) -> bool {
146 if self.rest().starts_with(s) {
147 self.pos += s.len();
148 true
149 } else {
150 false
151 }
152 }
153 fn ws(&mut self) {
154 while self
155 .rest()
156 .as_bytes()
157 .first()
158 .is_some_and(|b| matches!(b, b' ' | b'\t' | b'\n' | b'\r'))
159 {
160 self.pos += 1;
161 }
162 }
163 fn name(&mut self) -> Result<&'a str, Error> {
164 let start = self.pos;
165 if !self
166 .rest()
167 .as_bytes()
168 .first()
169 .is_some_and(|b| b.is_ascii_alphabetic() || *b == b'_')
170 {
171 return Err(Error::Syntax);
172 }
173 while self
174 .rest()
175 .as_bytes()
176 .first()
177 .is_some_and(|b| b.is_ascii_alphanumeric() || b"_:.-".contains(b))
178 {
179 self.pos += 1;
180 }
181 Ok(&self.s[start..self.pos])
182 }
183 fn node(&mut self, ns: &Namespaces, depth: usize) -> Result<Node, Error> {
184 self.nodes += 1;
185 if depth > 48 || self.nodes > 30000 {
186 return Err(Error::Limit);
187 }
188 if !self.eat("<") {
189 return Err(Error::Syntax);
190 }
191 if self.rest().starts_with(['!', '?']) {
192 return Err(Error::Unsupported);
193 }
194 let q = self.name()?;
195 let mut scope = ns.clone();
196 let mut attrs = Vec::new();
197 let empty;
198 loop {
199 let before = self.pos;
200 self.ws();
201 if self.eat("/>") {
202 empty = true;
203 break;
204 }
205 if self.eat(">") {
206 empty = false;
207 break;
208 }
209 if before == self.pos || attrs.len() >= 64 {
210 return Err(Error::Syntax);
211 }
212 let name = self.name()?;
213 self.ws();
214 if !self.eat("=") {
215 return Err(Error::Syntax);
216 }
217 self.ws();
218 let quote = if self.eat("\"") {
219 '"'
220 } else if self.eat("'") {
221 '\''
222 } else {
223 return Err(Error::Syntax);
224 };
225 let end = self.rest().find(quote).ok_or(Error::Syntax)?;
226 let value = unescape(&self.rest()[..end].replace(['\n', '\t'], " "))?;
227 self.pos += end + 1;
228 if attrs.iter().any(|(n, _)| *n == name) {
229 return Err(Error::Syntax);
230 }
231 if name == "xmlns" || name.starts_with("xmlns:") {
232 let prefix = name.strip_prefix("xmlns:").unwrap_or("");
233 if prefix == "xmlns"
234 || (!prefix.is_empty() && value.is_empty())
235 || value == "http://www.w3.org/2000/xmlns/"
236 || (prefix == "xml") != (value == "http://www.w3.org/XML/1998/namespace")
237 {
238 return Err(Error::Syntax);
239 }
240 scope.push((String::from(prefix), value.clone()));
241 if scope.len() > 64
242 || scope.iter().map(|(p, n)| p.len() + n.len()).sum::<usize>() > 4096
243 {
244 return Err(Error::Limit);
245 }
246 }
247 attrs.push((name, value));
248 }
249 let mut node = Node {
250 name: expand(q, &scope, false)?,
251 attrs: Vec::new(),
252 text: String::new(),
253 children: Vec::new(),
254 };
255 for (q, v) in attrs {
256 if q == "xmlns" || q.starts_with("xmlns:") {
257 continue;
258 }
259 let n = expand(q, &scope, true)?;
260 if node.attrs.iter().any(|(a, _)| *a == n) {
261 return Err(Error::Syntax);
262 }
263 node.attrs.push((n, v));
264 }
265 if empty {
266 return Ok(node);
267 }
268 loop {
269 if self.eat("</") {
270 let end = self.name()?;
271 self.ws();
272 if end != q || !self.eat(">") {
273 return Err(Error::Syntax);
274 }
275 break;
276 }
277 if self.rest().starts_with('<') {
278 node.children.push(self.node(&scope, depth + 1)?);
279 } else {
280 let end = self.rest().find('<').ok_or(Error::Syntax)?;
281 node.text.push_str(&unescape(&self.rest()[..end])?);
282 self.pos += end;
283 }
284 }
285 if !node.children.is_empty() {
286 if !node.text.trim_matches([' ', '\t', '\r', '\n']).is_empty() {
287 return Err(Error::Unsupported);
288 }
289 node.text.clear();
290 }
291 Ok(node)
292 }
293}
294pub fn parse(s: &str) -> Result<Node, Error> {
297 if s.len() > 4 * 1024 * 1024 {
298 return Err(Error::Limit);
299 }
300 if !s.chars().all(valid_char) {
301 return Err(Error::Syntax);
302 }
303 let normalized = s
304 .strip_prefix('\u{feff}')
305 .unwrap_or(s)
306 .replace("\r\n", "\n")
307 .replace('\r', "\n");
308 let mut p = Parser {
309 s: &normalized,
310 pos: 0,
311 nodes: 0,
312 };
313 if p.eat("<?xml ") {
314 let end = p.rest().find("?>").ok_or(Error::Syntax)?;
315 let decl = &p.rest()[..end];
316 let declaration = alloc::format!("<declaration {decl}/>");
317 let mut dp = Parser {
318 s: &declaration,
319 pos: 0,
320 nodes: 0,
321 };
322 let dn = dp.node(&Vec::new(), 0)?;
323 if dn.attrs.is_empty()
324 || dn.attrs[0].0.local != "version"
325 || dn.attrs[0].1 != "1.0"
326 || !dp.rest().is_empty()
327 {
328 return Err(Error::Unsupported);
329 }
330 let mut last = 0;
331 for (name, value) in &dn.attrs {
332 let order = match name.local.as_str() {
333 "version" if value == "1.0" => 1,
334 "encoding"
335 if matches!(
336 value.as_str(),
337 "UTF-8" | "utf-8" | "UTF-16" | "utf-16" | "UTF-16LE" | "UTF-16BE"
338 ) =>
339 {
340 2
341 }
342 "standalone" if matches!(value.as_str(), "yes" | "no") => 3,
343 _ => return Err(Error::Unsupported),
344 };
345 if !name.ns.is_empty() || order <= last {
346 return Err(Error::Syntax);
347 }
348 last = order;
349 }
350 p.pos += end + 2;
351 }
352 p.ws();
353 let node = p.node(&Vec::new(), 0)?;
354 p.ws();
355 if !p.rest().is_empty() {
356 return Err(Error::Syntax);
357 }
358 Ok(node)
359}