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//! ## syn
23//! The crate includes a replacement for `syn::visit::Visit` if the `syn` feature is enabled. Implement `directed_visit::syn::visit::Full` as you would `syn::visit::Visit`.
24//! 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`.
25//! 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.
26//! The `derive` feature subset of `full` is not yet supported.
27//!
28//! ## Limitations
29//! 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). For this reason there is also currently no `VisitMut` equivalent, because the ideal interaction between handling temporary dynamic nodes and mutating them is unclear.
30#![warn(missing_docs)]
31
32mod direct;
33pub use direct::{Direct, Director};
34/// Direct and Visit implementations for the syn AST
35#[cfg(feature = "syn")]
36pub mod syn;
37mod visit;
38pub use visit::{Visit, Visitor};
39
40#[derive(Debug)]
41pub(crate) struct DirectorVisitor<'d, A: ?Sized, V: ?Sized> {
42 pub(crate) direct: &'d mut A,
43 pub(crate) visit: &'d mut V,
44}
45
46impl<A: ?Sized, V: ?Sized> DirectorVisitor<'_, A, V> {
47 pub(crate) fn reborrow(&mut self) -> DirectorVisitor<'_, A, V> {
48 let Self { direct, visit } = self;
49
50 DirectorVisitor { direct, visit }
51 }
52}
53
54/// Perform a visit using a Direct-Visit pair, and a given input.
55pub fn visit<'dv, D, V, N>(direct: &'dv mut D, visit: &'dv mut V, node: &N)
56where
57 D: Direct<V, N> + ?Sized,
58 V: Visit<N> + ?Sized,
59 N: ?Sized,
60{
61 V::visit(Visitor::new(DirectorVisitor { direct, visit }, node), node);
62}
63
64#[cfg(test)]
65mod tests {
66 use super::*;
67
68 struct A(Option<B>);
69 struct B(Vec<C>);
70 struct C(A);
71
72 struct MyDirect;
73 impl crate::Direct<MyVisit, A> for MyDirect {
74 fn direct<'dv>(mut director: Director<'dv, Self, MyVisit>, node: &A) {
75 if let Some(b) = &node.0 {
76 Director::direct(&mut director, b);
77 }
78 }
79 }
80
81 impl crate::Direct<MyVisit, B> for MyDirect {
82 fn direct<'dv>(mut director: Director<'dv, Self, MyVisit>, node: &B) {
83 for c in &node.0 {
84 Director::direct(&mut director, c);
85 }
86 }
87 }
88
89 impl crate::Direct<MyVisit, C> for MyDirect {
90 fn direct<'dv>(mut director: Director<'dv, Self, MyVisit>, node: &C) {
91 Director::direct(&mut director, &node.0)
92 }
93 }
94
95 struct MyVisit(usize);
96 impl crate::Visit<A> for MyVisit {
97 fn visit<'dv, 'n, D: ?Sized>(mut visitor: Visitor<'dv, 'n, D, Self, A>, _node: &'n A)
98 where
99 D: Direct<Self, A>,
100 {
101 visitor.0 += 1;
102 Visitor::visit(visitor);
103 }
104 }
105 impl crate::Visit<B> for MyVisit {
106 fn visit<'dv, 'n, D: ?Sized>(mut visitor: Visitor<'dv, 'n, D, Self, B>, _node: &'n B)
107 where
108 D: Direct<Self, B>,
109 {
110 visitor.0 += 2;
111 Visitor::visit(visitor);
112 }
113 }
114 impl crate::Visit<C> for MyVisit {
115 fn visit<'dv, 'n, D: ?Sized>(mut visitor: Visitor<'dv, 'n, D, Self, C>, _node: &'n C)
116 where
117 D: Direct<Self, C>,
118 {
119 visitor.0 += 3;
120 Visitor::visit(visitor);
121 }
122 }
123
124 #[test]
125 fn custom_node_set() {
126 let input = A(Some(
127 // 1
128 B(vec![
129 // 3
130 C(
131 // 6
132 A(None),
133 ), // 7
134 C(
135 // 10
136 A(Some(
137 // 11
138 B(vec![]), // 13
139 )),
140 ),
141 ]),
142 ));
143
144 let mut direct = MyDirect;
145 let mut visit = MyVisit(0);
146 crate::visit(&mut direct, &mut visit, &input);
147
148 assert_eq!(visit.0, 13);
149 }
150}