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}