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}