use std::fmt;
use crate::{
low::v7400::AttributeValue,
tree::v7400::{NodeData, NodeId, NodeNameSym, Tree},
};
#[derive(Debug, Clone, Copy)]
pub struct NodeHandle<'a> {
tree: &'a Tree,
node_id: NodeId,
}
impl<'a> NodeHandle<'a> {
#[inline]
#[must_use]
pub(crate) fn new(tree: &'a Tree, node_id: NodeId) -> Self {
assert!(
tree.contains_node(node_id),
"The node ID is not used in the given tree: node_id={:?}",
node_id
);
Self { tree, node_id }
}
#[inline]
#[must_use]
pub fn tree(&self) -> &'a Tree {
self.tree
}
#[inline]
#[must_use]
pub fn node_id(&self) -> NodeId {
self.node_id
}
#[inline]
#[must_use]
pub(crate) fn node(&self) -> &'a indextree::Node<NodeData> {
self.tree.node(self.node_id)
}
#[inline]
#[must_use]
pub(crate) fn name_sym(&self) -> NodeNameSym {
self.node().get().name_sym()
}
#[inline]
#[must_use]
pub fn name(&self) -> &'a str {
self.tree.resolve_node_name(self.name_sym())
}
#[inline]
#[must_use]
pub fn attributes(&self) -> &'a [AttributeValue] {
self.node().get().attributes()
}
#[inline]
#[must_use]
pub fn children(&self) -> Children<'a> {
Children {
tree: self.tree,
iter: self.node_id.raw().children(&self.tree.arena),
}
}
#[inline]
#[must_use]
pub fn children_by_name(&self, name: &str) -> ChildrenByName<'a> {
ChildrenByName {
name_sym: self.tree.node_name_sym(name),
children_iter: self.children(),
}
}
#[inline]
#[must_use]
pub fn first_child_by_name(&self, name: &str) -> Option<Self> {
self.children_by_name(name).next()
}
#[inline]
#[must_use]
pub fn strict_eq(&self, other: &Self) -> bool {
nodes_strict_eq(*self, *other)
}
}
macro_rules! impl_related_node_accessor {
(
$(
$(#[$meta:meta])*
$accessor:ident;
)*
) => {
impl<'a> NodeHandle<'a> {
$(
impl_related_node_accessor! { @single, $(#[$meta])* $accessor; }
)*
}
};
(@single, $(#[$meta:meta])* $accessor:ident;) => {
$(#[$meta])*
#[must_use]
pub fn $accessor(&self) -> Option<NodeHandle<'a>> {
self.node()
.$accessor()
.map(|id| NodeId::new(id).to_handle(&self.tree))
}
};
}
impl_related_node_accessor! {
parent;
first_child;
last_child;
previous_sibling;
next_sibling;
}
#[must_use]
fn nodes_strict_eq(left: NodeHandle<'_>, right: NodeHandle<'_>) -> bool {
if left.name() != right.name() {
return false;
}
{
let left = left.attributes();
let right = right.attributes();
if left.len() != right.len() {
return false;
}
if !left.iter().zip(right).all(|(l, r)| l.strict_eq(r)) {
return false;
}
}
{
let mut left = left.children();
let mut right = right.children();
loop {
match (left.next(), right.next()) {
(Some(l), Some(r)) => {
if !nodes_strict_eq(l, r) {
return false;
}
}
(None, None) => break,
_ => return false,
}
}
}
true
}
#[derive(Clone)]
pub struct Children<'a> {
tree: &'a Tree,
iter: indextree::Children<'a, NodeData>,
}
impl<'a> Iterator for Children<'a> {
type Item = NodeHandle<'a>;
fn next(&mut self) -> Option<Self::Item> {
let child_id = self.iter.next()?;
Some(NodeId::new(child_id).to_handle(self.tree))
}
}
impl std::iter::FusedIterator for Children<'_> {}
impl<'a> fmt::Debug for Children<'a> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Children").finish()
}
}
#[derive(Clone)]
pub struct ChildrenByName<'a> {
name_sym: Option<NodeNameSym>,
children_iter: Children<'a>,
}
impl<'a> Iterator for ChildrenByName<'a> {
type Item = NodeHandle<'a>;
fn next(&mut self) -> Option<Self::Item> {
let name_sym = self.name_sym?;
self.children_iter
.find(|child| child.name_sym() == name_sym)
}
}
impl std::iter::FusedIterator for ChildrenByName<'_> {}
impl<'a> fmt::Debug for ChildrenByName<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("ChildrenByName")
.field("name_sym", &self.name_sym)
.finish()
}
}