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}