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