use crate::{Cursor, Pattern};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Flow {
Continue,
SkipChildren,
}
pub trait Visitor {
type Output;
type Err;
fn enter(&mut self, _node: Cursor<'_>) -> Result<Flow, Self::Err> {
Ok(Flow::Continue)
}
fn leave(&mut self, _node: Cursor<'_>) -> Result<(), Self::Err> {
Ok(())
}
fn finish(self) -> Result<Self::Output, Self::Err>;
}
pub fn visit<V: Visitor>(pattern: &Pattern, mut visitor: V) -> Result<V::Output, V::Err> {
let mut stack = vec![(pattern.root(), false)];
while let Some((node, leaving)) = stack.pop() {
if leaving {
visitor.leave(node)?;
continue;
}
let flow = visitor.enter(node)?;
stack.push((node, true));
if flow == Flow::Continue {
stack.extend(node.children().rev().map(|child| (child, false)));
}
}
visitor.finish()
}
#[cfg(test)]
mod tests {
use std::convert::Infallible;
use crate::Pattern;
use super::*;
struct Recorder(Vec<String>);
impl Visitor for Recorder {
type Output = Vec<String>;
type Err = Infallible;
fn enter(&mut self, node: Cursor<'_>) -> Result<Flow, Self::Err> {
self.0.push(format!("+{}", node.text()));
Ok(Flow::Continue)
}
fn leave(&mut self, node: Cursor<'_>) -> Result<(), Self::Err> {
self.0.push(format!("-{}", node.text()));
Ok(())
}
fn finish(self) -> Result<Self::Output, Self::Err> {
Ok(self.0)
}
}
#[test]
fn drives_enter_and_leave_without_recursion() {
let pattern = Pattern::parse("ab|c").unwrap();
let events = visit(&pattern, Recorder(Vec::new())).unwrap();
assert_eq!(events.first().unwrap(), "+ab|c");
assert_eq!(events.last().unwrap(), "-ab|c");
assert_eq!(events.len(), pattern.len() * 2);
let first_leaf = pattern
.root()
.walk()
.find(|node| node.text() == "a")
.unwrap();
assert_eq!(first_leaf.ancestors().next().unwrap().text(), "ab");
assert_eq!(first_leaf.next_sibling().unwrap().text(), "b");
}
}