use std::{collections::HashSet, iter::FusedIterator};
use data::LinkReference;
use super::{CriterionMatcher, DefaultStack, SequenceError};
use crate::Doublets;
#[derive(Clone, Copy, Debug)]
pub struct RightSequenceWalker<M> {
matcher: M,
}
impl<M> RightSequenceWalker<M> {
#[must_use]
pub const fn new(matcher: M) -> Self {
Self { matcher }
}
pub fn iter<'a, T, L>(&'a self, links: &'a L, sequence: T) -> SequenceIter<'a, T, L, M>
where
T: LinkReference,
L: Doublets<T> + ?Sized,
M: CriterionMatcher<T>,
{
let mut stack = DefaultStack::new();
if sequence != links.constants().null {
stack.push((sequence, false));
}
SequenceIter {
links,
matcher: &self.matcher,
stack,
active: HashSet::new(),
}
}
pub fn walk<T, L>(&self, links: &L, sequence: T) -> Result<Vec<T>, SequenceError<T>>
where
T: LinkReference,
L: Doublets<T> + ?Sized,
M: CriterionMatcher<T>,
{
self.iter(links, sequence).collect()
}
}
pub struct SequenceIter<'a, T: LinkReference, L: ?Sized, M> {
links: &'a L,
matcher: &'a M,
stack: DefaultStack<(T, bool)>,
active: HashSet<T>,
}
impl<T, L, M> SequenceIter<'_, T, L, M>
where
T: LinkReference,
L: Doublets<T> + ?Sized,
M: CriterionMatcher<T>,
{
fn advance(&mut self) -> Result<Option<T>, SequenceError<T>> {
while let Some((element, exiting)) = self.stack.pop() {
if exiting {
self.active.remove(&element);
continue;
}
if self.matcher.is_matched(self.links, element) {
return Ok(Some(element));
}
if !self.active.insert(element) {
return Err(SequenceError::CyclicSequence(element));
}
let link = self.links.try_get_link(element)?;
self.stack.push((element, true));
self.stack.push((link.target, false));
self.stack.push((link.source, false));
}
Ok(None)
}
}
impl<T, L, M> Iterator for SequenceIter<'_, T, L, M>
where
T: LinkReference,
L: Doublets<T> + ?Sized,
M: CriterionMatcher<T>,
{
type Item = Result<T, SequenceError<T>>;
fn next(&mut self) -> Option<Self::Item> {
match self.advance() {
Ok(element) => element.map(Ok),
Err(error) => {
self.stack.clear();
self.active.clear();
Some(Err(error))
}
}
}
}
impl<T, L, M> FusedIterator for SequenceIter<'_, T, L, M>
where
T: LinkReference,
L: Doublets<T> + ?Sized,
M: CriterionMatcher<T>,
{
}