graph_api_lib/walker/steps/
context.rs

1use crate::ElementId;
2use crate::graph::Graph;
3use crate::walker::builder::{EdgeWalkerBuilder, StartWalkerBuilder, VertexWalkerBuilder};
4use crate::walker::steps::Empty;
5use crate::walker::{EdgeWalker, VertexWalker, Walker};
6use include_doc::function_body;
7use std::marker::PhantomData;
8use std::ops::{Deref, DerefMut};
9// ================ CONTEXT IMPLEMENTATION ================
10
11#[derive(Clone, Debug)]
12pub struct ContextRef<Current, Parent> {
13    inner: Inner<Current, Parent>,
14}
15
16#[derive(Debug, Clone)]
17struct Inner<Current, Parent> {
18    parent: Parent,
19    delegate: Current,
20}
21
22impl<Current, Parent> Deref for ContextRef<Current, Parent> {
23    type Target = Current;
24
25    fn deref(&self) -> &Self::Target {
26        &self.inner.delegate
27    }
28}
29
30impl<Current, Parent> DerefMut for ContextRef<Current, Parent> {
31    fn deref_mut(&mut self) -> &mut Self::Target {
32        &mut self.inner.delegate
33    }
34}
35
36impl<Current, Parent> ContextRef<Current, Parent> {
37    pub(crate) fn new(delegate: Current, parent: Parent) -> ContextRef<Current, Parent> {
38        Self {
39            inner: Inner { parent, delegate },
40        }
41    }
42    pub fn parent(&self) -> &Parent {
43        &self.inner.parent
44    }
45}
46
47#[derive(Clone, Debug)]
48pub struct DefaultVertexContext<VertexId, Vertex> {
49    pub(crate) vertex_id: VertexId,
50    pub(crate) vertex: Vertex,
51}
52
53impl<VertexId, Vertex> DefaultVertexContext<VertexId, Vertex> {
54    pub fn vertex(&self) -> &Vertex {
55        &self.vertex
56    }
57
58    pub fn vertex_id(&self) -> &VertexId {
59        &self.vertex_id
60    }
61}
62
63#[derive(Clone, Debug)]
64pub struct DefaultEdgeContext<EdgeId, Edge> {
65    pub(crate) edge_id: EdgeId,
66    pub(crate) edge: Edge,
67}
68
69impl<EdgeId, Edge> DefaultEdgeContext<EdgeId, Edge> {
70    pub fn edge(&self) -> &Edge {
71        &self.edge
72    }
73
74    pub fn edge_id(&self) -> &EdgeId {
75        &self.edge_id
76    }
77}
78
79pub struct VertexContext<'graph, Parent, Callback, Context>
80where
81    Parent: VertexWalker<'graph>,
82    Callback: Fn(&<Parent::Graph as Graph>::VertexReference<'_>, &Parent::Context) -> Context,
83{
84    _phantom_data: PhantomData<&'graph ()>,
85    parent: Parent,
86    callback: Callback,
87    context: Option<Context>,
88}
89
90impl<'graph, Parent, Callback, Context> VertexContext<'graph, Parent, Callback, Context>
91where
92    Parent: VertexWalker<'graph>,
93    Callback: Fn(&<Parent::Graph as Graph>::VertexReference<'_>, &Parent::Context) -> Context,
94{
95    pub fn new(parent: Parent, callback: Callback) -> Self {
96        VertexContext {
97            _phantom_data: Default::default(),
98            parent,
99            callback,
100            context: None,
101        }
102    }
103}
104
105impl<'graph, Parent, Predicate, Context> Walker<'graph>
106    for VertexContext<'graph, Parent, Predicate, Context>
107where
108    Parent: VertexWalker<'graph>,
109    Predicate: Fn(&<Parent::Graph as Graph>::VertexReference<'_>, &Parent::Context) -> Context,
110    Context: Clone + 'static,
111{
112    type Graph = Parent::Graph;
113    type Context = Context;
114
115    fn next_element(&mut self, graph: &'graph Self::Graph) -> Option<ElementId<Self::Graph>> {
116        self.next(graph).map(ElementId::Vertex)
117    }
118
119    fn ctx(&self) -> &Self::Context {
120        self.context
121            .as_ref()
122            .expect("context cannot be retrieved before call to next")
123    }
124
125    fn ctx_mut(&mut self) -> &mut Self::Context {
126        self.context
127            .as_mut()
128            .expect("context cannot be retrieved before call to next")
129    }
130}
131
132impl<'graph, Parent, Predicate, Context> VertexWalker<'graph>
133    for VertexContext<'graph, Parent, Predicate, Context>
134where
135    Parent: VertexWalker<'graph>,
136    Predicate: Fn(&<Parent::Graph as Graph>::VertexReference<'_>, &Parent::Context) -> Context,
137    Context: Clone + 'static,
138{
139    fn next(&mut self, graph: &'graph Self::Graph) -> Option<<Self::Graph as Graph>::VertexId> {
140        while let Some(next) = self.parent.next(graph) {
141            if let Some(vertex) = graph.vertex(next) {
142                self.context = Some((self.callback)(&vertex, self.parent.ctx()));
143                return Some(next);
144            }
145        }
146        None
147    }
148}
149
150pub struct EdgeContext<'graph, Parent, Callback, Context>
151where
152    Parent: EdgeWalker<'graph>,
153    Callback: Fn(&<Parent::Graph as Graph>::EdgeReference<'_>, &Parent::Context) -> Context,
154{
155    _phantom_data: PhantomData<&'graph ()>,
156    parent: Parent,
157    callback: Callback,
158    context: Option<Context>,
159}
160
161impl<'graph, Parent, Callback, Context> EdgeContext<'graph, Parent, Callback, Context>
162where
163    Parent: EdgeWalker<'graph>,
164    Callback: Fn(&<Parent::Graph as Graph>::EdgeReference<'_>, &Parent::Context) -> Context,
165{
166    pub fn new(parent: Parent, callback: Callback) -> Self {
167        EdgeContext {
168            _phantom_data: Default::default(),
169            parent,
170            callback,
171            context: None,
172        }
173    }
174}
175
176impl<'graph, Parent, Predicate, Context> Walker<'graph>
177    for EdgeContext<'graph, Parent, Predicate, Context>
178where
179    Parent: EdgeWalker<'graph>,
180    Predicate: Fn(&<Parent::Graph as Graph>::EdgeReference<'_>, &Parent::Context) -> Context,
181    Context: Clone + 'static,
182{
183    type Graph = Parent::Graph;
184
185    type Context = Context;
186
187    fn next_element(&mut self, graph: &'graph Self::Graph) -> Option<ElementId<Self::Graph>> {
188        self.next(graph).map(ElementId::Edge)
189    }
190    fn ctx(&self) -> &Self::Context {
191        self.context
192            .as_ref()
193            .expect("context cannot be retrieved before call to next")
194    }
195
196    fn ctx_mut(&mut self) -> &mut Self::Context {
197        self.context
198            .as_mut()
199            .expect("context cannot be retrieved before call to next")
200    }
201}
202
203impl<'graph, Parent, Predicate, Context> EdgeWalker<'graph>
204    for EdgeContext<'graph, Parent, Predicate, Context>
205where
206    Parent: EdgeWalker<'graph>,
207    Predicate: Fn(&<Parent::Graph as Graph>::EdgeReference<'_>, &Parent::Context) -> Context,
208    Context: Clone + 'static,
209{
210    fn next(&mut self, graph: &'graph Self::Graph) -> Option<<Self::Graph as Graph>::EdgeId> {
211        if let Some(next) = self.parent.next(graph) {
212            if let Some(edge) = graph.edge(next) {
213                self.context = Some((self.callback)(&edge, self.parent.ctx()));
214                return Some(next);
215            }
216        }
217        None
218    }
219}
220
221impl<'graph, Mutability, Graph, Walker> VertexWalkerBuilder<'graph, Mutability, Graph, Walker>
222where
223    Graph: crate::graph::Graph,
224    Walker: VertexWalker<'graph, Graph = Graph>,
225{
226    /// # Context Step
227    ///
228    /// The `push_context` step allows you to associate additional data with each element in the traversal.
229    /// This is useful for carrying information along as you traverse, preserving state between traversal steps,
230    /// or accumulating results.
231    ///
232    /// ## Visual Diagram
233    ///
234    /// Before push_context step (traversal with regular elements):
235    /// ```text
236    ///   [Person A]* --- created ---> [Project X]*  
237    ///    |
238    ///   knows
239    ///    |
240    ///   [Person B]*
241    /// ```
242    ///
243    /// After push_context step (elements now have associated context data):
244    /// ```text
245    ///   [Person A]* + {age: 30} --- created ---> [Project X]* + {name: "Graph API"}
246    ///    |
247    ///   knows
248    ///    |
249    ///   [Person B]* + {age: 25}
250    /// ```
251    ///
252    /// ## Parameters
253    ///
254    /// - `callback`: A function that takes the current element and its existing context,
255    ///   and returns a new context value to associate with that element
256    ///
257    /// ## Return Value
258    ///
259    /// Returns a traversal with the same elements, but with additional context information
260    /// attached to each element.
261    ///
262    /// ## Example
263    ///
264    /// ```rust
265    #[doc = function_body!("examples/context.rs", vertex_context_example, [])]
266    /// ```
267    ///
268    /// ## Notes
269    ///
270    /// - Context is carried through the entire traversal, even across different graph elements
271    /// - Each push_context call creates a new context layer, with the previous context available as `ctx.parent()`
272    /// - For complex traversals, you can build a nested context structure
273    /// - The context is cloned for each element, so keep context objects relatively small for performance
274    /// - Use `push_default_context()` for common patterns like storing the element's ID and data
275    /// - Context persists even when traversing to different elements (e.g., from vertex to connected edge)
276    /// - When retrieving results, both the element and its context are returned in a tuple
277    pub fn push_context<Callback, Context>(
278        self,
279        callback: Callback,
280    ) -> VertexWalkerBuilder<
281        'graph,
282        Mutability,
283        Graph,
284        VertexContext<
285            'graph,
286            Walker,
287            impl Fn(
288                &Graph::VertexReference<'_>,
289                &Walker::Context,
290            ) -> ContextRef<Context, Walker::Context>,
291            ContextRef<Context, Walker::Context>,
292        >,
293    >
294    where
295        Callback: Fn(&Graph::VertexReference<'_>, &Walker::Context) -> Context + 'graph,
296        Context: Clone + 'static,
297    {
298        self.with_vertex_walker(move |walker| {
299            walker.context(move |vertex, context| {
300                ContextRef::new(callback(vertex, context), context.clone())
301            })
302        })
303    }
304}
305
306impl<'graph, Mutability, Graph, Walker> EdgeWalkerBuilder<'graph, Mutability, Graph, Walker>
307where
308    Graph: crate::graph::Graph,
309    Walker: EdgeWalker<'graph, Graph = Graph>,
310    <Walker as crate::walker::Walker<'graph>>::Context: Clone + 'static,
311{
312    /// # Context Step
313    ///
314    /// The `push_context` step allows you to associate additional data with each edge in the traversal.
315    /// This is useful for carrying information along as you traverse, preserving state between traversal steps,
316    /// or accumulating results.
317    ///
318    /// ## Visual Diagram
319    ///
320    /// Before push_context step (traversal with regular edges):
321    /// ```text
322    ///   [Person A] --- created* ---> [Project X]  
323    ///    |
324    ///   knows*
325    ///    |
326    ///    v
327    ///   [Person B]
328    /// ```
329    ///
330    /// After push_context step (edges now have associated context data):
331    /// ```text
332    ///   [Person A] --- created* + {type: "maintainer"} ---> [Project X]  
333    ///    |
334    ///   knows* + {since: "2020"}
335    ///    |
336    ///    v
337    ///   [Person B]
338    /// ```
339    ///
340    /// ## Parameters
341    ///
342    /// - `callback`: A function that takes the current edge and its existing context,
343    ///   and returns a new context value to associate with that edge
344    ///
345    /// ## Return Value
346    ///
347    /// Returns a traversal with the same elements, but with additional context information
348    /// attached to each edge.
349    ///
350    /// ## Example
351    ///
352    /// ```rust
353    #[doc = function_body!("examples/context.rs", edge_context_example, [])]
354    /// ```
355    ///
356    /// ## Notes
357    ///
358    /// - Context is carried through the entire traversal, even across different graph elements
359    /// - Each push_context call creates a new context layer, with the previous context available as `ctx.parent()`
360    /// - For complex traversals, you can build a nested context structure
361    /// - The context is cloned for each element, so keep context objects relatively small for performance
362    /// - Use `push_default_context()` for common patterns like storing the edge's ID and data
363    /// - When retrieving results, both the element and its context are returned in a tuple
364    pub fn push_context<Callback, Context>(
365        self,
366        callback: Callback,
367    ) -> EdgeWalkerBuilder<
368        'graph,
369        Mutability,
370        Graph,
371        EdgeContext<
372            'graph,
373            Walker,
374            impl Fn(&Graph::EdgeReference<'_>, &Walker::Context) -> ContextRef<Context, Walker::Context>,
375            ContextRef<Context, Walker::Context>,
376        >,
377    >
378    where
379        Callback: Fn(&Graph::EdgeReference<'_>, &Walker::Context) -> Context,
380        Context: Clone + 'static,
381    {
382        self.with_edge_walker(move |walker| {
383            walker.context(move |edge, context| {
384                ContextRef::new(callback(edge, context), context.clone())
385            })
386        })
387    }
388}
389
390impl<'graph, Graph, Mutability> StartWalkerBuilder<'graph, Mutability, Graph, ()>
391where
392    Graph: crate::graph::Graph,
393{
394    pub fn push_context<Context>(
395        self,
396        context: Context,
397    ) -> StartWalkerBuilder<'graph, Mutability, Graph, Context>
398    where
399        Context: Clone + 'static,
400    {
401        StartWalkerBuilder {
402            _phantom: Default::default(),
403            graph: self.graph,
404            empty: Empty::with_context(context),
405        }
406    }
407}