use alloc::collections::BTreeMap;
use alloc::string::String;
use alloc::vec::Vec;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct NodeId(pub(crate) usize);
impl NodeId {
#[must_use]
pub const fn index(self) -> usize {
self.0
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ExpandedName {
pub namespace: Option<String>,
pub local: String,
}
impl ExpandedName {
#[must_use]
pub fn local(local: impl Into<String>) -> Self {
Self {
namespace: None,
local: local.into(),
}
}
#[must_use]
pub fn qualified(
namespace: impl Into<String>,
local: impl Into<String>,
) -> Self {
Self {
namespace: Some(namespace.into()),
local: local.into(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Attribute {
pub name: NameId,
pub value: String,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum NodeKind {
Root,
Element {
name: NameId,
attributes: (u32, u32),
namespaces: (u32, u32),
},
Attr(Attribute),
Text(String),
Comment(String),
Namespace {
prefix: String,
uri: String,
},
ProcessingInstruction {
target: String,
data: String,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct NameId(pub(crate) u32);
#[derive(Debug, Clone)]
pub(crate) struct Node {
pub(crate) kind: NodeKind,
pub(crate) parent: Option<NodeId>,
pub(crate) children: (u32, u32),
}
#[derive(Debug, Clone)]
pub struct Document {
pub(crate) nodes: Vec<Node>,
pub(crate) names: Vec<ExpandedName>,
pub(crate) name_prefixes: Vec<Option<String>>,
pub(crate) attr_ids: Vec<NodeId>,
pub(crate) child_ids: Vec<NodeId>,
pub(crate) scratch: Vec<NodeId>,
pub(crate) ids: BTreeMap<String, NodeId>,
pub(crate) ns_ids: Vec<NodeId>,
}
impl Document {
pub(crate) fn with_capacity(nodes: usize) -> Self {
let mut v = Vec::with_capacity(nodes.saturating_add(1));
v.push(Node {
kind: NodeKind::Root,
parent: None,
children: (0, 0),
});
Self {
nodes: v,
names: Vec::new(),
name_prefixes: Vec::new(),
attr_ids: Vec::new(),
child_ids: Vec::with_capacity(nodes),
scratch: Vec::new(),
ids: BTreeMap::new(),
ns_ids: Vec::new(),
}
}
#[must_use]
pub const fn root(&self) -> NodeId {
NodeId(0)
}
pub(crate) fn push(&mut self, kind: NodeKind, parent: NodeId) -> NodeId {
let id = NodeId(self.nodes.len());
self.nodes.push(Node {
kind,
parent: Some(parent),
children: (0, 0),
});
self.scratch.push(id);
id
}
pub(crate) fn scratch_mark(&self) -> usize {
self.scratch.len()
}
pub(crate) fn finish_children(&mut self, parent: NodeId, mark: usize) {
let start = self.child_ids.len();
self.child_ids.extend_from_slice(&self.scratch[mark..]);
self.scratch.truncate(mark);
if let Some(n) = self.nodes.get_mut(parent.0) {
n.children = (
u32::try_from(start).unwrap_or(u32::MAX),
u32::try_from(self.child_ids.len() - start).unwrap_or(0),
);
}
}
pub(crate) fn push_detached(
&mut self,
kind: NodeKind,
parent: NodeId,
) -> NodeId {
let id = NodeId(self.nodes.len());
self.nodes.push(Node {
kind,
parent: Some(parent),
children: (0, 0),
});
id
}
#[must_use]
pub fn kind(&self, id: NodeId) -> Option<&NodeKind> {
self.nodes.get(id.0).map(|n| &n.kind)
}
pub(crate) fn kind_mut(&mut self, id: NodeId) -> Option<&mut NodeKind> {
self.nodes.get_mut(id.0).map(|n| &mut n.kind)
}
#[must_use]
pub fn parent(&self, id: NodeId) -> Option<NodeId> {
self.nodes.get(id.0).and_then(|n| n.parent)
}
#[must_use]
pub fn children(&self, id: NodeId) -> &[NodeId] {
self.nodes.get(id.0).map_or(&[], |n| {
let (start, len) = (n.children.0 as usize, n.children.1 as usize);
self.child_ids
.get(start..start.saturating_add(len))
.unwrap_or(&[])
})
}
#[must_use]
pub fn root_element(&self) -> Option<NodeId> {
self.children(self.root())
.iter()
.copied()
.find(|id| self.is_element(*id))
}
#[must_use]
pub fn is_element(&self, id: NodeId) -> bool {
matches!(self.kind(id), Some(NodeKind::Element { .. }))
}
#[must_use]
pub fn element_name(&self, id: NodeId) -> Option<&ExpandedName> {
match self.kind(id) {
Some(NodeKind::Element { name, .. }) => self.name(*name),
_ => None,
}
}
#[must_use]
pub fn name(&self, id: NameId) -> Option<&ExpandedName> {
self.names.get(id.0 as usize)
}
#[must_use]
pub fn element_by_id(&self, value: &str) -> Option<NodeId> {
self.ids.get(value).copied()
}
#[must_use]
pub fn prefix(&self, id: NameId) -> Option<&str> {
self.name_prefixes.get(id.0 as usize)?.as_deref()
}
#[must_use]
pub fn attribute_nodes(&self, id: NodeId) -> &[NodeId] {
match self.kind(id) {
Some(NodeKind::Element { attributes, .. }) => {
let (start, len) =
(attributes.0 as usize, attributes.1 as usize);
self.attr_ids
.get(start..start.saturating_add(len))
.unwrap_or(&[])
}
_ => &[],
}
}
#[must_use]
pub fn namespace_nodes(&self, id: NodeId) -> &[NodeId] {
match self.kind(id) {
Some(NodeKind::Element { namespaces, .. }) => {
let (start, len) =
(namespaces.0 as usize, namespaces.1 as usize);
self.ns_ids
.get(start..start.saturating_add(len))
.unwrap_or(&[])
}
_ => &[],
}
}
#[must_use]
pub fn attributes(&self, id: NodeId) -> Vec<&Attribute> {
self.attribute_nodes(id)
.iter()
.filter_map(|a| match self.kind(*a) {
Some(NodeKind::Attr(at)) => Some(at),
_ => None,
})
.collect()
}
#[must_use]
pub fn attribute(&self, id: NodeId, local: &str) -> Option<&str> {
self.attributes(id)
.into_iter()
.find(|a| self.name(a.name).is_some_and(|n| n.local == local))
.map(|a| a.value.as_str())
}
#[must_use]
pub fn text(&self, id: NodeId) -> String {
let mut out = String::new();
self.collect_text(id, &mut out);
out
}
fn collect_text(&self, id: NodeId, out: &mut String) {
match self.kind(id) {
Some(NodeKind::Attr(a)) => out.push_str(&a.value),
Some(NodeKind::Namespace { uri, .. }) => out.push_str(uri),
Some(NodeKind::Text(t)) => out.push_str(t),
Some(NodeKind::Root | NodeKind::Element { .. }) => {
for child in self.children(id) {
self.collect_text(*child, out);
}
}
_ => {}
}
}
pub fn descendants(&self) -> impl Iterator<Item = NodeId> + '_ {
(0..self.nodes.len()).map(NodeId)
}
#[must_use]
pub fn len(&self) -> usize {
self.nodes.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.nodes.len() <= 1
}
}