use super::{ModifierNode, ModifierNodeChain, NodeCapabilities, NodePath};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum TraversalDirection {
Forward,
Backward,
}
pub struct ModifierChainIter<'a> {
chain: &'a ModifierNodeChain,
cursor: usize,
remaining: usize,
direction: TraversalDirection,
}
impl<'a> ModifierChainIter<'a> {
pub(super) fn forward(chain: &'a ModifierNodeChain) -> Self {
Self {
chain,
cursor: 0,
remaining: chain.order_len(),
direction: TraversalDirection::Forward,
}
}
pub(super) fn backward(chain: &'a ModifierNodeChain) -> Self {
let len = chain.order_len();
Self {
chain,
cursor: len.wrapping_sub(1),
remaining: len,
direction: TraversalDirection::Backward,
}
}
}
impl<'a> Iterator for ModifierChainIter<'a> {
type Item = ModifierChainNodeRef<'a>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
if self.remaining == 0 {
return None;
}
let node_ref = self.chain.node_ref(ChainPosition::Node(self.cursor));
self.remaining -= 1;
match self.direction {
TraversalDirection::Forward => self.cursor += 1,
TraversalDirection::Backward => self.cursor = self.cursor.wrapping_sub(1),
}
Some(node_ref)
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
(self.remaining, Some(self.remaining))
}
}
impl ExactSizeIterator for ModifierChainIter<'_> {}
impl std::iter::FusedIterator for ModifierChainIter<'_> {}
#[derive(Clone)]
pub struct ModifierChainNodeRef<'a> {
pub(super) chain: &'a ModifierNodeChain,
pub(super) position: ChainPosition,
}
#[derive(Clone, Copy)]
pub(super) enum ChainPosition {
Head,
Node(usize),
Tail,
}
impl ModifierChainNodeRef<'_> {
fn path(&self) -> Option<NodePath> {
match self.position {
ChainPosition::Node(index) => Some(self.chain.order_at(index).0),
ChainPosition::Head | ChainPosition::Tail => None,
}
}
pub fn with_node<R>(&self, f: impl FnOnce(&dyn ModifierNode) -> R) -> Option<R> {
self.chain.with_path(self.path()?, f)
}
#[inline]
pub fn parent(&self) -> Option<Self> {
let position = match self.position {
ChainPosition::Head => return None,
ChainPosition::Node(index) => index.checked_sub(1),
ChainPosition::Tail => self.chain.order_len().checked_sub(1),
}
.map_or(ChainPosition::Head, ChainPosition::Node);
Some(self.chain.node_ref(position))
}
#[inline]
pub fn child(&self) -> Option<Self> {
let next = match self.position {
ChainPosition::Head => 0,
ChainPosition::Node(index) => index + 1,
ChainPosition::Tail => return None,
};
let position = if next < self.chain.order_len() {
ChainPosition::Node(next)
} else {
ChainPosition::Tail
};
Some(self.chain.node_ref(position))
}
#[inline]
pub fn kind_set(&self) -> NodeCapabilities {
match self.position {
ChainPosition::Node(index) => self.chain.order_at(index).1,
ChainPosition::Head | ChainPosition::Tail => NodeCapabilities::empty(),
}
}
pub fn entry_index(&self) -> Option<usize> {
self.path().map(|path| path.entry())
}
pub fn delegate_depth(&self) -> usize {
self.path().map_or(0, |path| path.delegates().len())
}
#[inline]
pub fn aggregate_child_capabilities(&self) -> NodeCapabilities {
match self.position {
ChainPosition::Head => self.chain.aggregated_capabilities,
ChainPosition::Node(index) => self.chain.order_at(index).2,
ChainPosition::Tail => NodeCapabilities::empty(),
}
}
pub fn is_head(&self) -> bool {
matches!(self.position, ChainPosition::Head)
}
pub fn is_tail(&self) -> bool {
matches!(self.position, ChainPosition::Tail)
}
pub fn is_sentinel(&self) -> bool {
matches!(self.position, ChainPosition::Head | ChainPosition::Tail)
}
pub fn has_capability(&self, mask: NodeCapabilities) -> bool {
!mask.is_empty() && self.kind_set().intersects(mask)
}
pub fn visit_descendants<F>(self, include_self: bool, mut f: F)
where
F: FnMut(Self),
{
let mut current = if include_self {
Some(self)
} else {
self.child()
};
while let Some(node) = current {
if node.is_tail() {
break;
}
if !node.is_sentinel() {
f(node.clone());
}
current = node.child();
}
}
pub fn visit_descendants_matching<F>(self, include_self: bool, mask: NodeCapabilities, mut f: F)
where
F: FnMut(Self),
{
if mask.is_empty() {
self.visit_descendants(include_self, f);
return;
}
if !self.aggregate_child_capabilities().intersects(mask) {
return;
}
self.visit_descendants(include_self, |node| {
if node.kind_set().intersects(mask) {
f(node);
}
});
}
pub fn visit_ancestors<F>(self, include_self: bool, mut f: F)
where
F: FnMut(Self),
{
let mut current = if include_self {
Some(self)
} else {
self.parent()
};
while let Some(node) = current {
if node.is_head() {
break;
}
f(node.clone());
current = node.parent();
}
}
pub fn visit_ancestors_matching<F>(self, include_self: bool, mask: NodeCapabilities, mut f: F)
where
F: FnMut(Self),
{
if mask.is_empty() {
self.visit_ancestors(include_self, f);
return;
}
self.visit_ancestors(include_self, |node| {
if node.kind_set().intersects(mask) {
f(node);
}
});
}
}