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<'a> {
pub name: NameId,
pub value: &'a str,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum NodeKind<'a> {
Root,
Element {
name: NameId,
attributes: (u32, u32),
namespaces: (u32, u32),
},
Attr(Attribute<'a>),
Text(&'a str),
Comment(&'a str),
Namespace {
prefix: &'a str,
uri: &'a str,
},
ProcessingInstruction {
target: &'a str,
data: &'a str,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum Chars {
Span(u32, u32),
Expanded(u32),
}
impl Chars {
pub(crate) const EMPTY: Self = Self::Span(0, 0);
}
#[derive(Debug, Clone)]
pub(crate) enum NodeData {
Root,
Element {
name: NameId,
attributes: (u32, u32),
namespaces: (u32, u32),
},
Attr {
name: NameId,
value: Chars,
},
Text(Chars),
Comment(Chars),
Namespace {
prefix: Chars,
uri: Chars,
},
ProcessingInstruction {
target: Chars,
data: Chars,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct NameId(pub(crate) u32);
#[derive(Debug, Clone)]
pub(crate) struct Node {
pub(crate) data: NodeData,
pub(crate) parent: Option<NodeId>,
pub(crate) children: (u32, u32),
}
#[derive(Debug, Clone)]
pub struct Document {
pub(crate) input: String,
pub(crate) expanded: Vec<String>,
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) const fn placeholder() -> Self {
Self {
input: String::new(),
expanded: Vec::new(),
nodes: Vec::new(),
names: Vec::new(),
name_prefixes: Vec::new(),
attr_ids: Vec::new(),
child_ids: Vec::new(),
scratch: Vec::new(),
ids: BTreeMap::new(),
ns_ids: Vec::new(),
}
}
pub(crate) fn with_capacity(nodes: usize) -> Self {
let mut v = Vec::with_capacity(nodes.saturating_add(1));
v.push(Node {
data: NodeData::Root,
parent: None,
children: (0, 0),
});
Self {
input: String::new(),
expanded: Vec::new(),
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, data: NodeData, parent: NodeId) -> NodeId {
let id = NodeId(self.nodes.len());
self.nodes.push(Node {
data,
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,
data: NodeData,
parent: NodeId,
) -> NodeId {
let id = NodeId(self.nodes.len());
self.nodes.push(Node {
data,
parent: Some(parent),
children: (0, 0),
});
id
}
pub(crate) fn chars(&self, c: Chars) -> &str {
match c {
Chars::Span(start, len) => {
let (start, len) = (start as usize, len as usize);
self.input.get(start..start + len).unwrap_or_default()
}
Chars::Expanded(i) => self
.expanded
.get(i as usize)
.map_or("", alloc::string::String::as_str),
}
}
#[must_use]
pub fn kind(&self, id: NodeId) -> Option<NodeKind<'_>> {
let node = self.nodes.get(id.0)?;
Some(match &node.data {
NodeData::Root => NodeKind::Root,
NodeData::Element {
name,
attributes,
namespaces,
} => NodeKind::Element {
name: *name,
attributes: *attributes,
namespaces: *namespaces,
},
NodeData::Attr { name, value } => NodeKind::Attr(Attribute {
name: *name,
value: self.chars(*value),
}),
NodeData::Text(c) => NodeKind::Text(self.chars(*c)),
NodeData::Comment(c) => NodeKind::Comment(self.chars(*c)),
NodeData::Namespace { prefix, uri } => NodeKind::Namespace {
prefix: self.chars(*prefix),
uri: self.chars(*uri),
},
NodeData::ProcessingInstruction { target, data } => {
NodeKind::ProcessingInstruction {
target: self.chars(*target),
data: self.chars(*data),
}
}
})
}
pub(crate) fn push_expanded(&mut self, text: &str) -> Chars {
let Ok(i) = u32::try_from(self.expanded.len()) else {
return Chars::EMPTY;
};
self.expanded.push(text.into());
Chars::Expanded(i)
}
pub(crate) fn data_mut(&mut self, id: NodeId) -> Option<&mut NodeData> {
self.nodes.get_mut(id.0).map(|n| &mut n.data)
}
#[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)
}
#[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
}
}