use std::borrow::Cow;
use sup_xml_tree::dom::{Document, Node, NodeKind};
use crate::reader::{Attr, EventInto};
use super::XsdEventSource;
enum Task<'doc> {
Enter(&'doc Node<'doc>),
Leave(&'doc str),
Leaf(&'doc Node<'doc>),
}
pub(crate) struct DocumentEventSource<'doc> {
tasks: Vec<Task<'doc>>,
doc: &'doc Document,
current_elem: std::cell::Cell<Option<&'doc Node<'doc>>>,
}
impl<'doc> DocumentEventSource<'doc> {
pub(crate) fn new(doc: &'doc Document) -> Self {
let mut tasks: Vec<Task<'doc>> = Vec::new();
let mut nodes: Vec<&'doc Node<'doc>> = Vec::new();
let mut cur: Option<&'doc Node<'doc>> = Some(doc.first_sibling());
while let Some(n) = cur {
nodes.push(n);
cur = n.next_sibling.get();
}
for n in nodes.into_iter().rev() {
push_task_for(&mut tasks, n);
}
Self { tasks, doc, current_elem: std::cell::Cell::new(None) }
}
}
fn push_task_for<'doc>(tasks: &mut Vec<Task<'doc>>, n: &'doc Node<'doc>) {
match n.kind {
NodeKind::Element => tasks.push(Task::Enter(n)),
_ => tasks.push(Task::Leaf(n)),
}
}
impl<'doc> XsdEventSource<'doc> for DocumentEventSource<'doc> {
fn next_into(
&mut self, attr_buf: &mut Vec<Attr<'doc>>,
) -> crate::error::Result<EventInto<'doc>> {
attr_buf.clear();
loop {
let Some(task) = self.tasks.pop() else {
return Ok(EventInto::Eof);
};
match task {
Task::Enter(elem) => {
self.tasks.push(Task::Leave(elem.name()));
let mut children: Vec<&'doc Node<'doc>> = elem.children().collect();
while let Some(c) = children.pop() {
push_task_for(&mut self.tasks, c);
}
for a in elem.attributes() {
let n = a.name();
if n == "xmlns" || n.starts_with("xmlns:") { continue; }
attr_buf.push(Attr {
name: n,
value: Cow::Borrowed(a.value()),
});
}
for (prefix, href) in elem.ns_declarations() {
let name: &'doc str = match prefix {
None => "xmlns",
Some(p) => self.doc.bump().alloc_str(&format!("xmlns:{p}")),
};
attr_buf.push(Attr { name, value: Cow::Borrowed(href) });
}
self.current_elem.set(Some(elem));
return Ok(EventInto::StartElement {
name: Cow::Borrowed(elem.name()),
});
}
Task::Leave(name) => {
return Ok(EventInto::EndElement {
name: Cow::Borrowed(name),
});
}
Task::Leaf(n) => match n.kind {
NodeKind::Text =>
return Ok(EventInto::Text(Cow::Borrowed(n.content()))),
NodeKind::CData =>
return Ok(EventInto::CData(Cow::Borrowed(n.content()))),
NodeKind::Comment =>
return Ok(EventInto::Comment(Cow::Borrowed(n.content()))),
NodeKind::Pi =>
return Ok(EventInto::Pi {
target: Cow::Borrowed(n.name()),
content: Cow::Borrowed(n.content()),
}),
NodeKind::EntityRef =>
return Ok(EventInto::EntityRef {
name: Cow::Borrowed(n.name()),
}),
_ => continue,
},
}
}
}
fn last_start_offset(&self) -> Option<usize> { None }
fn src_offset(&self) -> usize { 0 }
fn line_col_at(&self, _offset: usize) -> (u32, u32) { (0, 0) }
fn current_node_key(&self) -> Option<usize> {
self.current_elem.get().map(|n| n as *const Node<'doc> as usize)
}
fn fill_default_attr(&self, name: &str, value: &str) {
let Some(elem) = self.current_elem.get() else { return };
let n = self.doc.bump().alloc_str(name);
let v = self.doc.bump().alloc_str(value);
let attr = self.doc.new_attribute(n, v);
self.doc.append_attribute(elem, attr);
}
}