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}