graph_api_lib/walker/steps/control_flow.rs
1use crate::graph::Graph;
2use crate::walker::builder::{EdgeWalkerBuilder, VertexWalkerBuilder};
3use crate::walker::{EdgeWalker, VertexWalker, Walker};
4use crate::{EdgeReference, ElementId, VertexReference};
5use include_doc::function_body;
6use std::marker::PhantomData;
7use std::ops::ControlFlow;
8
9pub struct VertexControlFlow<'graph, Parent, Predicate> {
10 _phantom_data: PhantomData<&'graph ()>,
11 parent: Parent,
12 predicate: Predicate,
13}
14
15impl<Parent, Predicate> VertexControlFlow<'_, Parent, Predicate> {
16 pub(crate) fn new(parent: Parent, predicate: Predicate) -> Self {
17 Self {
18 _phantom_data: Default::default(),
19 parent,
20 predicate,
21 }
22 }
23}
24
25impl<'graph, Parent, Predicate> Walker<'graph> for VertexControlFlow<'graph, Parent, Predicate>
26where
27 Self: 'graph,
28 Parent: VertexWalker<'graph>,
29 for<'a> Predicate: Fn(
30 &'a <Parent::Graph as Graph>::VertexReference<'graph>,
31 &mut Parent::Context,
32 ) -> ControlFlow<
33 Option<&'a <Parent::Graph as Graph>::VertexReference<'graph>>,
34 Option<&'a <Parent::Graph as Graph>::VertexReference<'graph>>,
35 >,
36{
37 type Graph = Parent::Graph;
38 type Context = Parent::Context;
39
40 fn next_element(&mut self, graph: &'graph Self::Graph) -> Option<ElementId<Self::Graph>> {
41 self.next(graph).map(ElementId::Vertex)
42 }
43
44 fn ctx(&self) -> &Self::Context {
45 self.parent.ctx()
46 }
47
48 fn ctx_mut(&mut self) -> &mut Self::Context {
49 self.parent.ctx_mut()
50 }
51}
52
53impl<'graph, Parent, Predicate> VertexWalker<'graph>
54 for VertexControlFlow<'graph, Parent, Predicate>
55where
56 Self: 'graph,
57 Parent: VertexWalker<'graph>,
58 for<'a> Predicate: Fn(
59 &'a <Parent::Graph as Graph>::VertexReference<'graph>,
60 &mut Parent::Context,
61 ) -> ControlFlow<
62 Option<&'a <Parent::Graph as Graph>::VertexReference<'graph>>,
63 Option<&'a <Parent::Graph as Graph>::VertexReference<'graph>>,
64 >,
65{
66 fn next(&mut self, graph: &'graph Self::Graph) -> Option<<Self::Graph as Graph>::VertexId> {
67 while let Some(next) = self.parent.next(graph) {
68 if let Some(vertex) = graph.vertex(next) {
69 match (self.predicate)(&vertex, self.parent.ctx_mut()) {
70 ControlFlow::Continue(Some(reference)) => return Some(reference.id()),
71 ControlFlow::Continue(None) => continue, // Skip this element
72 ControlFlow::Break(reference) => {
73 return reference.map(|reference| reference.id());
74 } // Break with optional final element
75 }
76 }
77 }
78 None
79 }
80}
81
82pub struct EdgeControlFlow<'graph, Parent, Predicate> {
83 _phantom_data: PhantomData<&'graph ()>,
84 parent: Parent,
85 predicate: Predicate,
86}
87
88impl<Parent, Predicate> EdgeControlFlow<'_, Parent, Predicate> {
89 pub(crate) fn new(parent: Parent, predicate: Predicate) -> Self {
90 Self {
91 _phantom_data: Default::default(),
92 parent,
93 predicate,
94 }
95 }
96}
97
98impl<'graph, Parent, Predicate> Walker<'graph> for EdgeControlFlow<'graph, Parent, Predicate>
99where
100 Self: 'graph,
101 Parent: EdgeWalker<'graph>,
102 for<'a> Predicate: Fn(
103 &'a <Parent::Graph as Graph>::EdgeReference<'graph>,
104 &mut Parent::Context,
105 ) -> ControlFlow<
106 Option<&'a <Parent::Graph as Graph>::EdgeReference<'graph>>,
107 Option<&'a <Parent::Graph as Graph>::EdgeReference<'graph>>,
108 >,
109{
110 type Graph = Parent::Graph;
111 type Context = Parent::Context;
112
113 fn next_element(&mut self, graph: &'graph Self::Graph) -> Option<ElementId<Self::Graph>> {
114 self.next(graph).map(ElementId::Edge)
115 }
116
117 fn ctx(&self) -> &Self::Context {
118 self.parent.ctx()
119 }
120
121 fn ctx_mut(&mut self) -> &mut Self::Context {
122 self.parent.ctx_mut()
123 }
124}
125
126impl<'graph, Parent, Predicate> EdgeWalker<'graph> for EdgeControlFlow<'graph, Parent, Predicate>
127where
128 Self: 'graph,
129 Parent: EdgeWalker<'graph>,
130 for<'a> Predicate: Fn(
131 &'a <Parent::Graph as Graph>::EdgeReference<'graph>,
132 &mut Parent::Context,
133 ) -> ControlFlow<
134 Option<&'a <Parent::Graph as Graph>::EdgeReference<'graph>>,
135 Option<&'a <Parent::Graph as Graph>::EdgeReference<'graph>>,
136 >,
137{
138 fn next(&mut self, graph: &'graph Self::Graph) -> Option<<Self::Graph as Graph>::EdgeId> {
139 while let Some(next) = self.parent.next(graph) {
140 if let Some(edge) = graph.edge(next) {
141 match (self.predicate)(&edge, self.parent.ctx_mut()) {
142 ControlFlow::Continue(Some(reference)) => return Some(reference.id()),
143 ControlFlow::Continue(None) => continue, // Skip this element
144 ControlFlow::Break(reference) => {
145 return reference.map(|reference| reference.id());
146 } // Break with optional final element
147 }
148 }
149 }
150 None
151 }
152}
153
154impl<'graph, Mutability, Graph, Walker> VertexWalkerBuilder<'graph, Mutability, Graph, Walker>
155where
156 Graph: crate::graph::Graph,
157 Walker: VertexWalker<'graph, Graph = Graph>,
158{
159 /// # ControlFlow Step
160 ///
161 /// The `control_flow` step allows you to evaluate each vertex with a predicate function that returns a
162 /// `std::ops::ControlFlow` value. This gives precise control over traversal - you can either:
163 /// - Continue and include the element (ControlFlow::Continue(Some(id)))
164 /// - Continue but skip the element (ControlFlow::Continue(None))
165 /// - Stop traversal with an optional final element (ControlFlow::Break(option))
166 ///
167 /// ## Visual Diagram
168 ///
169 /// Before control_flow step (all vertices in traversal):
170 /// ```text
171 /// [A]* --- edge1 ---> [B]* --- edge2 ---> [C]*
172 /// ^
173 /// |
174 /// edge3
175 /// |
176 /// [D]*
177 /// ```
178 ///
179 /// After control_flow step that only includes projects and breaks on "Graph" projects:
180 /// ```text
181 /// [A] --- edge1 ---> [B]* --- edge2 ---> [C]*
182 /// ^
183 /// |
184 /// edge3
185 /// |
186 /// [D]
187 /// ```
188 ///
189 /// ## Parameters
190 ///
191 /// - `predicate`: A function that takes a reference to a vertex and a mutable reference to its context,
192 /// and returns a `std::ops::ControlFlow<Option<VertexId>, Option<VertexId>>` value:
193 /// - Return `ControlFlow::Continue(Some(vertex.id()))` to include the vertex and continue
194 /// - Return `ControlFlow::Continue(None)` to skip the vertex and continue
195 /// - Return `ControlFlow::Break(Some(vertex.id()))` to include the vertex and stop traversal
196 /// - Return `ControlFlow::Break(None)` to stop traversal without including the vertex
197 ///
198 /// ## Return Value
199 ///
200 /// A new walker that applies the control flow logic to the traversal.
201 ///
202 /// ## Example
203 ///
204 /// ```rust
205 #[doc = function_body!("examples/control_flow.rs", vertex_example, [])]
206 /// ```
207 ///
208 /// ## Notes
209 ///
210 /// - This step is more powerful than `filter()` as it can both filter elements and control traversal flow
211 /// - The predicate receives a mutable reference to the context, allowing you to update state during traversal
212 /// - Use this step when you need a combination of filtering and conditional stopping of traversal
213 /// - Only elements where the predicate returns `Some` will be included in the traversal
214 /// - When `ControlFlow::Break` is returned, the entire traversal stops immediately
215 pub fn control_flow<Predicate>(
216 self,
217 predicate: Predicate,
218 ) -> VertexWalkerBuilder<'graph, Mutability, Graph, VertexControlFlow<'graph, Walker, Predicate>>
219 where
220 Walker: 'graph,
221 for<'a> Predicate: Fn(
222 &'a Graph::VertexReference<'graph>,
223 &mut Walker::Context,
224 ) -> ControlFlow<
225 Option<&'a Graph::VertexReference<'graph>>,
226 Option<&'a Graph::VertexReference<'graph>>,
227 > + 'graph,
228 {
229 self.with_vertex_walker(|walker| VertexControlFlow::new(walker, predicate))
230 }
231}
232
233impl<'graph, Mutability, Graph, Walker> EdgeWalkerBuilder<'graph, Mutability, Graph, Walker>
234where
235 Graph: crate::graph::Graph,
236 Walker: EdgeWalker<'graph, Graph = Graph>,
237{
238 /// # ControlFlow Step
239 ///
240 /// The `control_flow` step allows you to evaluate each edge with a predicate function that returns a
241 /// `std::ops::ControlFlow` value. This gives precise control over traversal - you can either:
242 /// - Continue and include the element (ControlFlow::Continue(Some(id)))
243 /// - Continue but skip the element (ControlFlow::Continue(None))
244 /// - Stop traversal with an optional final element (ControlFlow::Break(option))
245 ///
246 /// ## Visual Diagram
247 ///
248 /// Before control_flow step (all edges in traversal):
249 /// ```text
250 /// [Person A] --- knows* ---> [Person B] --- created* ---> [Project]
251 /// ^
252 /// |
253 /// owns*
254 /// |
255 /// [Company]
256 /// ```
257 ///
258 /// After control_flow step that only includes "knows" edges and breaks on old connections:
259 /// ```text
260 /// [Person A] --- knows* ---> [Person B] --- created ---> [Project]
261 /// ^
262 /// |
263 /// owns
264 /// |
265 /// [Company]
266 /// ```
267 ///
268 /// ## Parameters
269 ///
270 /// - `predicate`: A function that takes a reference to an edge and a mutable reference to its context,
271 /// and returns a `std::ops::ControlFlow<Option<EdgeId>, Option<EdgeId>>` value:
272 /// - Return `ControlFlow::Continue(Some(edge.id()))` to include the edge and continue
273 /// - Return `ControlFlow::Continue(None)` to skip the edge and continue
274 /// - Return `ControlFlow::Break(Some(edge.id()))` to include the edge and stop traversal
275 /// - Return `ControlFlow::Break(None)` to stop traversal without including the edge
276 ///
277 /// ## Return Value
278 ///
279 /// A new walker that applies the control flow logic to the traversal.
280 ///
281 /// ## Example
282 ///
283 /// ```rust
284 #[doc = function_body!("examples/control_flow.rs", edge_example, [])]
285 /// ```
286 ///
287 /// ## Notes
288 ///
289 /// - This step is more powerful than `filter()` as it can both filter elements and control traversal flow
290 /// - The predicate receives a mutable reference to the context, allowing you to update state during traversal
291 /// - Use this step when you need a combination of filtering and conditional stopping of traversal
292 /// - Only elements where the predicate returns `Some` will be included in the traversal
293 /// - When `ControlFlow::Break` is returned, the entire traversal stops immediately
294 pub fn control_flow<Predicate>(
295 self,
296 predicate: Predicate,
297 ) -> EdgeWalkerBuilder<'graph, Mutability, Graph, EdgeControlFlow<'graph, Walker, Predicate>>
298 where
299 Walker: 'graph,
300 for<'a> Predicate: Fn(
301 &'a Graph::EdgeReference<'graph>,
302 &mut Walker::Context,
303 ) -> ControlFlow<
304 Option<&'a Graph::EdgeReference<'graph>>,
305 Option<&'a Graph::EdgeReference<'graph>>,
306 > + 'graph,
307 {
308 self.with_edge_walker(|walker| EdgeControlFlow::new(walker, predicate))
309 }
310}