bc_envelope/base/walk.rs
1//! Functions for traversing and manipulating the envelope hierarchy.
2//!
3//! This module provides functionality for traversing the hierarchical structure
4//! of envelopes, allowing for operations such as inspection, transformation,
5//! and extraction of specific elements. It implements a visitor pattern that
6//! enables executing arbitrary code on each element of an envelope in a
7//! structured way.
8//!
9//! The traversal can be performed in two modes:
10//! - Structure-based traversal: Visits every element in the envelope hierarchy
11//! - Tree-based traversal: Skips node elements and focuses on the semantic
12//! content
13//!
14//! # Examples
15//!
16//! ```
17//! use std::{cell::RefCell, collections::HashSet};
18//!
19//! use bc_envelope::prelude::*;
20//!
21//! // Create an envelope with nested structure
22//! let envelope = Envelope::new("Alice")
23//! .add_assertion("knows", "Bob")
24//! .add_assertion("email", "alice@example.com");
25//!
26//! // Collect all digests in the envelope by walking its structure
27//! let digests = RefCell::new(HashSet::new());
28//! let visitor = |env: &Envelope,
29//! _level: usize,
30//! _edge: EdgeType,
31//! state: ()|
32//! -> ((), bool) {
33//! digests.borrow_mut().insert(env.digest());
34//! (state, false)
35//! };
36//!
37//! // Walk the entire envelope structure
38//! envelope.walk(false, (), &visitor);
39//!
40//! // All elements of the envelope will have their digests collected
41//! assert!(digests.borrow().len() > 0);
42//! ```
43
44use super::envelope::EnvelopeCase;
45use crate::Envelope;
46
47/// The type of incoming edge provided to the visitor.
48///
49/// This enum identifies how an envelope element is connected to its parent in
50/// the hierarchy during traversal. It helps the visitor function understand the
51/// semantic relationship between elements.
52///
53/// Each edge type represents a specific relationship within the envelope
54/// structure:
55/// - `None`: Root or no connection
56/// - `Subject`: Element is the subject of its parent node
57/// - `Assertion`: Element is an assertion on its parent node
58/// - `Predicate`: Element is the predicate of an assertion
59/// - `Object`: Element is the object of an assertion
60/// - `Wrapped`: Element is wrapped by its parent
61#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
62pub enum EdgeType {
63 /// No incoming edge (root)
64 None,
65 /// Element is the subject of a node
66 Subject,
67 /// Element is an assertion on a node
68 Assertion,
69 /// Element is the predicate of an assertion
70 Predicate,
71 /// Element is the object of an assertion
72 Object,
73 /// Element is the content wrapped by another envelope
74 Content,
75}
76
77/// Provides a label for the edge type in tree formatting.
78impl EdgeType {
79 /// Returns a short text label for the edge type, or None if no label is
80 /// needed.
81 ///
82 /// This is primarily used for tree formatting to identify relationships
83 /// between elements.
84 ///
85 /// # Examples
86 ///
87 /// ```
88 /// # use bc_envelope::prelude::*;
89 /// assert_eq!(EdgeType::Subject.label(), Some("subj"));
90 /// assert_eq!(EdgeType::Content.label(), Some("cont"));
91 /// assert_eq!(EdgeType::Predicate.label(), Some("pred"));
92 /// assert_eq!(EdgeType::Object.label(), Some("obj"));
93 /// assert_eq!(EdgeType::Assertion.label(), None);
94 /// ```
95 pub fn label(&self) -> Option<&'static str> {
96 match self {
97 EdgeType::Subject => Some("subj"),
98 EdgeType::Content => Some("cont"),
99 EdgeType::Predicate => Some("pred"),
100 EdgeType::Object => Some("obj"),
101 _ => None,
102 }
103 }
104}
105
106/// A visitor function that is called for each element in the envelope.
107///
108/// The visitor function takes the following parameters:
109/// - `envelope`: The current envelope element being visited
110/// - `level`: The depth level in the hierarchy (0 for root)
111/// - `incoming_edge`: The type of edge connecting this element to its parent
112/// - `state`: Optional context passed down from the parent's visitor call
113///
114/// The visitor returns a state that will be passed to child elements.
115/// This enables accumulating state or passing context during traversal.
116///
117/// # Type Parameters
118///
119/// * `State` - The type of context passed between parent and child elements
120pub type Visitor<'a, State> =
121 dyn Fn(&Envelope, usize, EdgeType, State) -> (State, bool) + 'a;
122
123/// Functions for traversing and manipulating the envelope hierarchy.
124impl Envelope {
125 /// Walks the envelope structure, calling the visitor function for each
126 /// element.
127 ///
128 /// This function traverses the entire envelope hierarchy and calls the
129 /// visitor function on each element. The traversal can be performed in
130 /// two modes:
131 ///
132 /// - Structure-based traversal (`hide_nodes = false`): Visits every element
133 /// including node containers
134 /// - Tree-based traversal (`hide_nodes = true`): Skips node elements and
135 /// focuses on semantic content
136 ///
137 /// The visitor function can optionally return a context value that is
138 /// passed to child elements, enabling state to be accumulated or passed
139 /// down during traversal.
140 ///
141 /// # Type Parameters
142 ///
143 /// * `Parent` - The type of context passed between parent and child
144 /// elements
145 ///
146 /// # Arguments
147 ///
148 /// * `hide_nodes` - If true, the visitor will not be called for node
149 /// containers
150 /// * `visit` - The visitor function called for each element
151 ///
152 /// # Examples
153 ///
154 /// ```
155 /// use std::cell::RefCell;
156 ///
157 /// use bc_envelope::prelude::*;
158 ///
159 /// // Create an envelope with nested structure
160 /// let envelope = Envelope::new("Alice").add_assertion("knows", "Bob");
161 ///
162 /// // Count the number of elements in the envelope
163 /// let count = RefCell::new(0);
164 /// let visitor = |_env: &Envelope,
165 /// _level: usize,
166 /// _edge: EdgeType,
167 /// state: ()|
168 /// -> ((), bool) {
169 /// *count.borrow_mut() += 1;
170 /// (state, false)
171 /// };
172 ///
173 /// // Walk the entire envelope structure
174 /// envelope.walk(false, (), &visitor);
175 /// assert!(*count.borrow() > 0);
176 /// ```
177 pub fn walk<State: Clone>(
178 &self,
179 hide_nodes: bool,
180 state: State,
181 visit: &Visitor<'_, State>,
182 ) {
183 if hide_nodes {
184 self.walk_tree(state, visit)
185 } else {
186 self.walk_structure(state, visit)
187 }
188 }
189
190 /// Walks the complete structure of the envelope, visiting every element.
191 ///
192 /// This is an internal method that begins a structure-based traversal from
193 /// the root level. Use the public `walk` method with `hide_nodes =
194 /// false` instead of calling this directly.
195 fn walk_structure<State: Clone>(
196 &self,
197 state: State,
198 visit: &Visitor<'_, State>,
199 ) {
200 self._walk_structure(0, EdgeType::None, state, visit)
201 }
202
203 /// Recursive implementation of structure-based traversal.
204 ///
205 /// This internal method performs the actual recursive traversal of the
206 /// envelope structure, visiting every element and maintaining the
207 /// correct level and edge relationships.
208 fn _walk_structure<State: Clone>(
209 &self,
210 level: usize,
211 incoming_edge: EdgeType,
212 state: State,
213 visit: &Visitor<'_, State>,
214 ) {
215 let mut state = state;
216 let stop;
217 (state, stop) = visit(self, level, incoming_edge, state);
218 if stop {
219 return;
220 }
221 let next_level = level + 1;
222 match self.case() {
223 EnvelopeCase::Node { subject, assertions, .. } => {
224 subject._walk_structure(
225 next_level,
226 EdgeType::Subject,
227 state.clone(),
228 visit,
229 );
230 for assertion in assertions {
231 assertion._walk_structure(
232 next_level,
233 EdgeType::Assertion,
234 state.clone(),
235 visit,
236 );
237 }
238 }
239 EnvelopeCase::Wrapped { envelope, .. } => {
240 envelope._walk_structure(
241 next_level,
242 EdgeType::Content,
243 state,
244 visit,
245 );
246 }
247 EnvelopeCase::Assertion(assertion) => {
248 assertion.predicate()._walk_structure(
249 next_level,
250 EdgeType::Predicate,
251 state.clone(),
252 visit,
253 );
254 assertion.object()._walk_structure(
255 next_level,
256 EdgeType::Object,
257 state,
258 visit,
259 );
260 }
261 _ => {}
262 }
263 }
264
265 /// Walks the envelope's semantic tree, skipping node containers.
266 ///
267 /// This is an internal method that begins a tree-based traversal from the
268 /// root level. Use the public `walk` method with `hide_nodes = true`
269 /// instead of calling this directly.
270 fn walk_tree<State: Clone>(
271 &self,
272 state: State,
273 visit: &Visitor<'_, State>,
274 ) {
275 _ = self._walk_tree(0, EdgeType::None, state, visit)
276 }
277
278 /// Recursive implementation of tree-based traversal.
279 ///
280 /// This internal method performs the actual recursive traversal of the
281 /// envelope's semantic tree, skipping node containers and focusing on
282 /// the semantic content elements. It maintains the correct level and
283 /// edge relationships while skipping structural elements.
284 fn _walk_tree<State: Clone>(
285 &self,
286 level: usize,
287 incoming_edge: EdgeType,
288 state: State,
289 visit: &Visitor<'_, State>,
290 ) -> State {
291 let mut state = state;
292 let mut subject_level = level;
293 if !self.is_node() {
294 let stop;
295 (state, stop) = visit(self, level, incoming_edge, state);
296 if stop {
297 return state;
298 }
299 subject_level = level + 1;
300 }
301 match self.case() {
302 EnvelopeCase::Node { subject, assertions, .. } => {
303 let assertion_state = subject._walk_tree(
304 subject_level,
305 EdgeType::Subject,
306 state.clone(),
307 visit,
308 );
309 let assertion_level = subject_level + 1;
310 for assertion in assertions {
311 assertion._walk_tree(
312 assertion_level,
313 EdgeType::Assertion,
314 assertion_state.clone(),
315 visit,
316 );
317 }
318 }
319 EnvelopeCase::Wrapped { envelope, .. } => {
320 envelope._walk_tree(
321 subject_level,
322 EdgeType::Content,
323 state.clone(),
324 visit,
325 );
326 }
327 EnvelopeCase::Assertion(assertion) => {
328 assertion.predicate()._walk_tree(
329 subject_level,
330 EdgeType::Predicate,
331 state.clone(),
332 visit,
333 );
334 assertion.object()._walk_tree(
335 subject_level,
336 EdgeType::Object,
337 state.clone(),
338 visit,
339 );
340 }
341 _ => {}
342 }
343 state
344 }
345}