graph_api_lib/walker/steps/
fold.rs

1use crate::walker::builder::{EdgeWalkerBuilder, VertexWalkerBuilder};
2use crate::walker::{EdgeWalker, VertexWalker};
3use include_doc::function_body;
4
5impl<'graph, Mutability, Graph, Walker> VertexWalkerBuilder<'graph, Mutability, Graph, Walker>
6where
7    Graph: crate::graph::Graph,
8    Walker: VertexWalker<'graph, Graph = Graph>,
9{
10    /// # Fold Step
11    ///
12    /// The `fold` step allows you to accumulate a result by processing each element in a traversal.
13    /// This is similar to the standard Rust `fold` operation but works directly on graph traversals.
14    ///
15    /// ## Visual Diagram
16    ///
17    /// Before fold step (traversal position on vertices):
18    /// ```text
19    ///   [A]* --- edge1 ---> [B]* --- edge2 ---> [C]*  
20    ///    ^                                         
21    ///    |                                         
22    ///   edge3                                       
23    ///    |                                         
24    ///   [D]*                                        
25    /// ```
26    ///
27    /// During fold step (processing each element with accumulator):
28    /// ```text
29    ///   Accumulator: Init -> [A] -> [B] -> [C] -> [D] -> Final Result
30    /// ```
31    ///
32    /// ## Parameters
33    ///
34    /// - `init`: The initial value for the accumulator
35    /// - `f`: A closure that takes:
36    ///   - The current accumulator value
37    ///   - A reference to the current element (vertex or edge)
38    ///   - The current element's context
39    ///   - Returns the updated accumulator value
40    ///
41    /// ## Return Value
42    ///
43    /// Returns the final accumulated value after processing all elements in the traversal.
44    ///
45    /// ## Example
46    ///
47    /// ```rust
48    #[doc = function_body!("examples/fold.rs", vertex_example, [])]
49    /// ```
50    ///
51    /// ## Notes
52    ///
53    /// - The fold step is a terminal operation - it consumes the traversal and returns a value
54    /// - Unlike map, fold doesn't yield a stream of values but a single accumulated result
55    /// - The closure is called once for each element with the accumulator and element
56    /// - Can be used for many common operations like counting, summing, finding min/max, etc.
57    /// - More flexible than specialized steps like count when you need to calculate custom aggregates
58    /// - The accumulator can be any type that matches your needs
59    /// - Context is available if you need it for your calculations
60    pub fn fold<Acc, F>(mut self, init: Acc, mut f: F) -> Acc
61    where
62        F: FnMut(Acc, Graph::VertexReference<'graph>, &Walker::Context) -> Acc,
63        'graph: 'graph,
64    {
65        let graph = self.graph();
66        let mut walker = self.walker();
67        let mut acc = init;
68
69        while let Some(vertex_id) = walker.next(graph) {
70            let vertex = graph
71                .vertex(vertex_id)
72                .expect("vertex ID must resolve to vertex");
73            acc = f(acc, vertex, walker.ctx());
74        }
75
76        acc
77    }
78}
79
80impl<'graph, Mutability, Graph, Walker> EdgeWalkerBuilder<'graph, Mutability, Graph, Walker>
81where
82    Graph: crate::graph::Graph,
83    Walker: EdgeWalker<'graph, Graph = Graph>,
84{
85    /// # Fold Step
86    ///
87    /// Accumulates a result by processing each edge in the traversal.
88    ///
89    /// See the documentation for [`VertexWalkerBuilder::fold`] for more details.
90    ///
91    /// ## Example
92    ///
93    /// ```rust
94    #[doc = function_body!("examples/fold.rs", edge_example, [])]
95    /// ```
96    pub fn fold<Acc, F>(mut self, init: Acc, mut f: F) -> Acc
97    where
98        F: FnMut(Acc, Graph::EdgeReference<'graph>, &Walker::Context) -> Acc,
99        'graph: 'graph,
100    {
101        let graph = self.graph();
102        let mut walker = self.walker();
103        let mut acc = init;
104
105        while let Some(edge_id) = walker.next(graph) {
106            let edge = graph.edge(edge_id).expect("edge ID must resolve to edge");
107            acc = f(acc, edge, walker.ctx());
108        }
109
110        acc
111    }
112}