graph_api_lib/walker/steps/probe.rs
1use crate::ElementId;
2use crate::graph::Graph;
3use crate::walker::builder::{EdgeWalkerBuilder, VertexWalkerBuilder};
4use crate::walker::{EdgeWalker, VertexWalker, Walker};
5use include_doc::function_body;
6use std::marker::PhantomData;
7
8// ================ PROBE IMPLEMENTATION ================
9
10pub struct VertexProbe<'graph, Parent, Callback> {
11 _phantom_data: PhantomData<&'graph ()>,
12 parent: Parent,
13 callback: Callback,
14}
15
16impl<Parent, Callback> VertexProbe<'_, Parent, Callback> {
17 pub(crate) fn new(parent: Parent, callback: Callback) -> Self {
18 VertexProbe {
19 _phantom_data: Default::default(),
20 parent,
21 callback,
22 }
23 }
24}
25
26impl<'graph, Parent, Callback> Walker<'graph> for VertexProbe<'graph, Parent, Callback>
27where
28 Parent: VertexWalker<'graph>,
29 Callback: FnMut(&<Parent::Graph as Graph>::VertexReference<'_>, &Parent::Context),
30{
31 type Graph = Parent::Graph;
32 type Context = Parent::Context;
33
34 fn next_element(&mut self, graph: &'graph Self::Graph) -> Option<ElementId<Self::Graph>> {
35 self.next(graph).map(ElementId::Vertex)
36 }
37
38 fn ctx(&self) -> &Self::Context {
39 self.parent.ctx()
40 }
41 fn ctx_mut(&mut self) -> &mut Self::Context {
42 self.parent.ctx_mut()
43 }
44}
45
46impl<'graph, Parent, Callback> VertexWalker<'graph> for VertexProbe<'graph, Parent, Callback>
47where
48 Parent: VertexWalker<'graph>,
49 Callback: FnMut(&<Parent::Graph as Graph>::VertexReference<'_>, &Parent::Context),
50{
51 fn next(&mut self, graph: &'graph Self::Graph) -> Option<<Self::Graph as Graph>::VertexId> {
52 let next = self.parent.next(graph);
53 if let Some(id) = next {
54 if let Some(vertex) = graph.vertex(id) {
55 (self.callback)(&vertex, self.parent.ctx());
56 }
57 }
58 next
59 }
60}
61
62pub struct EdgeProbe<'graph, Parent, Callback> {
63 _phantom_data: PhantomData<&'graph ()>,
64 parent: Parent,
65 callback: Callback,
66}
67
68impl<Parent, Callback> EdgeProbe<'_, Parent, Callback> {
69 pub(crate) fn new(parent: Parent, callback: Callback) -> Self {
70 EdgeProbe {
71 _phantom_data: Default::default(),
72 parent,
73 callback,
74 }
75 }
76}
77
78impl<'graph, Parent, Callback> Walker<'graph> for EdgeProbe<'graph, Parent, Callback>
79where
80 Parent: EdgeWalker<'graph>,
81 Callback: FnMut(&<Parent::Graph as Graph>::EdgeReference<'_>, &Parent::Context),
82{
83 type Graph = Parent::Graph;
84 type Context = Parent::Context;
85
86 fn next_element(&mut self, graph: &'graph Self::Graph) -> Option<ElementId<Self::Graph>> {
87 self.next(graph).map(ElementId::Edge)
88 }
89
90 fn ctx(&self) -> &Self::Context {
91 self.parent.ctx()
92 }
93 fn ctx_mut(&mut self) -> &mut Self::Context {
94 self.parent.ctx_mut()
95 }
96}
97
98impl<'graph, Parent, Callback> EdgeWalker<'graph> for EdgeProbe<'graph, Parent, Callback>
99where
100 Parent: EdgeWalker<'graph>,
101 Callback: FnMut(&<Parent::Graph as Graph>::EdgeReference<'_>, &Parent::Context),
102{
103 fn next(&mut self, graph: &'graph Self::Graph) -> Option<<Self::Graph as Graph>::EdgeId> {
104 let next = self.parent.next(graph);
105 if let Some(next) = next {
106 let edge = graph.edge(next).expect("edge must exist");
107 (self.callback)(&edge, self.parent.ctx());
108 }
109 next
110 }
111}
112
113impl<'graph, Mutability, Graph, Walker> VertexWalkerBuilder<'graph, Mutability, Graph, Walker>
114where
115 Graph: crate::graph::Graph,
116 Walker: VertexWalker<'graph, Graph = Graph>,
117{
118 /// # Probe Step
119 ///
120 /// The `probe` step allows you to execute a callback function for each vertex in the traversal
121 /// without altering the traversal itself. This is useful for debugging, logging, or collecting
122 /// information during a traversal.
123 ///
124 /// ## Visual Diagram
125 ///
126 /// Before probe step:
127 /// ```text
128 /// [A]* --- edge1 ---> [B]* --- edge2 ---> [C]*
129 /// ^
130 /// |
131 /// edge3
132 /// |
133 /// [D]*
134 /// ```
135 ///
136 /// After probe step (unchanged, but callback executed for each vertex *):
137 /// ```text
138 /// [A]* --- edge1 ---> [B]* --- edge2 ---> [C]*
139 /// ^
140 /// |
141 /// edge3
142 /// |
143 /// [D]*
144 /// ```
145 ///
146 /// ## Parameters
147 ///
148 /// - `callback`: A function that takes a reference to the current vertex being traversed,
149 /// and optionally the current context.
150 /// The function signature can be either:
151 /// - `FnMut(&Graph::VertexReference<'_>, &Context)` - Probe with access to current context
152 ///
153 /// ## Return Value
154 ///
155 /// A walker of the same type as the input with the probe operation added to the pipeline,
156 /// allowing for further chaining of operations.
157 ///
158 /// ## Example
159 ///
160 /// ```rust
161 #[doc = function_body!("examples/probe.rs", vertex_example, [])]
162 /// ```
163 ///
164 /// ## Notes
165 ///
166 /// - The `probe` step does not modify the traversal path or elements
167 /// - The callback function is executed for each vertex as it's traversed
168 /// - When using the context variant, you can access traversal context data during probing
169 /// - Useful for debugging, logging, or gathering statistics about your graph
170 /// - Side effects in the callback function (like printing or counting) do not affect the traversal
171 /// - Can be used at multiple points in a traversal to monitor the flow at different stages
172 /// - Consider using pattern matching in the callback to work with specific vertex types
173 /// - Context access is especially useful when combined with `push_context` steps earlier in the traversal
174 pub fn probe<Callback>(
175 self,
176 callback: Callback,
177 ) -> VertexWalkerBuilder<'graph, Mutability, Graph, VertexProbe<'graph, Walker, Callback>>
178 where
179 Callback: FnMut(&Graph::VertexReference<'_>, &Walker::Context),
180 {
181 self.with_vertex_walker(|walker| VertexProbe::new(walker, callback))
182 }
183}
184
185impl<'graph, Mutability, Graph, Walker> EdgeWalkerBuilder<'graph, Mutability, Graph, Walker>
186where
187 Graph: crate::graph::Graph,
188 Walker: EdgeWalker<'graph, Graph = Graph>,
189{
190 /// # Probe Step
191 ///
192 /// The `probe` step allows you to execute a callback function for each edge in the traversal
193 /// without altering the traversal itself. This is useful for debugging, analyzing connections,
194 /// or collecting edge statistics during a traversal.
195 ///
196 /// ## Visual Diagram
197 ///
198 /// Before probe step:
199 /// ```text
200 /// [Person A] --- knows* ---> [Person B] --- created* ---> [Project]
201 /// ^
202 /// |
203 /// owns*
204 /// |
205 /// [Company]
206 /// ```
207 ///
208 /// After probe step (unchanged, but callback executed for each edge *):
209 /// ```text
210 /// [Person A] --- knows* ---> [Person B] --- created* ---> [Project]
211 /// ^
212 /// |
213 /// owns*
214 /// |
215 /// [Company]
216 /// ```
217 ///
218 /// ## Parameters
219 ///
220 /// - `callback`: A function that takes a reference to the current edge being traversed,
221 /// and optionally the current context.
222 /// The function signature can be either:
223 /// - `FnMut(&Graph::EdgeReference<'_>, &Context)` - Probe with access to current context
224 ///
225 /// ## Return Value
226 ///
227 /// A walker of the same type as the input with the probe operation added to the pipeline,
228 /// allowing for further chaining of operations.
229 ///
230 /// ## Example
231 ///
232 /// ```rust
233 #[doc = function_body!("examples/probe.rs", edge_example, [])]
234 /// ```
235 ///
236 /// ## Notes
237 ///
238 /// - The `probe` step does not modify the traversal path or edges
239 /// - The callback function is executed for each edge as it's traversed
240 /// - When using the context variant, you can access traversal context data during probing
241 /// - Useful for analyzing connection patterns without modifying the traversal
242 /// - Consider using pattern matching in the callback to handle different edge types
243 /// - You can use endpoint accessors like `tail()` and `head()` to inspect connected vertices
244 /// - Context access is especially useful when combined with `push_context` steps earlier in the traversal
245 pub fn probe<Callback>(
246 self,
247 callback: Callback,
248 ) -> EdgeWalkerBuilder<'graph, Mutability, Graph, EdgeProbe<'graph, Walker, Callback>>
249 where
250 Callback: FnMut(&Graph::EdgeReference<'_>, &Walker::Context),
251 {
252 self.with_edge_walker(|walker| EdgeProbe::new(walker, callback))
253 }
254}