use std::collections::BTreeMap;
pub trait Node: Sized {
type Text;
fn name(&self) -> Option<&Self::Text>;
fn text(&self) -> Option<&Self::Text>;
#[must_use]
fn attrs(&self) -> Option<&BTreeMap<Self::Text, Self::Text>>;
#[must_use]
fn get<'a, Q>(&self, name: &'a Q) -> Option<&Self::Text>
where
Self::Text: Ord + From<&'a Q>,
Q: ?Sized,
{
self.attrs().and_then(|a| a.get(&name.into()))
}
fn children(&self) -> &[Self];
fn descendants(&self) -> NodeIter<Self> {
NodeIter::tree(self)
}
fn all_text(&self) -> String
where
Self::Text: std::fmt::Display,
{
self.descendants()
.filter_map(|n| n.text())
.map(ToString::to_string)
.collect::<Vec<_>>()
.join("\n")
}
}
pub enum NodeIter<'x, N> {
Direct {
iter: std::slice::Iter<'x, N>,
},
Tree {
node: &'x N,
child: Option<Box<NodeIter<'x, N>>>,
next: Option<usize>,
},
}
impl<'x, N> NodeIter<'x, N>
where
N: Node,
{
pub(crate) fn direct(iter: std::slice::Iter<'x, N>) -> Self {
Self::Direct { iter }
}
pub(crate) fn tree(node: &'x N) -> Self {
Self::Tree {
node,
child: None,
next: None,
}
}
}
impl<'x, N> Iterator for NodeIter<'x, N>
where
N: Node,
{
type Item = &'x N;
fn next(&mut self) -> Option<Self::Item> {
match self {
NodeIter::Direct { iter } => iter.next(),
NodeIter::Tree { node, child, next } => loop {
if let Some(c) = child.as_mut() {
if let Some(next) = c.next() {
return Some(next);
}
*child = None;
} else if let Some(n) = next {
let children = node.children();
if let Some(c) = children.get(*n) {
*child = Some(Box::new(Self::tree(c)));
*next = Some(*n + 1);
} else {
return None;
}
} else {
*next = Some(0);
return Some(*node);
}
},
}
}
}