|
20 | 20 |
|
21 | 21 | use std::sync::Arc;
|
22 | 22 |
|
23 |
| -use crate::Result; |
| 23 | +use crate::{error::_not_impl_err, Result}; |
24 | 24 |
|
25 | 25 | /// This macro is used to control continuation behaviors during tree traversals
|
26 | 26 | /// based on the specified direction. Depending on `$DIRECTION` and the value of
|
@@ -174,6 +174,66 @@ pub trait TreeNode: Sized {
|
174 | 174 | })
|
175 | 175 | }
|
176 | 176 |
|
| 177 | + /// Implements the [visitor pattern](https://en.wikipedia.org/wiki/Visitor_pattern) for |
| 178 | + /// recursively mutating / rewriting [`TreeNode`]s in place |
| 179 | + /// |
| 180 | + /// Consider the following tree structure: |
| 181 | + /// ```text |
| 182 | + /// ParentNode |
| 183 | + /// left: ChildNode1 |
| 184 | + /// right: ChildNode2 |
| 185 | + /// ``` |
| 186 | + /// |
| 187 | + /// Here, the nodes would be mutated using the following order: |
| 188 | + /// ```text |
| 189 | + /// TreeNodeMutator::f_down(ParentNode) |
| 190 | + /// TreeNodeMutator::f_down(ChildNode1) |
| 191 | + /// TreeNodeMutator::f_up(ChildNode1) |
| 192 | + /// TreeNodeMutator::f_down(ChildNode2) |
| 193 | + /// TreeNodeMutator::f_up(ChildNode2) |
| 194 | + /// TreeNodeMutator::f_up(ParentNode) |
| 195 | + /// ``` |
| 196 | + /// |
| 197 | + /// See [`TreeNodeRecursion`] for more details on controlling the traversal. |
| 198 | + /// |
| 199 | + /// # Error Handling |
| 200 | + /// |
| 201 | + /// If [`TreeNodeVisitor::f_down()`] or [`TreeNodeVisitor::f_up()`] returns [`Err`], |
| 202 | + /// the recursion stops immediately and the tree may be left partially changed |
| 203 | + /// |
| 204 | + /// # Changing Children During Traversal |
| 205 | + /// |
| 206 | + /// If `f_down` changes the nodes children, the new children are visited |
| 207 | + /// (not the old children prior to rewrite) |
| 208 | + fn mutate<M: TreeNodeMutator<Node = Self>>( |
| 209 | + &mut self, |
| 210 | + mutator: &mut M, |
| 211 | + ) -> Result<Transformed<()>> { |
| 212 | + // Note this is an inlined version of handle_transform_recursion! |
| 213 | + let pre_visited = mutator.f_down(self)?; |
| 214 | + |
| 215 | + // Traverse children and then call f_up on self if necessary |
| 216 | + match pre_visited.tnr { |
| 217 | + TreeNodeRecursion::Continue => { |
| 218 | + // rewrite children recursively with mutator |
| 219 | + self.mutate_children(|c| c.mutate(mutator))? |
| 220 | + .try_transform_node_with( |
| 221 | + |_: ()| mutator.f_up(self), |
| 222 | + TreeNodeRecursion::Jump, |
| 223 | + ) |
| 224 | + } |
| 225 | + TreeNodeRecursion::Jump => { |
| 226 | + // skip other children and start back up |
| 227 | + mutator.f_up(self) |
| 228 | + } |
| 229 | + TreeNodeRecursion::Stop => return Ok(pre_visited), |
| 230 | + } |
| 231 | + .map(|mut post_visited| { |
| 232 | + post_visited.transformed |= pre_visited.transformed; |
| 233 | + post_visited |
| 234 | + }) |
| 235 | + } |
| 236 | + |
177 | 237 | /// Applies `f` to the node and its children. `f` is applied in a pre-order
|
178 | 238 | /// way, and it is controlled by [`TreeNodeRecursion`], which means result
|
179 | 239 | /// of the `f` on a node can cause an early return.
|
@@ -353,13 +413,34 @@ pub trait TreeNode: Sized {
|
353 | 413 | }
|
354 | 414 |
|
355 | 415 | /// Apply the closure `F` to the node's children.
|
| 416 | + /// |
| 417 | + /// See `mutate_children` for rewriting in place |
356 | 418 | fn apply_children<F: FnMut(&Self) -> Result<TreeNodeRecursion>>(
|
357 | 419 | &self,
|
358 | 420 | f: &mut F,
|
359 | 421 | ) -> Result<TreeNodeRecursion>;
|
360 | 422 |
|
361 |
| - /// Apply transform `F` to the node's children. Note that the transform `F` |
362 |
| - /// might have a direction (pre-order or post-order). |
| 423 | + /// Rewrite the node's children in place using `F`. |
| 424 | + /// |
| 425 | + /// Using [`Self::map_children`], the owned API, is more ideomatic and |
| 426 | + /// has clearer semantics on error (the node is consumed). However, it requires |
| 427 | + /// copying the interior fields of the tree node during rewrite |
| 428 | + /// |
| 429 | + /// This API writes the nodes in place, which can be faster as it avoids |
| 430 | + /// copying. However, one downside is that the tree node can be left in an |
| 431 | + /// partially rewritten state when an error occurs. |
| 432 | + fn mutate_children<F: FnMut(&mut Self) -> Result<Transformed<()>>>( |
| 433 | + &mut self, |
| 434 | + _f: F, |
| 435 | + ) -> Result<Transformed<()>> { |
| 436 | + _not_impl_err!( |
| 437 | + "mutate_children not implemented for {} yet", |
| 438 | + std::any::type_name::<Self>() |
| 439 | + ) |
| 440 | + } |
| 441 | + |
| 442 | + /// Apply transform `F` to potentially rewrite the node's children. Note |
| 443 | + /// that the transform `F` might have a direction (pre-order or post-order). |
363 | 444 | fn map_children<F: FnMut(Self) -> Result<Transformed<Self>>>(
|
364 | 445 | self,
|
365 | 446 | f: F,
|
@@ -411,6 +492,36 @@ pub trait TreeNodeRewriter: Sized {
|
411 | 492 | }
|
412 | 493 | }
|
413 | 494 |
|
| 495 | +/// Trait for potentially rewriting tree of [`TreeNode`]s in place |
| 496 | +/// |
| 497 | +/// See [`TreeNodeRewriter`] for rewriting owned tree ndoes |
| 498 | +/// See [`TreeNodeVisitor`] for visiting, but not changing, tree nodes |
| 499 | +pub trait TreeNodeMutator: Sized { |
| 500 | + /// The node type to rewrite. |
| 501 | + type Node: TreeNode; |
| 502 | + |
| 503 | + /// Invoked while traversing down the tree before any children are rewritten. |
| 504 | + /// Default implementation returns the node as is and continues recursion. |
| 505 | + /// |
| 506 | + /// Since this mutates the nodes in place, the returned Transformed object |
| 507 | + /// returns `()` (no data). |
| 508 | + /// |
| 509 | + /// If the node's children are changed by `f_down`, the *new* children are |
| 510 | + /// visited, not the original. |
| 511 | + fn f_down(&mut self, _node: &mut Self::Node) -> Result<Transformed<()>> { |
| 512 | + Ok(Transformed::no(())) |
| 513 | + } |
| 514 | + |
| 515 | + /// Invoked while traversing up the tree after all children have been rewritten. |
| 516 | + /// Default implementation returns the node as is and continues recursion. |
| 517 | + /// |
| 518 | + /// Since this mutates the nodes in place, the returned Transformed object |
| 519 | + /// returns `()` (no data). |
| 520 | + fn f_up(&mut self, _node: &mut Self::Node) -> Result<Transformed<()>> { |
| 521 | + Ok(Transformed::no(())) |
| 522 | + } |
| 523 | +} |
| 524 | + |
414 | 525 | /// Controls how [`TreeNode`] recursions should proceed.
|
415 | 526 | #[derive(Debug, PartialEq, Clone, Copy)]
|
416 | 527 | pub enum TreeNodeRecursion {
|
@@ -489,6 +600,11 @@ impl<T> Transformed<T> {
|
489 | 600 | f(self.data).map(|data| Transformed::new(data, self.transformed, self.tnr))
|
490 | 601 | }
|
491 | 602 |
|
| 603 | + /// Invokes f(), depending on the value of self.tnr. |
| 604 | + /// |
| 605 | + /// This is used to conditionally apply a function during a f_up tree |
| 606 | + /// traversal, if the result of children traversal was `Continue`. |
| 607 | + /// |
492 | 608 | /// Handling [`TreeNodeRecursion::Continue`] and [`TreeNodeRecursion::Stop`]
|
493 | 609 | /// is straightforward, but [`TreeNodeRecursion::Jump`] can behave differently
|
494 | 610 | /// when we are traversing down or up on a tree. If [`TreeNodeRecursion`] of
|
@@ -532,6 +648,24 @@ impl<T> Transformed<T> {
|
532 | 648 | }
|
533 | 649 | }
|
534 | 650 |
|
| 651 | +impl Transformed<()> { |
| 652 | + /// Invoke the given function `f` and combine the transformed state with |
| 653 | + /// the current state, |
| 654 | + /// |
| 655 | + /// if f() returns an Err, returns that err |
| 656 | + /// If f() returns Ok, returns a true transformed flag if either self or |
| 657 | + /// the result of f() was transformed |
| 658 | + pub fn and_then<F>(self, f: F) -> Result<Transformed<()>> |
| 659 | + where |
| 660 | + F: FnOnce() -> Result<Transformed<()>>, |
| 661 | + { |
| 662 | + f().map(|mut t| { |
| 663 | + t.transformed |= self.transformed; |
| 664 | + t |
| 665 | + }) |
| 666 | + } |
| 667 | +} |
| 668 | + |
535 | 669 | /// Transformation helper to process tree nodes that are siblings.
|
536 | 670 | pub trait TransformedIterator: Iterator {
|
537 | 671 | fn map_until_stop_and_collect<
|
|
0 commit comments