#![allow(dead_code)]
use std::cmp::Ordering;
use xpath_eval::{Document, ExpandedName, Node as XpathNode, NodeKind};
struct NodeData {
kind: NodeKind,
name: Option<ExpandedName>,
text: Option<String>,
parent: Option<usize>,
children: Vec<usize>,
attributes: Vec<usize>,
namespaces: Vec<usize>,
}
struct Arena {
nodes: Vec<NodeData>,
}
pub struct RoxmlDoc {
arena: Arena,
}
impl RoxmlDoc {
pub fn parse(xml: &str) -> Self {
let document = roxmltree::Document::parse(xml).expect("fixture XML must be well-formed");
let mut nodes = vec![NodeData {
kind: NodeKind::Root,
name: None,
text: None,
parent: None,
children: Vec::new(),
attributes: Vec::new(),
namespaces: Vec::new(),
}];
add_children(&mut nodes, 0, document.root());
RoxmlDoc {
arena: Arena { nodes },
}
}
fn node(&self, idx: usize) -> RoxmlNode<'_> {
RoxmlNode {
arena: &self.arena,
idx,
}
}
}
fn normalize_namespace(namespace: Option<&str>) -> Option<String> {
namespace.filter(|uri| !uri.is_empty()).map(str::to_owned)
}
fn add_children(nodes: &mut Vec<NodeData>, parent_idx: usize, roxml_node: roxmltree::Node<'_, '_>) {
for child in roxml_node.children() {
match child.node_type() {
roxmltree::NodeType::Element => {
let idx = nodes.len();
nodes.push(NodeData {
kind: NodeKind::Element,
name: Some(ExpandedName {
namespace_uri: normalize_namespace(child.tag_name().namespace()),
local_name: child.tag_name().name().to_owned(),
}),
text: None,
parent: Some(parent_idx),
children: Vec::new(),
attributes: Vec::new(),
namespaces: Vec::new(),
});
nodes[parent_idx].children.push(idx);
for namespace in child.namespaces() {
let ns_idx = nodes.len();
nodes.push(NodeData {
kind: NodeKind::Namespace,
name: Some(ExpandedName {
namespace_uri: None,
local_name: namespace.name().unwrap_or_default().to_owned(),
}),
text: Some(namespace.uri().to_owned()),
parent: Some(idx),
children: Vec::new(),
attributes: Vec::new(),
namespaces: Vec::new(),
});
nodes[idx].namespaces.push(ns_idx);
}
for attribute in child.attributes() {
let attr_idx = nodes.len();
nodes.push(NodeData {
kind: NodeKind::Attribute,
name: Some(ExpandedName {
namespace_uri: normalize_namespace(attribute.namespace()),
local_name: attribute.name().to_owned(),
}),
text: Some(attribute.value().to_owned()),
parent: Some(idx),
children: Vec::new(),
attributes: Vec::new(),
namespaces: Vec::new(),
});
nodes[idx].attributes.push(attr_idx);
}
add_children(nodes, idx, child);
}
roxmltree::NodeType::Text => {
let idx = nodes.len();
nodes.push(NodeData {
kind: NodeKind::Text,
name: None,
text: Some(child.text().unwrap_or_default().to_owned()),
parent: Some(parent_idx),
children: Vec::new(),
attributes: Vec::new(),
namespaces: Vec::new(),
});
nodes[parent_idx].children.push(idx);
}
roxmltree::NodeType::Comment | roxmltree::NodeType::PI => {}
roxmltree::NodeType::Root => unreachable!("only Document::root() itself is Root"),
}
}
}
impl Document for RoxmlDoc {
type N<'a> = RoxmlNode<'a>;
fn root(&self) -> Self::N<'_> {
self.node(0)
}
}
#[derive(Clone, Copy)]
pub struct RoxmlNode<'a> {
arena: &'a Arena,
idx: usize,
}
impl std::fmt::Debug for RoxmlNode<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "RoxmlNode(#{})", self.idx)
}
}
impl<'a> PartialEq for RoxmlNode<'a> {
fn eq(&self, other: &Self) -> bool {
std::ptr::eq(self.arena, other.arena) && self.idx == other.idx
}
}
impl<'a> Eq for RoxmlNode<'a> {}
impl<'a> XpathNode<'a> for RoxmlNode<'a> {
fn kind(self) -> NodeKind {
self.arena.nodes[self.idx].kind
}
fn parent(self) -> Option<Self> {
self.arena.nodes[self.idx].parent.map(|p| RoxmlNode {
arena: self.arena,
idx: p,
})
}
fn children(self) -> impl Iterator<Item = Self> + 'a {
let arena = self.arena;
self.arena.nodes[self.idx]
.children
.clone()
.into_iter()
.map(move |i| RoxmlNode { arena, idx: i })
}
fn attributes(self) -> impl Iterator<Item = Self> + 'a {
let arena = self.arena;
self.arena.nodes[self.idx]
.attributes
.clone()
.into_iter()
.map(move |i| RoxmlNode { arena, idx: i })
}
fn namespaces(self) -> impl Iterator<Item = Self> + 'a {
let arena = self.arena;
self.arena.nodes[self.idx]
.namespaces
.clone()
.into_iter()
.map(move |i| RoxmlNode { arena, idx: i })
}
fn expanded_name(self) -> Option<ExpandedName> {
self.arena.nodes[self.idx].name.clone()
}
fn string_value(self) -> String {
let data = &self.arena.nodes[self.idx];
match data.kind {
NodeKind::Root | NodeKind::Element => {
let mut out = String::new();
collect_text(self.arena, self.idx, &mut out);
out
}
NodeKind::Attribute | NodeKind::Namespace | NodeKind::Text => {
data.text.clone().unwrap_or_default()
}
NodeKind::ProcessingInstruction | NodeKind::Comment => String::new(),
}
}
fn document_order(self, other: Self) -> Ordering {
self.idx.cmp(&other.idx)
}
}
fn collect_text(arena: &Arena, idx: usize, out: &mut String) {
for &child in &arena.nodes[idx].children {
match arena.nodes[child].kind {
NodeKind::Text => out.push_str(arena.nodes[child].text.as_deref().unwrap_or_default()),
NodeKind::Element => collect_text(arena, child, out),
_ => {}
}
}
}