1use quarb::names::Names;
34use quarb::{AstAdapter, NodeId, Value};
35use quick_xml::escape::resolve_predefined_entity;
36use quick_xml::events::{BytesStart, Event};
37use quick_xml::{Decoder, Reader};
38use std::collections::HashMap;
39
40struct Node {
41 tag: Option<u32>,
44 attrs: Vec<(u32, String)>,
46 text: String,
49 parent: Option<NodeId>,
50 children: Vec<NodeId>,
51}
52
53impl Node {
54 fn attr(&self, names: &Names, name: &str) -> Option<&str> {
55 let k = names.key(name)?;
56 self.attrs
57 .iter()
58 .find(|(a, _)| *a == k)
59 .map(|(_, v)| v.as_str())
60 }
61}
62
63#[derive(Debug, thiserror::Error)]
65pub enum XmlError {
66 #[error(transparent)]
67 Syntax(#[from] quick_xml::Error),
68 #[error("unknown entity reference '&{0};'")]
69 UnknownEntity(String),
70 #[error("unclosed element '<{0}>'")]
71 Unclosed(String),
72 #[error("no root element")]
73 NoRoot,
74 #[error("content after the root element")]
75 TrailingContent,
76}
77
78pub fn declared_encoding(bytes: &[u8]) -> Option<String> {
87 quarb::source::xml_declared_encoding(bytes)
88}
89
90pub struct XmlAdapter {
92 nodes: Vec<Node>,
93 names: Names,
95 ids: HashMap<String, NodeId>,
97 root: NodeId,
98}
99
100impl XmlAdapter {
101 pub fn parse(xml: &str) -> Result<Self, XmlError> {
105 let mut nodes = vec![Node {
106 tag: None,
107 attrs: Vec::new(),
108 text: String::new(),
109 parent: None,
110 children: Vec::new(),
111 }];
112 let mut ids = HashMap::new();
113 let root = NodeId(0);
114
115 let mut names = Names::new();
116 let mut reader = Reader::from_str(xml);
117 let decoder = reader.decoder();
118 let mut stack: Vec<usize> = vec![0];
121
122 loop {
123 match reader.read_event()? {
124 Event::Start(e) => {
125 let idx = intern(&mut nodes, &mut names, &mut ids, &stack, decoder, &e)?;
126 stack.push(idx);
127 }
128 Event::Empty(e) => {
129 intern(&mut nodes, &mut names, &mut ids, &stack, decoder, &e)?;
130 }
131 Event::End(_) => {
132 let closed = stack.pop().expect("end event matches an open element");
133 let text = std::mem::take(&mut nodes[closed].text);
134 let parent = *stack.last().expect("stack holds the root");
135 nodes[parent].text.push_str(&text);
136 nodes[closed].text = text;
137 }
138 Event::Text(e) => {
139 let open = *stack.last().expect("stack holds the root");
140 let text = e.xml_content().map_err(quick_xml::Error::from)?;
141 nodes[open].text.push_str(&text);
142 }
143 Event::CData(e) => {
144 let open = *stack.last().expect("stack holds the root");
145 let text = e.decode().map_err(quick_xml::Error::from)?;
146 nodes[open].text.push_str(&text);
147 }
148 Event::GeneralRef(e) => {
149 let open = *stack.last().expect("stack holds the root");
150 if let Some(ch) = e.resolve_char_ref()? {
151 nodes[open].text.push(ch);
152 } else {
153 let name = e.decode().map_err(quick_xml::Error::from)?;
154 let Some(resolved) = resolve_predefined_entity(&name) else {
155 return Err(XmlError::UnknownEntity(name.into_owned()));
156 };
157 nodes[open].text.push_str(resolved);
158 }
159 }
160 Event::Decl(_) | Event::PI(_) | Event::Comment(_) | Event::DocType(_) => {}
161 Event::Eof => break,
162 }
163 }
164
165 if stack.len() > 1 {
166 let open = stack.pop().expect("checked non-root");
167 let tag = nodes[open].tag.expect("open elements are tagged");
168 return Err(XmlError::Unclosed(names.get(tag).to_string()));
169 }
170 match nodes[0].children.len() {
171 0 => return Err(XmlError::NoRoot),
172 1 => {}
173 _ => return Err(XmlError::TrailingContent),
174 }
175 nodes[0].text = nodes[nodes[0].children[0].0 as usize].text.clone();
178
179 Ok(XmlAdapter {
180 nodes,
181 names,
182 ids,
183 root,
184 })
185 }
186
187 pub fn locator(&self, node: NodeId) -> String {
191 let mut segments = Vec::new();
192 let mut cur = Some(node);
193 while let Some(id) = cur {
194 let n = &self.nodes[id.0 as usize];
195 if let Some(tag) = n.tag {
196 segments.push(self.segment(id, tag));
197 }
198 cur = n.parent;
199 }
200 segments.reverse();
201 format!("/{}", segments.join("/"))
202 }
203
204 fn segment(&self, node: NodeId, tag: u32) -> String {
205 let name = self.names.get(tag);
206 let Some(parent) = self.nodes[node.0 as usize].parent else {
207 return name.to_string();
208 };
209 let siblings = &self.nodes[parent.0 as usize].children;
210 let same_tag: Vec<NodeId> = siblings
211 .iter()
212 .copied()
213 .filter(|&s| self.nodes[s.0 as usize].tag == Some(tag))
214 .collect();
215 if same_tag.len() > 1 {
216 let n = same_tag.iter().position(|&s| s == node).unwrap() + 1;
217 format!("{name}[{n}]")
218 } else {
219 name.to_string()
220 }
221 }
222}
223
224fn intern(
227 nodes: &mut Vec<Node>,
228 names: &mut Names,
229 ids: &mut HashMap<String, NodeId>,
230 stack: &[usize],
231 decoder: Decoder,
232 e: &BytesStart,
233) -> Result<usize, XmlError> {
234 let idx = nodes.len();
235 let this = NodeId(idx as u64);
236 let tag = names.intern(
237 &decoder
238 .decode(e.name().as_ref())
239 .map_err(quick_xml::Error::from)?,
240 );
241 let mut attrs = Vec::new();
242 for attr in e.attributes() {
243 let attr = attr.map_err(quick_xml::Error::from)?;
244 let key = decoder
245 .decode(attr.key.as_ref())
246 .map_err(quick_xml::Error::from)?
247 .into_owned();
248 let value = attr.decode_and_unescape_value(decoder)?.into_owned();
249 if key == "id" || key == "xml:id" {
250 ids.entry(value.clone()).or_insert(this);
251 }
252 attrs.push((names.intern(&key), value));
253 }
254 let parent = *stack.last().expect("stack holds the root");
255 nodes.push(Node {
256 tag: Some(tag),
257 attrs,
258 text: String::new(),
259 parent: Some(NodeId(parent as u64)),
260 children: Vec::new(),
261 });
262 nodes[parent].children.push(this);
263 Ok(idx)
264}
265
266fn split_qualified(tag: &str) -> (Option<&str>, &str) {
268 match tag.split_once(':') {
269 Some((prefix, local)) => (Some(prefix), local),
270 None => (None, tag),
271 }
272}
273
274impl AstAdapter for XmlAdapter {
275 fn estimate(&self, scope: &[&str], plan: &quarb::StepPlan<'_>) -> Option<quarb::Estimate> {
277 quarb::adapter::arena_estimate(self, scope, plan, quarb::Latency::Memory, None)
278 }
279
280 fn root(&self) -> NodeId {
281 self.root
282 }
283
284 fn walks_for_references(&self) -> bool {
287 true
288 }
289
290 fn children(&self, node: NodeId) -> Vec<NodeId> {
291 self.nodes[node.0 as usize].children.clone()
292 }
293
294 fn name(&self, node: NodeId) -> Option<String> {
295 self.nodes[node.0 as usize]
296 .tag
297 .map(|t| self.names.get(t).to_string())
298 }
299
300 fn traits(&self, node: NodeId) -> Vec<String> {
304 let local = self
305 .name(node)
306 .map(|n| n.rsplit(':').next().unwrap_or("").to_string())
307 .unwrap_or_default();
308 if local == "table" {
309 vec!["tabula".to_string()]
310 } else {
311 Vec::new()
312 }
313 }
314
315 fn parent(&self, node: NodeId) -> Option<NodeId> {
316 self.nodes[node.0 as usize].parent
317 }
318
319 fn ref_property(&self, node: NodeId) -> Option<String> {
326 let n = &self.nodes[node.0 as usize];
327 ["xlink:href", "href"]
328 .into_iter()
329 .find(|a| n.attr(&self.names, a).is_some())
330 .map(str::to_string)
331 }
332
333 fn property_names(&self, node: NodeId) -> Vec<String> {
335 self.nodes[node.0 as usize]
336 .attrs
337 .iter()
338 .map(|(k, _)| self.names.get(*k).to_string())
339 .collect()
340 }
341
342 fn can_refer_to(&self, _source: NodeId, _target: NodeId) -> Option<bool> {
345 Some(true)
346 }
347
348 fn property(&self, node: NodeId, name: &str) -> Option<Value> {
349 self.nodes[node.0 as usize]
350 .attr(&self.names, name)
351 .map(|v| Value::Str(v.to_string()))
352 }
353
354 fn default_value(&self, node: NodeId) -> Option<Value> {
356 Some(Value::Str(self.nodes[node.0 as usize].text.clone()))
357 }
358
359 fn metadata_catalogue(&self) -> &'static [&'static str] {
365 &["tag", "local-name", "ns-prefix", "n-attrs"]
366 }
367
368 fn metadata(&self, node: NodeId, key: &str) -> Option<Value> {
369 let n = &self.nodes[node.0 as usize];
370 match key {
371 "tag" => n.tag.map(|t| Value::Str(self.names.get(t).to_string())),
372 "local-name" => n
373 .tag
374 .map(|t| Value::Str(split_qualified(self.names.get(t)).1.to_string())),
375 "ns-prefix" => n
376 .tag
377 .and_then(|t| split_qualified(self.names.get(t)).0)
378 .map(|p| Value::Str(p.to_string())),
379 "n-attrs" => Some(Value::Int(n.attrs.len() as i64)),
380 _ => None,
381 }
382 }
383
384 fn resolve(&self, node: NodeId, property: &str, _hint: Option<&str>) -> Option<NodeId> {
388 let value = self.nodes[node.0 as usize].attr(&self.names, property)?;
389 let target = value.strip_prefix('#').unwrap_or(value);
390 self.ids.get(target).copied()
391 }
392}
393
394#[cfg(test)]
395mod declared_tests {
396 #[test]
397 fn prologue_declaration() {
398 assert_eq!(
399 super::declared_encoding(b"<?xml version='1.0' encoding = 'Windows-1251' ?><a/>"),
400 Some("Windows-1251".into())
401 );
402 assert_eq!(
403 super::declared_encoding(b"<?xml version=\"1.0\"?><a/>"),
404 None
405 );
406 assert_eq!(super::declared_encoding(b"<a encoding=\"x\"/>"), None);
407 assert_eq!(
408 super::declared_encoding(b"<?xml-stylesheet href=\"a.xsl\" encoding=\"koi8-r\"?><r/>"),
409 None
410 );
411 assert_eq!(
412 super::declared_encoding(b"\xef\xbb\xbf<?xml encoding=\"utf-8\"?>"),
413 None
414 );
415 }
416}