use std::sync::Arc;
use crate::Result;
macro_rules! handle_transform_recursion {
($F_DOWN:expr, $F_CHILD:expr, $F_UP:expr) => {{
$F_DOWN?
.transform_children(|n| n.map_children($F_CHILD))?
.transform_parent($F_UP)
}};
}
pub trait TreeNode: Sized {
fn visit<V: TreeNodeVisitor<Node = Self>>(
&self,
visitor: &mut V,
) -> Result<TreeNodeRecursion> {
visitor
.f_down(self)?
.visit_children(|| self.apply_children(|c| c.visit(visitor)))?
.visit_parent(|| visitor.f_up(self))
}
fn rewrite<R: TreeNodeRewriter<Node = Self>>(
self,
rewriter: &mut R,
) -> Result<Transformed<Self>> {
handle_transform_recursion!(rewriter.f_down(self), |c| c.rewrite(rewriter), |n| {
rewriter.f_up(n)
})
}
fn apply<F: FnMut(&Self) -> Result<TreeNodeRecursion>>(
&self,
mut f: F,
) -> Result<TreeNodeRecursion> {
fn apply_impl<N: TreeNode, F: FnMut(&N) -> Result<TreeNodeRecursion>>(
node: &N,
f: &mut F,
) -> Result<TreeNodeRecursion> {
f(node)?.visit_children(|| node.apply_children(|c| apply_impl(c, f)))
}
apply_impl(self, &mut f)
}
fn transform<F: FnMut(Self) -> Result<Transformed<Self>>>(
self,
f: F,
) -> Result<Transformed<Self>> {
self.transform_up(f)
}
fn transform_down<F: FnMut(Self) -> Result<Transformed<Self>>>(
self,
mut f: F,
) -> Result<Transformed<Self>> {
fn transform_down_impl<N: TreeNode, F: FnMut(N) -> Result<Transformed<N>>>(
node: N,
f: &mut F,
) -> Result<Transformed<N>> {
f(node)?.transform_children(|n| n.map_children(|c| transform_down_impl(c, f)))
}
transform_down_impl(self, &mut f)
}
#[deprecated(since = "38.0.0", note = "Use `transform_down` instead")]
fn transform_down_mut<F: FnMut(Self) -> Result<Transformed<Self>>>(
self,
f: &mut F,
) -> Result<Transformed<Self>> {
self.transform_down(f)
}
fn transform_up<F: FnMut(Self) -> Result<Transformed<Self>>>(
self,
mut f: F,
) -> Result<Transformed<Self>> {
fn transform_up_impl<N: TreeNode, F: FnMut(N) -> Result<Transformed<N>>>(
node: N,
f: &mut F,
) -> Result<Transformed<N>> {
node.map_children(|c| transform_up_impl(c, f))?
.transform_parent(f)
}
transform_up_impl(self, &mut f)
}
#[deprecated(since = "38.0.0", note = "Use `transform_up` instead")]
fn transform_up_mut<F: FnMut(Self) -> Result<Transformed<Self>>>(
self,
f: &mut F,
) -> Result<Transformed<Self>> {
self.transform_up(f)
}
fn transform_down_up<
FD: FnMut(Self) -> Result<Transformed<Self>>,
FU: FnMut(Self) -> Result<Transformed<Self>>,
>(
self,
mut f_down: FD,
mut f_up: FU,
) -> Result<Transformed<Self>> {
fn transform_down_up_impl<
N: TreeNode,
FD: FnMut(N) -> Result<Transformed<N>>,
FU: FnMut(N) -> Result<Transformed<N>>,
>(
node: N,
f_down: &mut FD,
f_up: &mut FU,
) -> Result<Transformed<N>> {
handle_transform_recursion!(
f_down(node),
|c| transform_down_up_impl(c, f_down, f_up),
f_up
)
}
transform_down_up_impl(self, &mut f_down, &mut f_up)
}
fn exists<F: FnMut(&Self) -> Result<bool>>(&self, mut f: F) -> Result<bool> {
let mut found = false;
self.apply(|n| {
Ok(if f(n)? {
found = true;
TreeNodeRecursion::Stop
} else {
TreeNodeRecursion::Continue
})
})
.map(|_| found)
}
fn apply_children<F: FnMut(&Self) -> Result<TreeNodeRecursion>>(
&self,
f: F,
) -> Result<TreeNodeRecursion>;
fn map_children<F: FnMut(Self) -> Result<Transformed<Self>>>(
self,
f: F,
) -> Result<Transformed<Self>>;
}
pub trait TreeNodeVisitor: Sized {
type Node: TreeNode;
fn f_down(&mut self, _node: &Self::Node) -> Result<TreeNodeRecursion> {
Ok(TreeNodeRecursion::Continue)
}
fn f_up(&mut self, _node: &Self::Node) -> Result<TreeNodeRecursion> {
Ok(TreeNodeRecursion::Continue)
}
}
pub trait TreeNodeRewriter: Sized {
type Node: TreeNode;
fn f_down(&mut self, node: Self::Node) -> Result<Transformed<Self::Node>> {
Ok(Transformed::no(node))
}
fn f_up(&mut self, node: Self::Node) -> Result<Transformed<Self::Node>> {
Ok(Transformed::no(node))
}
}
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum TreeNodeRecursion {
Continue,
Jump,
Stop,
}
impl TreeNodeRecursion {
pub fn visit_children<F: FnOnce() -> Result<TreeNodeRecursion>>(
self,
f: F,
) -> Result<TreeNodeRecursion> {
match self {
TreeNodeRecursion::Continue => f(),
TreeNodeRecursion::Jump => Ok(TreeNodeRecursion::Continue),
TreeNodeRecursion::Stop => Ok(self),
}
}
pub fn visit_sibling<F: FnOnce() -> Result<TreeNodeRecursion>>(
self,
f: F,
) -> Result<TreeNodeRecursion> {
match self {
TreeNodeRecursion::Continue | TreeNodeRecursion::Jump => f(),
TreeNodeRecursion::Stop => Ok(self),
}
}
pub fn visit_parent<F: FnOnce() -> Result<TreeNodeRecursion>>(
self,
f: F,
) -> Result<TreeNodeRecursion> {
match self {
TreeNodeRecursion::Continue => f(),
TreeNodeRecursion::Jump | TreeNodeRecursion::Stop => Ok(self),
}
}
}
#[derive(PartialEq, Debug)]
pub struct Transformed<T> {
pub data: T,
pub transformed: bool,
pub tnr: TreeNodeRecursion,
}
impl<T> Transformed<T> {
pub fn new(data: T, transformed: bool, tnr: TreeNodeRecursion) -> Self {
Self {
data,
transformed,
tnr,
}
}
pub fn yes(data: T) -> Self {
Self::new(data, true, TreeNodeRecursion::Continue)
}
pub fn no(data: T) -> Self {
Self::new(data, false, TreeNodeRecursion::Continue)
}
pub fn update_data<U, F: FnOnce(T) -> U>(self, f: F) -> Transformed<U> {
Transformed::new(f(self.data), self.transformed, self.tnr)
}
pub fn map_data<U, F: FnOnce(T) -> Result<U>>(self, f: F) -> Result<Transformed<U>> {
f(self.data).map(|data| Transformed::new(data, self.transformed, self.tnr))
}
pub fn transform_data<U, F: FnOnce(T) -> Result<Transformed<U>>>(
self,
f: F,
) -> Result<Transformed<U>> {
f(self.data).map(|mut t| {
t.transformed |= self.transformed;
t
})
}
pub fn transform_children<F: FnOnce(T) -> Result<Transformed<T>>>(
mut self,
f: F,
) -> Result<Transformed<T>> {
match self.tnr {
TreeNodeRecursion::Continue => {
return f(self.data).map(|mut t| {
t.transformed |= self.transformed;
t
});
}
TreeNodeRecursion::Jump => {
self.tnr = TreeNodeRecursion::Continue;
}
TreeNodeRecursion::Stop => {}
}
Ok(self)
}
pub fn transform_sibling<F: FnOnce(T) -> Result<Transformed<T>>>(
self,
f: F,
) -> Result<Transformed<T>> {
match self.tnr {
TreeNodeRecursion::Continue | TreeNodeRecursion::Jump => {
f(self.data).map(|mut t| {
t.transformed |= self.transformed;
t
})
}
TreeNodeRecursion::Stop => Ok(self),
}
}
pub fn transform_parent<F: FnOnce(T) -> Result<Transformed<T>>>(
self,
f: F,
) -> Result<Transformed<T>> {
match self.tnr {
TreeNodeRecursion::Continue => f(self.data).map(|mut t| {
t.transformed |= self.transformed;
t
}),
TreeNodeRecursion::Jump | TreeNodeRecursion::Stop => Ok(self),
}
}
}
pub trait TreeNodeIterator: Iterator {
fn apply_until_stop<F: FnMut(Self::Item) -> Result<TreeNodeRecursion>>(
self,
f: F,
) -> Result<TreeNodeRecursion>;
fn map_until_stop_and_collect<
F: FnMut(Self::Item) -> Result<Transformed<Self::Item>>,
>(
self,
f: F,
) -> Result<Transformed<Vec<Self::Item>>>;
}
impl<I: Iterator> TreeNodeIterator for I {
fn apply_until_stop<F: FnMut(Self::Item) -> Result<TreeNodeRecursion>>(
self,
mut f: F,
) -> Result<TreeNodeRecursion> {
let mut tnr = TreeNodeRecursion::Continue;
for i in self {
tnr = f(i)?;
match tnr {
TreeNodeRecursion::Continue | TreeNodeRecursion::Jump => {}
TreeNodeRecursion::Stop => return Ok(TreeNodeRecursion::Stop),
}
}
Ok(tnr)
}
fn map_until_stop_and_collect<
F: FnMut(Self::Item) -> Result<Transformed<Self::Item>>,
>(
self,
mut f: F,
) -> Result<Transformed<Vec<Self::Item>>> {
let mut tnr = TreeNodeRecursion::Continue;
let mut transformed = false;
self.map(|item| match tnr {
TreeNodeRecursion::Continue | TreeNodeRecursion::Jump => {
f(item).map(|result| {
tnr = result.tnr;
transformed |= result.transformed;
result.data
})
}
TreeNodeRecursion::Stop => Ok(item),
})
.collect::<Result<Vec<_>>>()
.map(|data| Transformed::new(data, transformed, tnr))
}
}
#[macro_export]
macro_rules! map_until_stop_and_collect {
($F0:expr, $($EXPR:expr, $F:expr),*) => {{
$F0.and_then(|Transformed { data: data0, mut transformed, mut tnr }| {
let all_datas = (
data0,
$(
if tnr == TreeNodeRecursion::Continue || tnr == TreeNodeRecursion::Jump {
$F.map(|result| {
tnr = result.tnr;
transformed |= result.transformed;
result.data
})?
} else {
$EXPR
},
)*
);
Ok(Transformed::new(all_datas, transformed, tnr))
})
}}
}
pub trait TransformedResult<T> {
fn data(self) -> Result<T>;
fn transformed(self) -> Result<bool>;
fn tnr(self) -> Result<TreeNodeRecursion>;
}
impl<T> TransformedResult<T> for Result<Transformed<T>> {
fn data(self) -> Result<T> {
self.map(|t| t.data)
}
fn transformed(self) -> Result<bool> {
self.map(|t| t.transformed)
}
fn tnr(self) -> Result<TreeNodeRecursion> {
self.map(|t| t.tnr)
}
}
pub trait DynTreeNode {
fn arc_children(&self) -> Vec<Arc<Self>>;
fn with_new_arc_children(
&self,
arc_self: Arc<Self>,
new_children: Vec<Arc<Self>>,
) -> Result<Arc<Self>>;
}
impl<T: DynTreeNode + ?Sized> TreeNode for Arc<T> {
fn apply_children<F: FnMut(&Self) -> Result<TreeNodeRecursion>>(
&self,
f: F,
) -> Result<TreeNodeRecursion> {
self.arc_children().iter().apply_until_stop(f)
}
fn map_children<F: FnMut(Self) -> Result<Transformed<Self>>>(
self,
f: F,
) -> Result<Transformed<Self>> {
let children = self.arc_children();
if !children.is_empty() {
let new_children = children.into_iter().map_until_stop_and_collect(f)?;
if new_children.transformed {
let arc_self = Arc::clone(&self);
new_children.map_data(|new_children| {
self.with_new_arc_children(arc_self, new_children)
})
} else {
Ok(Transformed::new(self, false, new_children.tnr))
}
} else {
Ok(Transformed::no(self))
}
}
}
pub trait ConcreteTreeNode: Sized {
fn children(&self) -> Vec<&Self>;
fn take_children(self) -> (Self, Vec<Self>);
fn with_new_children(self, children: Vec<Self>) -> Result<Self>;
}
impl<T: ConcreteTreeNode> TreeNode for T {
fn apply_children<F: FnMut(&Self) -> Result<TreeNodeRecursion>>(
&self,
f: F,
) -> Result<TreeNodeRecursion> {
self.children().into_iter().apply_until_stop(f)
}
fn map_children<F: FnMut(Self) -> Result<Transformed<Self>>>(
self,
f: F,
) -> Result<Transformed<Self>> {
let (new_self, children) = self.take_children();
if !children.is_empty() {
let new_children = children.into_iter().map_until_stop_and_collect(f)?;
new_children.map_data(|new_children| new_self.with_new_children(new_children))
} else {
Ok(Transformed::no(new_self))
}
}
}
#[cfg(test)]
mod tests {
use std::fmt::Display;
use crate::tree_node::{
Transformed, TreeNode, TreeNodeIterator, TreeNodeRecursion, TreeNodeRewriter,
TreeNodeVisitor,
};
use crate::Result;
#[derive(PartialEq, Debug)]
struct TestTreeNode<T> {
children: Vec<TestTreeNode<T>>,
data: T,
}
impl<T> TestTreeNode<T> {
fn new(children: Vec<TestTreeNode<T>>, data: T) -> Self {
Self { children, data }
}
}
impl<T> TreeNode for TestTreeNode<T> {
fn apply_children<F: FnMut(&Self) -> Result<TreeNodeRecursion>>(
&self,
f: F,
) -> Result<TreeNodeRecursion> {
self.children.iter().apply_until_stop(f)
}
fn map_children<F: FnMut(Self) -> Result<Transformed<Self>>>(
self,
f: F,
) -> Result<Transformed<Self>> {
Ok(self
.children
.into_iter()
.map_until_stop_and_collect(f)?
.update_data(|new_children| Self {
children: new_children,
..self
}))
}
}
fn test_tree() -> TestTreeNode<String> {
let node_a = TestTreeNode::new(vec![], "a".to_string());
let node_b = TestTreeNode::new(vec![], "b".to_string());
let node_d = TestTreeNode::new(vec![node_a], "d".to_string());
let node_c = TestTreeNode::new(vec![node_b, node_d], "c".to_string());
let node_e = TestTreeNode::new(vec![node_c], "e".to_string());
let node_h = TestTreeNode::new(vec![], "h".to_string());
let node_g = TestTreeNode::new(vec![node_h], "g".to_string());
let node_f = TestTreeNode::new(vec![node_e, node_g], "f".to_string());
let node_i = TestTreeNode::new(vec![node_f], "i".to_string());
TestTreeNode::new(vec![node_i], "j".to_string())
}
fn all_visits() -> Vec<String> {
vec![
"f_down(j)",
"f_down(i)",
"f_down(f)",
"f_down(e)",
"f_down(c)",
"f_down(b)",
"f_up(b)",
"f_down(d)",
"f_down(a)",
"f_up(a)",
"f_up(d)",
"f_up(c)",
"f_up(e)",
"f_down(g)",
"f_down(h)",
"f_up(h)",
"f_up(g)",
"f_up(f)",
"f_up(i)",
"f_up(j)",
]
.into_iter()
.map(|s| s.to_string())
.collect()
}
fn transformed_tree() -> TestTreeNode<String> {
let node_a = TestTreeNode::new(vec![], "f_up(f_down(a))".to_string());
let node_b = TestTreeNode::new(vec![], "f_up(f_down(b))".to_string());
let node_d = TestTreeNode::new(vec![node_a], "f_up(f_down(d))".to_string());
let node_c =
TestTreeNode::new(vec![node_b, node_d], "f_up(f_down(c))".to_string());
let node_e = TestTreeNode::new(vec![node_c], "f_up(f_down(e))".to_string());
let node_h = TestTreeNode::new(vec![], "f_up(f_down(h))".to_string());
let node_g = TestTreeNode::new(vec![node_h], "f_up(f_down(g))".to_string());
let node_f =
TestTreeNode::new(vec![node_e, node_g], "f_up(f_down(f))".to_string());
let node_i = TestTreeNode::new(vec![node_f], "f_up(f_down(i))".to_string());
TestTreeNode::new(vec![node_i], "f_up(f_down(j))".to_string())
}
fn transformed_down_tree() -> TestTreeNode<String> {
let node_a = TestTreeNode::new(vec![], "f_down(a)".to_string());
let node_b = TestTreeNode::new(vec![], "f_down(b)".to_string());
let node_d = TestTreeNode::new(vec![node_a], "f_down(d)".to_string());
let node_c = TestTreeNode::new(vec![node_b, node_d], "f_down(c)".to_string());
let node_e = TestTreeNode::new(vec![node_c], "f_down(e)".to_string());
let node_h = TestTreeNode::new(vec![], "f_down(h)".to_string());
let node_g = TestTreeNode::new(vec![node_h], "f_down(g)".to_string());
let node_f = TestTreeNode::new(vec![node_e, node_g], "f_down(f)".to_string());
let node_i = TestTreeNode::new(vec![node_f], "f_down(i)".to_string());
TestTreeNode::new(vec![node_i], "f_down(j)".to_string())
}
fn transformed_up_tree() -> TestTreeNode<String> {
let node_a = TestTreeNode::new(vec![], "f_up(a)".to_string());
let node_b = TestTreeNode::new(vec![], "f_up(b)".to_string());
let node_d = TestTreeNode::new(vec![node_a], "f_up(d)".to_string());
let node_c = TestTreeNode::new(vec![node_b, node_d], "f_up(c)".to_string());
let node_e = TestTreeNode::new(vec![node_c], "f_up(e)".to_string());
let node_h = TestTreeNode::new(vec![], "f_up(h)".to_string());
let node_g = TestTreeNode::new(vec![node_h], "f_up(g)".to_string());
let node_f = TestTreeNode::new(vec![node_e, node_g], "f_up(f)".to_string());
let node_i = TestTreeNode::new(vec![node_f], "f_up(i)".to_string());
TestTreeNode::new(vec![node_i], "f_up(j)".to_string())
}
fn f_down_jump_on_a_visits() -> Vec<String> {
vec![
"f_down(j)",
"f_down(i)",
"f_down(f)",
"f_down(e)",
"f_down(c)",
"f_down(b)",
"f_up(b)",
"f_down(d)",
"f_down(a)",
"f_up(a)",
"f_up(d)",
"f_up(c)",
"f_up(e)",
"f_down(g)",
"f_down(h)",
"f_up(h)",
"f_up(g)",
"f_up(f)",
"f_up(i)",
"f_up(j)",
]
.into_iter()
.map(|s| s.to_string())
.collect()
}
fn f_down_jump_on_a_transformed_down_tree() -> TestTreeNode<String> {
let node_a = TestTreeNode::new(vec![], "f_down(a)".to_string());
let node_b = TestTreeNode::new(vec![], "f_down(b)".to_string());
let node_d = TestTreeNode::new(vec![node_a], "f_down(d)".to_string());
let node_c = TestTreeNode::new(vec![node_b, node_d], "f_down(c)".to_string());
let node_e = TestTreeNode::new(vec![node_c], "f_down(e)".to_string());
let node_h = TestTreeNode::new(vec![], "f_down(h)".to_string());
let node_g = TestTreeNode::new(vec![node_h], "f_down(g)".to_string());
let node_f = TestTreeNode::new(vec![node_e, node_g], "f_down(f)".to_string());
let node_i = TestTreeNode::new(vec![node_f], "f_down(i)".to_string());
TestTreeNode::new(vec![node_i], "f_down(j)".to_string())
}
fn f_down_jump_on_e_visits() -> Vec<String> {
vec![
"f_down(j)",
"f_down(i)",
"f_down(f)",
"f_down(e)",
"f_up(e)",
"f_down(g)",
"f_down(h)",
"f_up(h)",
"f_up(g)",
"f_up(f)",
"f_up(i)",
"f_up(j)",
]
.into_iter()
.map(|s| s.to_string())
.collect()
}
fn f_down_jump_on_e_transformed_tree() -> TestTreeNode<String> {
let node_a = TestTreeNode::new(vec![], "a".to_string());
let node_b = TestTreeNode::new(vec![], "b".to_string());
let node_d = TestTreeNode::new(vec![node_a], "d".to_string());
let node_c = TestTreeNode::new(vec![node_b, node_d], "c".to_string());
let node_e = TestTreeNode::new(vec![node_c], "f_up(f_down(e))".to_string());
let node_h = TestTreeNode::new(vec![], "f_up(f_down(h))".to_string());
let node_g = TestTreeNode::new(vec![node_h], "f_up(f_down(g))".to_string());
let node_f =
TestTreeNode::new(vec![node_e, node_g], "f_up(f_down(f))".to_string());
let node_i = TestTreeNode::new(vec![node_f], "f_up(f_down(i))".to_string());
TestTreeNode::new(vec![node_i], "f_up(f_down(j))".to_string())
}
fn f_down_jump_on_e_transformed_down_tree() -> TestTreeNode<String> {
let node_a = TestTreeNode::new(vec![], "a".to_string());
let node_b = TestTreeNode::new(vec![], "b".to_string());
let node_d = TestTreeNode::new(vec![node_a], "d".to_string());
let node_c = TestTreeNode::new(vec![node_b, node_d], "c".to_string());
let node_e = TestTreeNode::new(vec![node_c], "f_down(e)".to_string());
let node_h = TestTreeNode::new(vec![], "f_down(h)".to_string());
let node_g = TestTreeNode::new(vec![node_h], "f_down(g)".to_string());
let node_f = TestTreeNode::new(vec![node_e, node_g], "f_down(f)".to_string());
let node_i = TestTreeNode::new(vec![node_f], "f_down(i)".to_string());
TestTreeNode::new(vec![node_i], "f_down(j)".to_string())
}
fn f_up_jump_on_a_visits() -> Vec<String> {
vec![
"f_down(j)",
"f_down(i)",
"f_down(f)",
"f_down(e)",
"f_down(c)",
"f_down(b)",
"f_up(b)",
"f_down(d)",
"f_down(a)",
"f_up(a)",
"f_down(g)",
"f_down(h)",
"f_up(h)",
"f_up(g)",
"f_up(f)",
"f_up(i)",
"f_up(j)",
]
.into_iter()
.map(|s| s.to_string())
.collect()
}
fn f_up_jump_on_a_transformed_tree() -> TestTreeNode<String> {
let node_a = TestTreeNode::new(vec![], "f_up(f_down(a))".to_string());
let node_b = TestTreeNode::new(vec![], "f_up(f_down(b))".to_string());
let node_d = TestTreeNode::new(vec![node_a], "f_down(d)".to_string());
let node_c = TestTreeNode::new(vec![node_b, node_d], "f_down(c)".to_string());
let node_e = TestTreeNode::new(vec![node_c], "f_down(e)".to_string());
let node_h = TestTreeNode::new(vec![], "f_up(f_down(h))".to_string());
let node_g = TestTreeNode::new(vec![node_h], "f_up(f_down(g))".to_string());
let node_f =
TestTreeNode::new(vec![node_e, node_g], "f_up(f_down(f))".to_string());
let node_i = TestTreeNode::new(vec![node_f], "f_up(f_down(i))".to_string());
TestTreeNode::new(vec![node_i], "f_up(f_down(j))".to_string())
}
fn f_up_jump_on_a_transformed_up_tree() -> TestTreeNode<String> {
let node_a = TestTreeNode::new(vec![], "f_up(a)".to_string());
let node_b = TestTreeNode::new(vec![], "f_up(b)".to_string());
let node_d = TestTreeNode::new(vec![node_a], "d".to_string());
let node_c = TestTreeNode::new(vec![node_b, node_d], "c".to_string());
let node_e = TestTreeNode::new(vec![node_c], "e".to_string());
let node_h = TestTreeNode::new(vec![], "f_up(h)".to_string());
let node_g = TestTreeNode::new(vec![node_h], "f_up(g)".to_string());
let node_f = TestTreeNode::new(vec![node_e, node_g], "f_up(f)".to_string());
let node_i = TestTreeNode::new(vec![node_f], "f_up(i)".to_string());
TestTreeNode::new(vec![node_i], "f_up(j)".to_string())
}
fn f_up_jump_on_e_visits() -> Vec<String> {
vec![
"f_down(j)",
"f_down(i)",
"f_down(f)",
"f_down(e)",
"f_down(c)",
"f_down(b)",
"f_up(b)",
"f_down(d)",
"f_down(a)",
"f_up(a)",
"f_up(d)",
"f_up(c)",
"f_up(e)",
"f_down(g)",
"f_down(h)",
"f_up(h)",
"f_up(g)",
"f_up(f)",
"f_up(i)",
"f_up(j)",
]
.into_iter()
.map(|s| s.to_string())
.collect()
}
fn f_up_jump_on_e_transformed_tree() -> TestTreeNode<String> {
transformed_tree()
}
fn f_up_jump_on_e_transformed_up_tree() -> TestTreeNode<String> {
transformed_up_tree()
}
fn f_down_stop_on_a_visits() -> Vec<String> {
vec![
"f_down(j)",
"f_down(i)",
"f_down(f)",
"f_down(e)",
"f_down(c)",
"f_down(b)",
"f_up(b)",
"f_down(d)",
"f_down(a)",
]
.into_iter()
.map(|s| s.to_string())
.collect()
}
fn f_down_stop_on_a_transformed_tree() -> TestTreeNode<String> {
let node_a = TestTreeNode::new(vec![], "f_down(a)".to_string());
let node_b = TestTreeNode::new(vec![], "f_up(f_down(b))".to_string());
let node_d = TestTreeNode::new(vec![node_a], "f_down(d)".to_string());
let node_c = TestTreeNode::new(vec![node_b, node_d], "f_down(c)".to_string());
let node_e = TestTreeNode::new(vec![node_c], "f_down(e)".to_string());
let node_h = TestTreeNode::new(vec![], "h".to_string());
let node_g = TestTreeNode::new(vec![node_h], "g".to_string());
let node_f = TestTreeNode::new(vec![node_e, node_g], "f_down(f)".to_string());
let node_i = TestTreeNode::new(vec![node_f], "f_down(i)".to_string());
TestTreeNode::new(vec![node_i], "f_down(j)".to_string())
}
fn f_down_stop_on_a_transformed_down_tree() -> TestTreeNode<String> {
let node_a = TestTreeNode::new(vec![], "f_down(a)".to_string());
let node_b = TestTreeNode::new(vec![], "f_down(b)".to_string());
let node_d = TestTreeNode::new(vec![node_a], "f_down(d)".to_string());
let node_c = TestTreeNode::new(vec![node_b, node_d], "f_down(c)".to_string());
let node_e = TestTreeNode::new(vec![node_c], "f_down(e)".to_string());
let node_h = TestTreeNode::new(vec![], "h".to_string());
let node_g = TestTreeNode::new(vec![node_h], "g".to_string());
let node_f = TestTreeNode::new(vec![node_e, node_g], "f_down(f)".to_string());
let node_i = TestTreeNode::new(vec![node_f], "f_down(i)".to_string());
TestTreeNode::new(vec![node_i], "f_down(j)".to_string())
}
fn f_down_stop_on_e_visits() -> Vec<String> {
vec!["f_down(j)", "f_down(i)", "f_down(f)", "f_down(e)"]
.into_iter()
.map(|s| s.to_string())
.collect()
}
fn f_down_stop_on_e_transformed_tree() -> TestTreeNode<String> {
let node_a = TestTreeNode::new(vec![], "a".to_string());
let node_b = TestTreeNode::new(vec![], "b".to_string());
let node_d = TestTreeNode::new(vec![node_a], "d".to_string());
let node_c = TestTreeNode::new(vec![node_b, node_d], "c".to_string());
let node_e = TestTreeNode::new(vec![node_c], "f_down(e)".to_string());
let node_h = TestTreeNode::new(vec![], "h".to_string());
let node_g = TestTreeNode::new(vec![node_h], "g".to_string());
let node_f = TestTreeNode::new(vec![node_e, node_g], "f_down(f)".to_string());
let node_i = TestTreeNode::new(vec![node_f], "f_down(i)".to_string());
TestTreeNode::new(vec![node_i], "f_down(j)".to_string())
}
fn f_down_stop_on_e_transformed_down_tree() -> TestTreeNode<String> {
let node_a = TestTreeNode::new(vec![], "a".to_string());
let node_b = TestTreeNode::new(vec![], "b".to_string());
let node_d = TestTreeNode::new(vec![node_a], "d".to_string());
let node_c = TestTreeNode::new(vec![node_b, node_d], "c".to_string());
let node_e = TestTreeNode::new(vec![node_c], "f_down(e)".to_string());
let node_h = TestTreeNode::new(vec![], "h".to_string());
let node_g = TestTreeNode::new(vec![node_h], "g".to_string());
let node_f = TestTreeNode::new(vec![node_e, node_g], "f_down(f)".to_string());
let node_i = TestTreeNode::new(vec![node_f], "f_down(i)".to_string());
TestTreeNode::new(vec![node_i], "f_down(j)".to_string())
}
fn f_up_stop_on_a_visits() -> Vec<String> {
vec![
"f_down(j)",
"f_down(i)",
"f_down(f)",
"f_down(e)",
"f_down(c)",
"f_down(b)",
"f_up(b)",
"f_down(d)",
"f_down(a)",
"f_up(a)",
]
.into_iter()
.map(|s| s.to_string())
.collect()
}
fn f_up_stop_on_a_transformed_tree() -> TestTreeNode<String> {
let node_a = TestTreeNode::new(vec![], "f_up(f_down(a))".to_string());
let node_b = TestTreeNode::new(vec![], "f_up(f_down(b))".to_string());
let node_d = TestTreeNode::new(vec![node_a], "f_down(d)".to_string());
let node_c = TestTreeNode::new(vec![node_b, node_d], "f_down(c)".to_string());
let node_e = TestTreeNode::new(vec![node_c], "f_down(e)".to_string());
let node_h = TestTreeNode::new(vec![], "h".to_string());
let node_g = TestTreeNode::new(vec![node_h], "g".to_string());
let node_f = TestTreeNode::new(vec![node_e, node_g], "f_down(f)".to_string());
let node_i = TestTreeNode::new(vec![node_f], "f_down(i)".to_string());
TestTreeNode::new(vec![node_i], "f_down(j)".to_string())
}
fn f_up_stop_on_a_transformed_up_tree() -> TestTreeNode<String> {
let node_a = TestTreeNode::new(vec![], "f_up(a)".to_string());
let node_b = TestTreeNode::new(vec![], "f_up(b)".to_string());
let node_d = TestTreeNode::new(vec![node_a], "d".to_string());
let node_c = TestTreeNode::new(vec![node_b, node_d], "c".to_string());
let node_e = TestTreeNode::new(vec![node_c], "e".to_string());
let node_h = TestTreeNode::new(vec![], "h".to_string());
let node_g = TestTreeNode::new(vec![node_h], "g".to_string());
let node_f = TestTreeNode::new(vec![node_e, node_g], "f".to_string());
let node_i = TestTreeNode::new(vec![node_f], "i".to_string());
TestTreeNode::new(vec![node_i], "j".to_string())
}
fn f_up_stop_on_e_visits() -> Vec<String> {
vec![
"f_down(j)",
"f_down(i)",
"f_down(f)",
"f_down(e)",
"f_down(c)",
"f_down(b)",
"f_up(b)",
"f_down(d)",
"f_down(a)",
"f_up(a)",
"f_up(d)",
"f_up(c)",
"f_up(e)",
]
.into_iter()
.map(|s| s.to_string())
.collect()
}
fn f_up_stop_on_e_transformed_tree() -> TestTreeNode<String> {
let node_a = TestTreeNode::new(vec![], "f_up(f_down(a))".to_string());
let node_b = TestTreeNode::new(vec![], "f_up(f_down(b))".to_string());
let node_d = TestTreeNode::new(vec![node_a], "f_up(f_down(d))".to_string());
let node_c =
TestTreeNode::new(vec![node_b, node_d], "f_up(f_down(c))".to_string());
let node_e = TestTreeNode::new(vec![node_c], "f_up(f_down(e))".to_string());
let node_h = TestTreeNode::new(vec![], "h".to_string());
let node_g = TestTreeNode::new(vec![node_h], "g".to_string());
let node_f = TestTreeNode::new(vec![node_e, node_g], "f_down(f)".to_string());
let node_i = TestTreeNode::new(vec![node_f], "f_down(i)".to_string());
TestTreeNode::new(vec![node_i], "f_down(j)".to_string())
}
fn f_up_stop_on_e_transformed_up_tree() -> TestTreeNode<String> {
let node_a = TestTreeNode::new(vec![], "f_up(a)".to_string());
let node_b = TestTreeNode::new(vec![], "f_up(b)".to_string());
let node_d = TestTreeNode::new(vec![node_a], "f_up(d)".to_string());
let node_c = TestTreeNode::new(vec![node_b, node_d], "f_up(c)".to_string());
let node_e = TestTreeNode::new(vec![node_c], "f_up(e)".to_string());
let node_h = TestTreeNode::new(vec![], "h".to_string());
let node_g = TestTreeNode::new(vec![node_h], "g".to_string());
let node_f = TestTreeNode::new(vec![node_e, node_g], "f".to_string());
let node_i = TestTreeNode::new(vec![node_f], "i".to_string());
TestTreeNode::new(vec![node_i], "j".to_string())
}
fn down_visits(visits: Vec<String>) -> Vec<String> {
visits
.into_iter()
.filter(|v| v.starts_with("f_down"))
.collect()
}
type TestVisitorF<T> = Box<dyn FnMut(&TestTreeNode<T>) -> Result<TreeNodeRecursion>>;
struct TestVisitor<T> {
visits: Vec<String>,
f_down: TestVisitorF<T>,
f_up: TestVisitorF<T>,
}
impl<T> TestVisitor<T> {
fn new(f_down: TestVisitorF<T>, f_up: TestVisitorF<T>) -> Self {
Self {
visits: vec![],
f_down,
f_up,
}
}
}
impl<T: Display> TreeNodeVisitor for TestVisitor<T> {
type Node = TestTreeNode<T>;
fn f_down(&mut self, node: &Self::Node) -> Result<TreeNodeRecursion> {
self.visits.push(format!("f_down({})", node.data));
(*self.f_down)(node)
}
fn f_up(&mut self, node: &Self::Node) -> Result<TreeNodeRecursion> {
self.visits.push(format!("f_up({})", node.data));
(*self.f_up)(node)
}
}
fn visit_continue<T>(_: &TestTreeNode<T>) -> Result<TreeNodeRecursion> {
Ok(TreeNodeRecursion::Continue)
}
fn visit_event_on<T: PartialEq, D: Into<T>>(
data: D,
event: TreeNodeRecursion,
) -> impl FnMut(&TestTreeNode<T>) -> Result<TreeNodeRecursion> {
let d = data.into();
move |node| {
Ok(if node.data == d {
event
} else {
TreeNodeRecursion::Continue
})
}
}
macro_rules! visit_test {
($NAME:ident, $F_DOWN:expr, $F_UP:expr, $EXPECTED_VISITS:expr) => {
#[test]
fn $NAME() -> Result<()> {
let tree = test_tree();
let mut visitor = TestVisitor::new(Box::new($F_DOWN), Box::new($F_UP));
tree.visit(&mut visitor)?;
assert_eq!(visitor.visits, $EXPECTED_VISITS);
Ok(())
}
};
}
macro_rules! test_apply {
($NAME:ident, $F:expr, $EXPECTED_VISITS:expr) => {
#[test]
fn $NAME() -> Result<()> {
let tree = test_tree();
let mut visits = vec![];
tree.apply(|node| {
visits.push(format!("f_down({})", node.data));
$F(node)
})?;
assert_eq!(visits, $EXPECTED_VISITS);
Ok(())
}
};
}
type TestRewriterF<T> =
Box<dyn FnMut(TestTreeNode<T>) -> Result<Transformed<TestTreeNode<T>>>>;
struct TestRewriter<T> {
f_down: TestRewriterF<T>,
f_up: TestRewriterF<T>,
}
impl<T> TestRewriter<T> {
fn new(f_down: TestRewriterF<T>, f_up: TestRewriterF<T>) -> Self {
Self { f_down, f_up }
}
}
impl<T: Display> TreeNodeRewriter for TestRewriter<T> {
type Node = TestTreeNode<T>;
fn f_down(&mut self, node: Self::Node) -> Result<Transformed<Self::Node>> {
(*self.f_down)(node)
}
fn f_up(&mut self, node: Self::Node) -> Result<Transformed<Self::Node>> {
(*self.f_up)(node)
}
}
fn transform_yes<N: Display, T: Display + From<String>>(
transformation_name: N,
) -> impl FnMut(TestTreeNode<T>) -> Result<Transformed<TestTreeNode<T>>> {
move |node| {
Ok(Transformed::yes(TestTreeNode::new(
node.children,
format!("{}({})", transformation_name, node.data).into(),
)))
}
}
fn transform_and_event_on<
N: Display,
T: PartialEq + Display + From<String>,
D: Into<T>,
>(
transformation_name: N,
data: D,
event: TreeNodeRecursion,
) -> impl FnMut(TestTreeNode<T>) -> Result<Transformed<TestTreeNode<T>>> {
let d = data.into();
move |node| {
let new_node = TestTreeNode::new(
node.children,
format!("{}({})", transformation_name, node.data).into(),
);
Ok(if node.data == d {
Transformed::new(new_node, true, event)
} else {
Transformed::yes(new_node)
})
}
}
macro_rules! rewrite_test {
($NAME:ident, $F_DOWN:expr, $F_UP:expr, $EXPECTED_TREE:expr) => {
#[test]
fn $NAME() -> Result<()> {
let tree = test_tree();
let mut rewriter = TestRewriter::new(Box::new($F_DOWN), Box::new($F_UP));
assert_eq!(tree.rewrite(&mut rewriter)?, $EXPECTED_TREE);
Ok(())
}
};
}
macro_rules! transform_test {
($NAME:ident, $F_DOWN:expr, $F_UP:expr, $EXPECTED_TREE:expr) => {
#[test]
fn $NAME() -> Result<()> {
let tree = test_tree();
assert_eq!(tree.transform_down_up($F_DOWN, $F_UP,)?, $EXPECTED_TREE);
Ok(())
}
};
}
macro_rules! transform_down_test {
($NAME:ident, $F:expr, $EXPECTED_TREE:expr) => {
#[test]
fn $NAME() -> Result<()> {
let tree = test_tree();
assert_eq!(tree.transform_down($F)?, $EXPECTED_TREE);
Ok(())
}
};
}
macro_rules! transform_up_test {
($NAME:ident, $F:expr, $EXPECTED_TREE:expr) => {
#[test]
fn $NAME() -> Result<()> {
let tree = test_tree();
assert_eq!(tree.transform_up($F)?, $EXPECTED_TREE);
Ok(())
}
};
}
visit_test!(test_visit, visit_continue, visit_continue, all_visits());
visit_test!(
test_visit_f_down_jump_on_a,
visit_event_on("a", TreeNodeRecursion::Jump),
visit_continue,
f_down_jump_on_a_visits()
);
visit_test!(
test_visit_f_down_jump_on_e,
visit_event_on("e", TreeNodeRecursion::Jump),
visit_continue,
f_down_jump_on_e_visits()
);
visit_test!(
test_visit_f_up_jump_on_a,
visit_continue,
visit_event_on("a", TreeNodeRecursion::Jump),
f_up_jump_on_a_visits()
);
visit_test!(
test_visit_f_up_jump_on_e,
visit_continue,
visit_event_on("e", TreeNodeRecursion::Jump),
f_up_jump_on_e_visits()
);
visit_test!(
test_visit_f_down_stop_on_a,
visit_event_on("a", TreeNodeRecursion::Stop),
visit_continue,
f_down_stop_on_a_visits()
);
visit_test!(
test_visit_f_down_stop_on_e,
visit_event_on("e", TreeNodeRecursion::Stop),
visit_continue,
f_down_stop_on_e_visits()
);
visit_test!(
test_visit_f_up_stop_on_a,
visit_continue,
visit_event_on("a", TreeNodeRecursion::Stop),
f_up_stop_on_a_visits()
);
visit_test!(
test_visit_f_up_stop_on_e,
visit_continue,
visit_event_on("e", TreeNodeRecursion::Stop),
f_up_stop_on_e_visits()
);
test_apply!(test_apply, visit_continue, down_visits(all_visits()));
test_apply!(
test_apply_f_down_jump_on_a,
visit_event_on("a", TreeNodeRecursion::Jump),
down_visits(f_down_jump_on_a_visits())
);
test_apply!(
test_apply_f_down_jump_on_e,
visit_event_on("e", TreeNodeRecursion::Jump),
down_visits(f_down_jump_on_e_visits())
);
test_apply!(
test_apply_f_down_stop_on_a,
visit_event_on("a", TreeNodeRecursion::Stop),
down_visits(f_down_stop_on_a_visits())
);
test_apply!(
test_apply_f_down_stop_on_e,
visit_event_on("e", TreeNodeRecursion::Stop),
down_visits(f_down_stop_on_e_visits())
);
rewrite_test!(
test_rewrite,
transform_yes("f_down"),
transform_yes("f_up"),
Transformed::yes(transformed_tree())
);
rewrite_test!(
test_rewrite_f_down_jump_on_a,
transform_and_event_on("f_down", "a", TreeNodeRecursion::Jump),
transform_yes("f_up"),
Transformed::yes(transformed_tree())
);
rewrite_test!(
test_rewrite_f_down_jump_on_e,
transform_and_event_on("f_down", "e", TreeNodeRecursion::Jump),
transform_yes("f_up"),
Transformed::yes(f_down_jump_on_e_transformed_tree())
);
rewrite_test!(
test_rewrite_f_up_jump_on_a,
transform_yes("f_down"),
transform_and_event_on("f_up", "f_down(a)", TreeNodeRecursion::Jump),
Transformed::yes(f_up_jump_on_a_transformed_tree())
);
rewrite_test!(
test_rewrite_f_up_jump_on_e,
transform_yes("f_down"),
transform_and_event_on("f_up", "f_down(e)", TreeNodeRecursion::Jump),
Transformed::yes(f_up_jump_on_e_transformed_tree())
);
rewrite_test!(
test_rewrite_f_down_stop_on_a,
transform_and_event_on("f_down", "a", TreeNodeRecursion::Stop),
transform_yes("f_up"),
Transformed::new(
f_down_stop_on_a_transformed_tree(),
true,
TreeNodeRecursion::Stop
)
);
rewrite_test!(
test_rewrite_f_down_stop_on_e,
transform_and_event_on("f_down", "e", TreeNodeRecursion::Stop),
transform_yes("f_up"),
Transformed::new(
f_down_stop_on_e_transformed_tree(),
true,
TreeNodeRecursion::Stop
)
);
rewrite_test!(
test_rewrite_f_up_stop_on_a,
transform_yes("f_down"),
transform_and_event_on("f_up", "f_down(a)", TreeNodeRecursion::Stop),
Transformed::new(
f_up_stop_on_a_transformed_tree(),
true,
TreeNodeRecursion::Stop
)
);
rewrite_test!(
test_rewrite_f_up_stop_on_e,
transform_yes("f_down"),
transform_and_event_on("f_up", "f_down(e)", TreeNodeRecursion::Stop),
Transformed::new(
f_up_stop_on_e_transformed_tree(),
true,
TreeNodeRecursion::Stop
)
);
transform_test!(
test_transform,
transform_yes("f_down"),
transform_yes("f_up"),
Transformed::yes(transformed_tree())
);
transform_test!(
test_transform_f_down_jump_on_a,
transform_and_event_on("f_down", "a", TreeNodeRecursion::Jump),
transform_yes("f_up"),
Transformed::yes(transformed_tree())
);
transform_test!(
test_transform_f_down_jump_on_e,
transform_and_event_on("f_down", "e", TreeNodeRecursion::Jump),
transform_yes("f_up"),
Transformed::yes(f_down_jump_on_e_transformed_tree())
);
transform_test!(
test_transform_f_up_jump_on_a,
transform_yes("f_down"),
transform_and_event_on("f_up", "f_down(a)", TreeNodeRecursion::Jump),
Transformed::yes(f_up_jump_on_a_transformed_tree())
);
transform_test!(
test_transform_f_up_jump_on_e,
transform_yes("f_down"),
transform_and_event_on("f_up", "f_down(e)", TreeNodeRecursion::Jump),
Transformed::yes(f_up_jump_on_e_transformed_tree())
);
transform_test!(
test_transform_f_down_stop_on_a,
transform_and_event_on("f_down", "a", TreeNodeRecursion::Stop),
transform_yes("f_up"),
Transformed::new(
f_down_stop_on_a_transformed_tree(),
true,
TreeNodeRecursion::Stop
)
);
transform_test!(
test_transform_f_down_stop_on_e,
transform_and_event_on("f_down", "e", TreeNodeRecursion::Stop),
transform_yes("f_up"),
Transformed::new(
f_down_stop_on_e_transformed_tree(),
true,
TreeNodeRecursion::Stop
)
);
transform_test!(
test_transform_f_up_stop_on_a,
transform_yes("f_down"),
transform_and_event_on("f_up", "f_down(a)", TreeNodeRecursion::Stop),
Transformed::new(
f_up_stop_on_a_transformed_tree(),
true,
TreeNodeRecursion::Stop
)
);
transform_test!(
test_transform_f_up_stop_on_e,
transform_yes("f_down"),
transform_and_event_on("f_up", "f_down(e)", TreeNodeRecursion::Stop),
Transformed::new(
f_up_stop_on_e_transformed_tree(),
true,
TreeNodeRecursion::Stop
)
);
transform_down_test!(
test_transform_down,
transform_yes("f_down"),
Transformed::yes(transformed_down_tree())
);
transform_down_test!(
test_transform_down_f_down_jump_on_a,
transform_and_event_on("f_down", "a", TreeNodeRecursion::Jump),
Transformed::yes(f_down_jump_on_a_transformed_down_tree())
);
transform_down_test!(
test_transform_down_f_down_jump_on_e,
transform_and_event_on("f_down", "e", TreeNodeRecursion::Jump),
Transformed::yes(f_down_jump_on_e_transformed_down_tree())
);
transform_down_test!(
test_transform_down_f_down_stop_on_a,
transform_and_event_on("f_down", "a", TreeNodeRecursion::Stop),
Transformed::new(
f_down_stop_on_a_transformed_down_tree(),
true,
TreeNodeRecursion::Stop
)
);
transform_down_test!(
test_transform_down_f_down_stop_on_e,
transform_and_event_on("f_down", "e", TreeNodeRecursion::Stop),
Transformed::new(
f_down_stop_on_e_transformed_down_tree(),
true,
TreeNodeRecursion::Stop
)
);
transform_up_test!(
test_transform_up,
transform_yes("f_up"),
Transformed::yes(transformed_up_tree())
);
transform_up_test!(
test_transform_up_f_up_jump_on_a,
transform_and_event_on("f_up", "a", TreeNodeRecursion::Jump),
Transformed::yes(f_up_jump_on_a_transformed_up_tree())
);
transform_up_test!(
test_transform_up_f_up_jump_on_e,
transform_and_event_on("f_up", "e", TreeNodeRecursion::Jump),
Transformed::yes(f_up_jump_on_e_transformed_up_tree())
);
transform_up_test!(
test_transform_up_f_up_stop_on_a,
transform_and_event_on("f_up", "a", TreeNodeRecursion::Stop),
Transformed::new(
f_up_stop_on_a_transformed_up_tree(),
true,
TreeNodeRecursion::Stop
)
);
transform_up_test!(
test_transform_up_f_up_stop_on_e,
transform_and_event_on("f_up", "e", TreeNodeRecursion::Stop),
Transformed::new(
f_up_stop_on_e_transformed_up_tree(),
true,
TreeNodeRecursion::Stop
)
);
}