use std::ops::ControlFlow::{self};
use crate::ast::{Ast, Object, Property, RootEntry, Value};
#[allow(unused_variables)]
pub trait Visitor {
fn enter_root_entry(&mut self, object: &RootEntry) -> EnterFlow {
Descend::Children.into()
}
fn exit_root_entry(&mut self, object: &RootEntry) -> ExitFlow {
Continue::Siblings.into()
}
fn enter_object(&mut self, object: &Object) -> EnterFlow {
Descend::Children.into()
}
fn exit_object(&mut self, object: &Object) -> ExitFlow {
Continue::Siblings.into()
}
fn enter_property(&mut self, property: &Property) -> EnterFlow {
Descend::Children.into()
}
fn exit_property(&mut self, property: &Property) -> ExitFlow {
Continue::Siblings.into()
}
fn enter_value(&mut self, value: &Value) -> EnterFlow {
Descend::Children.into()
}
fn exit_value(&mut self, value: &Value) -> ExitFlow {
Continue::Siblings.into()
}
}
pub trait VisitorExt: Sized + Visitor {
fn walk(mut self, ast: &Ast) -> Self {
ast.walk(&mut self);
self
}
}
impl<T: Sized + Visitor> VisitorExt for T {}
pub enum Break {
Stop,
Abort,
}
pub enum Continue {
Siblings,
Parent,
}
pub enum Descend {
Children,
Skip,
}
pub type EnterFlow = ControlFlow<Break, Descend>;
pub type ExitFlow = ControlFlow<Break, Continue>;
impl From<Descend> for EnterFlow {
fn from(descend: Descend) -> Self {
ControlFlow::Continue(descend)
}
}
impl From<Continue> for ExitFlow {
fn from(cont: Continue) -> Self {
ControlFlow::Continue(cont)
}
}
fn walk_inner<V, T>(
visitor: &mut V,
node: &T,
enter: fn(&mut V, &T) -> EnterFlow,
exit: fn(&mut V, &T) -> ExitFlow,
children: impl FnOnce(&mut V) -> ControlFlow<Break>,
) -> ExitFlow {
let walked = match enter(visitor, node) {
ControlFlow::Break(b) => ControlFlow::Break(b),
ControlFlow::Continue(Descend::Skip) => ControlFlow::Continue(()),
ControlFlow::Continue(Descend::Children) => children(visitor),
};
if let ControlFlow::Break(Break::Abort) = walked {
return ControlFlow::Break(Break::Abort);
}
match (walked, exit(visitor, node)) {
(_, ControlFlow::Break(Break::Abort)) => ControlFlow::Break(Break::Abort),
(ControlFlow::Break(Break::Stop), _) | (_, ControlFlow::Break(Break::Stop)) => {
ControlFlow::Break(Break::Stop)
}
(_, exit_result) => exit_result,
}
}
fn walk_all<V, T>(
visitor: &mut V,
items: impl IntoIterator<Item = T>,
walk: fn(&mut V, T) -> ExitFlow,
) -> ControlFlow<Break> {
for item in items {
match walk(visitor, item)? {
Continue::Siblings => {}
Continue::Parent => break,
}
}
ControlFlow::Continue(())
}
fn walk_root_object<V: Visitor>(visitor: &mut V, object: &RootEntry) -> ExitFlow {
walk_inner(
visitor,
object,
V::enter_root_entry,
V::exit_root_entry,
|v| walk_object(v, &object.object).map_continue(|_| ()),
)
}
fn walk_object<V: Visitor>(visitor: &mut V, s: &Object) -> ExitFlow {
walk_inner(visitor, s, V::enter_object, V::exit_object, |v| {
walk_all(v, &s.properties, walk_property)
})
}
fn walk_property<V: Visitor>(visitor: &mut V, property: &Property) -> ExitFlow {
walk_inner(
visitor,
property,
V::enter_property,
V::exit_property,
|v| {
property
.value
.as_ref()
.map(|value| walk_value(v, value).map_continue(|_| ()))
.unwrap_or(ControlFlow::Continue(()))
},
)
}
fn walk_value<V: Visitor>(visitor: &mut V, value: &Value) -> ExitFlow {
walk_inner(
visitor,
value,
V::enter_value,
V::exit_value,
|v| match value {
Value::Struct(s) | Value::Embedded(s) => walk_object(v, s).map_continue(|_| ()),
Value::Container { items, .. } | Value::UnorderedContainer { items, .. } => {
walk_all(v, items, walk_value)
}
Value::Map { entries, .. } => walk_all(v, entries, |v, (key, value)| {
match walk_value(v, key)? {
Continue::Siblings => {}
Continue::Parent => return Continue::Parent.into(),
}
match value {
Some(value) => walk_value(v, value),
None => Continue::Siblings.into(),
}
}),
Value::Optional {
value: Some(inner), ..
} => walk_value(v, inner).map_continue(|_| ()),
_ => ControlFlow::Continue(()),
},
)
}
impl Ast {
pub fn walk<V: Visitor>(&self, visitor: &mut V) {
let _ = walk_all(visitor, self.root_entries(), walk_root_object);
}
}