graph_api_lib/walker/steps/detour.rs
1use crate::ElementId;
2use crate::graph::Graph;
3use crate::walker::builder::{GraphAccess, ImmutableMarker, VertexWalkerBuilder, WalkerBuilder};
4use crate::walker::{VertexWalker, Walker};
5use include_doc::function_body;
6use std::cell::Cell;
7use std::marker::PhantomData;
8use std::rc::Rc;
9
10// ================ DETOUR IMPLEMENTATION ================
11
12/// A Waypoint represents a temporary position in a graph traversal.
13///
14/// It acts as a bridge between the main traversal and a detour (sub-traversal),
15/// storing the vertex ID and context needed to continue the main traversal
16/// after the detour completes.
17pub struct Waypoint<'graph, Graph, Context>
18where
19 Graph: crate::graph::Graph,
20 Context: Clone,
21{
22 _phantom: PhantomData<&'graph (Graph, Context)>,
23 // Shared cell containing the next vertex ID to visit
24 // Rc is needed here to share state between the Detour and Waypoint
25 next: Rc<Cell<Option<Graph::VertexId>>>,
26 // Shared cell containing the context from the parent traversal
27 // Rc is needed to share context between the Detour and Waypoint
28 context: Rc<Cell<Option<Context>>>,
29 // The currently active context for this waypoint
30 current_context: Option<Context>,
31}
32
33impl<'graph, Graph, Context> Walker<'graph> for Waypoint<'graph, Graph, Context>
34where
35 Graph: crate::graph::Graph,
36 Context: 'static + Clone,
37{
38 type Graph = Graph;
39 type Context = Context;
40
41 fn next_element(&mut self, graph: &'graph Self::Graph) -> Option<ElementId<Self::Graph>> {
42 self.next(graph).map(ElementId::Vertex)
43 }
44 fn ctx(&self) -> &Self::Context {
45 self.current_context
46 .as_ref()
47 .expect("context must be set before access")
48 }
49
50 fn ctx_mut(&mut self) -> &mut Self::Context {
51 self.current_context
52 .as_mut()
53 .expect("context cannot be retrieved before call to next")
54 }
55}
56
57impl<'graph, Graph, Context> VertexWalker<'graph> for Waypoint<'graph, Graph, Context>
58where
59 Graph: crate::graph::Graph,
60 Context: 'static + Clone,
61{
62 fn next(
63 &mut self,
64 _graph: &Self::Graph,
65 ) -> Option<<Self::Graph as crate::graph::Graph>::VertexId> {
66 // Extract the context and vertex ID from the shared cells
67 self.current_context = self.context.take();
68 self.next.take()
69 }
70}
71
72/// Detour creates a sub-traversal for each element in the main traversal.
73///
74/// It allows exploring connected elements without losing the current position
75/// in the main traversal. For each element in the parent traversal, a new
76/// sub-traversal is created using the provided path function.
77pub struct Detour<'graph, Parent, Path, Terminal>
78where
79 Parent: VertexWalker<'graph>,
80 Terminal: Walker<'graph, Graph = Parent::Graph>,
81{
82 _phantom_data: PhantomData<&'graph Terminal>,
83 // The parent traversal that provides vertices to detour from
84 parent: Parent,
85 // Function that builds the detour path for each vertex
86 path: Path,
87 // The current sub-traversal walker (created on demand)
88 walker: Option<
89 crate::walker::builder::WalkerBuilder<'graph, ImmutableMarker, Parent::Graph, Terminal>,
90 >,
91 // The current vertex ID from the parent traversal
92 next: Option<<Parent::Graph as Graph>::VertexId>,
93 // The context from the terminal (detour) traversal
94 context: Option<Terminal::Context>,
95 // Shared cell for the next vertex ID (shared with Waypoint)
96 waypoint_next: Rc<Cell<Option<<Parent::Graph as Graph>::VertexId>>>,
97 // Shared cell for the context (shared with Waypoint)
98 waypoint_context: Rc<Cell<Option<Parent::Context>>>,
99}
100
101impl<'graph, Parent, Path, Terminal> Detour<'graph, Parent, Path, Terminal>
102where
103 Parent: VertexWalker<'graph>,
104 Terminal: Walker<'graph, Graph = Parent::Graph>,
105{
106 pub(crate) fn new(parent: Parent, path: Path) -> Self {
107 Detour {
108 _phantom_data: Default::default(),
109 parent,
110 path,
111 walker: None,
112 next: None,
113 context: None,
114 waypoint_next: Default::default(),
115 waypoint_context: Default::default(),
116 }
117 }
118}
119
120impl<'graph, Parent, Path, Terminal, WalkerBuilder> Walker<'graph>
121 for Detour<'graph, Parent, Path, Terminal>
122where
123 Parent: VertexWalker<'graph>,
124 Path: Fn(
125 VertexWalkerBuilder<
126 'graph,
127 ImmutableMarker,
128 Parent::Graph,
129 Waypoint<'graph, Parent::Graph, Parent::Context>,
130 >,
131 ) -> WalkerBuilder,
132 WalkerBuilder: Into<
133 crate::walker::builder::WalkerBuilder<'graph, ImmutableMarker, Parent::Graph, Terminal>,
134 >,
135 Terminal: Walker<'graph, Graph = Parent::Graph>,
136 <Parent as Walker<'graph>>::Graph: 'graph,
137{
138 type Graph = Parent::Graph;
139 type Context = Terminal::Context;
140
141 fn next_element(&mut self, graph: &'graph Self::Graph) -> Option<ElementId<Self::Graph>> {
142 self.next(graph).map(ElementId::Vertex)
143 }
144
145 fn ctx(&self) -> &Self::Context {
146 self.context
147 .as_ref()
148 .expect("next must be called before trying to get context")
149 }
150
151 fn ctx_mut(&mut self) -> &mut Self::Context {
152 self.context
153 .as_mut()
154 .expect("context cannot be retrieved before call to next")
155 }
156}
157
158impl<'graph, Parent, Path, Terminal, WalkerBuilder> VertexWalker<'graph>
159 for Detour<'graph, Parent, Path, Terminal>
160where
161 Parent: VertexWalker<'graph>,
162 Path: Fn(
163 VertexWalkerBuilder<
164 'graph,
165 ImmutableMarker,
166 Parent::Graph,
167 Waypoint<'graph, Parent::Graph, Parent::Context>,
168 >,
169 ) -> WalkerBuilder,
170 WalkerBuilder: Into<
171 crate::walker::builder::WalkerBuilder<'graph, ImmutableMarker, Parent::Graph, Terminal>,
172 >,
173 Terminal: Walker<'graph, Graph = Parent::Graph>,
174 <Parent as Walker<'graph>>::Graph: 'graph,
175{
176 fn next(&mut self, graph: &'graph Self::Graph) -> Option<<Self::Graph as Graph>::VertexId> {
177 // Initialize the walker on first use
178 if self.walker.is_none() {
179 // Create a new Waypoint that shares state with this Detour
180 // The waypoint allows the detour traversal to access the current vertex and context
181 self.walker = Some(
182 (self.path)(crate::walker::builder::new(
183 GraphAccess::Immutable(graph),
184 Waypoint {
185 _phantom: Default::default(),
186 next: self.waypoint_next.clone(),
187 context: self.waypoint_context.clone(),
188 current_context: None,
189 },
190 ))
191 .into(),
192 );
193 }
194 let walker = self.walker.as_mut().expect("walker must be set").walker();
195
196 loop {
197 match walker.next_element(graph) {
198 None => {
199 // The detour traversal is exhausted, get the next vertex from parent
200 match self.parent.next(graph) {
201 None => {
202 // No more vertices in parent traversal
203 return None;
204 }
205 Some(next) => {
206 // Found a new vertex from parent, set up for next detour
207 self.next = Some(next);
208 // Share the next vertex ID with the waypoint
209 self.waypoint_next.replace(Some(next));
210 // Share the context with the waypoint
211 self.waypoint_context
212 .replace(Some(self.parent.ctx().clone()));
213 }
214 }
215 }
216 Some(_ctx) => {
217 // The detour found an element, save its context
218 self.context = Some(walker.ctx().clone());
219 // Return the original vertex from parent traversal
220 // (detour only provides context, doesn't change the traversal elements)
221 return self.next;
222 }
223 }
224 }
225 }
226}
227
228impl<'graph, Mutability, Graph, Walker> VertexWalkerBuilder<'graph, Mutability, Graph, Walker>
229where
230 Graph: crate::graph::Graph,
231 Walker: VertexWalker<'graph, Graph = Graph>,
232{
233 /// # Detour Step
234 ///
235 /// The `detour` step allows you to create a sub-traversal for each element in the current traversal.
236 /// It's like a temporary branch in the traversal that returns to the main traversal when complete.
237 /// This is powerful for exploring connected elements without losing your current position.
238 ///
239 /// ## Visual Diagram
240 ///
241 /// Before detour step (traversal position on Person A):
242 /// ```text
243 /// [Person A]* --- knows ---> [Person B] --- created ---> [Project 1]
244 ///
245 /// created
246 ///
247 /// ↓
248 ///
249 /// [Project 2]
250 /// ```
251 ///
252 /// During detour execution (for each element, a sub-traversal is performed):
253 /// ```text
254 /// Main traversal:
255 /// [Person A]* --- knows ---> [Person B]
256 ///
257 /// Sub-traversal from Person A:
258 /// [Person A] --- knows ---> [Person B]
259 ///
260 /// created
261 ///
262 /// ↓
263 ///
264 /// [Project 2]*
265 /// ```
266 ///
267 /// After detour step (traversal position returns to original elements):
268 /// ```text
269 /// [Person A]* --- knows ---> [Person B]
270 ///
271 /// created
272 ///
273 /// ↓
274 ///
275 /// [Project 2]
276 /// ```
277 ///
278 /// ## Parameters
279 ///
280 /// - `traversal_fn`: A function that takes a reference to the current element and returns a new traversal.
281 /// The results of this traversal are collected in the context.
282 ///
283 /// ## Return Value
284 ///
285 /// A walker with the same elements as before, but with the results of the sub-traversals stored in the context.
286 ///
287 /// ## Example
288 ///
289 /// ```rust
290 #[doc = function_body!("examples/detour.rs", example, [])]
291 /// ```
292 ///
293 /// ## Notes
294 ///
295 /// - The detour doesn't change the main traversal elements - it only adds context data
296 /// - Detours can be nested for complex traversals
297 /// - The detour function can return any walker, allowing for flexible sub-traversals
298 /// - Use `push_context` inside detours to store data from the sub-traversal
299 /// - Detours are executed eagerly for each element in the traversal
300 pub fn detour<Path, Terminal, WalkerBuilderT>(
301 self,
302 predicate: Path,
303 ) -> VertexWalkerBuilder<'graph, Mutability, Graph, Detour<'graph, Walker, Path, Terminal>>
304 where
305 Path: Fn(
306 VertexWalkerBuilder<
307 'graph,
308 ImmutableMarker,
309 Graph,
310 Waypoint<'graph, Graph, Walker::Context>,
311 >,
312 ) -> WalkerBuilderT,
313 WalkerBuilderT: Into<self::WalkerBuilder<'graph, ImmutableMarker, Graph, Terminal>>,
314 Terminal: crate::walker::Walker<'graph, Graph = Graph>,
315 {
316 self.with_vertex_walker(|walker| Detour::new(walker, predicate))
317 }
318}