directed_visit/
lib.rs

1//! Implement the visitor pattern with interchangable implementations for both the visit algorithm and node object traversal. It is also possible to dynamically create temporary 'virtual' nodes during traversal.  
2//!
3//! An object traversal is comprised of 3 parts:  
4//! The input to traverse, which can start at any node type. This can be an external type too, there are no required derives or trait impls.  
5//! The director navigates between node objects. It must implement `Direct<'_, N>` for each node type `N` in the object graph, determining the sub-nodes.  
6//! The visitor performs the desired algorithm, implementing `Visit<N>` for each node type `N` in the object graph.  
7//!
8//! ```rust,ignore
9//! fn my_visit(input: &MyTree) -> usize {
10//!     let mut my_director = MyDirector::new();
11//!     let mut my_visitor = MyVisitor::new();
12//!     directed_visit::visit(
13//!         &mut my_director,
14//!         &mut my_visitor,
15//!         input,
16//!     );
17//!
18//!     my_visitor.result_value()
19//! }
20//! ```
21//!
22//! `DirectMut` and `VisitMut` traits are also provided. Note that if your `DirectMut` implementation dynamically creates temporary nodes, you must convert those temporary nodes back to their original form after visiting the node, or the changes will be lost.
23//!
24//! ## syn
25//! The crate includes a replacement for `syn::visit::Visit`/`syn::visit_mut::VisitMut` if the `syn` feature is enabled. Implement `directed_visit::syn::visit::Full` as you would `syn::visit::Visit`.  
26//! For your director, `directed_visit::syn::direct::FullDefault` traverses as `syn::visit` does, or you can customize the behavior by implementing `directed_visit::syn::direct::Full`.  
27//! In addition to the existing syn AST, two nodes have been added to the tree to represent when generic parameters become in and out of scope.  
28//! The `derive` feature subset of `full` is not yet supported.
29//!
30//! ## Limitations
31//! Because the director can dynamically create new nodes to visit, the visitor cannot hold references to the node graph (i.e. there is no single `'ast` lifetime for all nodes).
32#![warn(missing_docs)]
33
34mod direct;
35pub use direct::{Direct, DirectMut, Director};
36/// Direct and Visit implementations for the syn AST
37#[cfg(feature = "syn")]
38pub mod syn;
39mod visit;
40pub use visit::{Visit, VisitMut, Visitor};
41
42#[derive(Debug)]
43pub(crate) struct DirectorVisitor<'d, A: ?Sized, V: ?Sized> {
44    pub(crate) direct: &'d mut A,
45    pub(crate) visit: &'d mut V,
46}
47
48impl<A: ?Sized, V: ?Sized> DirectorVisitor<'_, A, V> {
49    pub(crate) fn reborrow(&mut self) -> DirectorVisitor<'_, A, V> {
50        let Self { direct, visit } = self;
51
52        DirectorVisitor { direct, visit }
53    }
54}
55
56/// Perform a visit using a [Direct]-[Visit] pair, and a given input.
57pub fn visit<'dv, D, V, N>(direct: &'dv mut D, visit: &'dv mut V, node: &N)
58where
59    D: Direct<V, N> + ?Sized,
60    V: Visit<N> + ?Sized,
61    N: ?Sized,
62{
63    V::visit(Visitor::new(DirectorVisitor { direct, visit }), node);
64}
65
66/// Perform a visit using a [DirectMut]-[VisitMut] pair, and a given input.
67pub fn visit_mut<'dv, D, V, N>(direct: &'dv mut D, visit: &'dv mut V, node: &mut N)
68where
69    D: DirectMut<V, N> + ?Sized,
70    V: VisitMut<N> + ?Sized,
71    N: ?Sized,
72{
73    V::visit_mut(Visitor::new(DirectorVisitor { direct, visit }), node);
74}
75
76#[cfg(test)]
77mod tests {
78    use super::*;
79
80    struct A(Option<B>);
81    struct B(Vec<C>);
82    struct C(A);
83
84    struct MyDirect;
85    impl crate::Direct<MyVisit, A> for MyDirect {
86        fn direct<'dv>(mut director: Director<'dv, Self, MyVisit>, node: &A) {
87            if let Some(b) = &node.0 {
88                Director::direct(&mut director, b);
89            }
90        }
91    }
92
93    impl crate::Direct<MyVisit, B> for MyDirect {
94        fn direct<'dv>(mut director: Director<'dv, Self, MyVisit>, node: &B) {
95            for c in &node.0 {
96                Director::direct(&mut director, c);
97            }
98        }
99    }
100
101    impl crate::Direct<MyVisit, C> for MyDirect {
102        fn direct<'dv>(mut director: Director<'dv, Self, MyVisit>, node: &C) {
103            Director::direct(&mut director, &node.0)
104        }
105    }
106
107    struct MyVisit(usize);
108    impl crate::Visit<A> for MyVisit {
109        fn visit<'dv, D: ?Sized>(mut visitor: Visitor<'dv, D, Self>, node: &A)
110        where
111            D: Direct<Self, A>,
112        {
113            visitor.0 += 1;
114            Visitor::visit(visitor, node);
115        }
116    }
117    impl crate::Visit<B> for MyVisit {
118        fn visit<'dv, D: ?Sized>(mut visitor: Visitor<'dv, D, Self>, node: &B)
119        where
120            D: Direct<Self, B>,
121        {
122            visitor.0 += 2;
123            Visitor::visit(visitor, node);
124        }
125    }
126    impl crate::Visit<C> for MyVisit {
127        fn visit<'dv, D: ?Sized>(mut visitor: Visitor<'dv, D, Self>, node: &C)
128        where
129            D: Direct<Self, C>,
130        {
131            visitor.0 += 3;
132            Visitor::visit(visitor, node);
133        }
134    }
135
136    #[test]
137    fn custom_node_set() {
138        let input = A(Some(
139            // 1
140            B(vec![
141                // 3
142                C(
143                    // 6
144                    A(None),
145                ), // 7
146                C(
147                    // 10
148                    A(Some(
149                        // 11
150                        B(vec![]), // 13
151                    )),
152                ),
153            ]),
154        ));
155
156        let mut direct = MyDirect;
157        let mut visit = MyVisit(0);
158        crate::visit(&mut direct, &mut visit, &input);
159
160        assert_eq!(visit.0, 13);
161    }
162}