Skip to main content

facet_path/
walk.rs

1//! Shape-only visitor API for deterministic traversal of [`Shape`] trees.
2//!
3//! This module provides a visitor pattern for walking the static type structure
4//! described by [`Shape`], emitting [`Path`] context at each node. It traverses
5//! only the type metadata — no values are inspected.
6//!
7//! # Traversal order
8//!
9//! - **Depth-first, declaration order.** Struct fields are visited in the order
10//!   they appear in the source. Enum variants are visited in declaration order,
11//!   and within each variant, fields follow the same rule.
12//! - `enter` is called **before** children; `leave` is called **after** children
13//!   (only if `enter` returned [`VisitDecision::Recurse`]).
14//!
15//! # Traversal control
16//!
17//! [`VisitDecision`] returned from [`ShapeVisitor::enter`] controls descent:
18//!
19//! | Decision        | Effect                                          |
20//! |-----------------|-------------------------------------------------|
21//! | `Recurse`       | Visit children, then call `leave`.              |
22//! | `SkipChildren`  | Skip descendants of this node; `leave` is still called. |
23//! | `Stop`          | Terminate the entire walk immediately.           |
24//!
25//! # Cycle handling
26//!
27//! Recursive types (e.g. a tree node whose children are `Vec<Node>`) would cause
28//! infinite descent. The walker tracks ancestor types by [`ConstTypeId`] and
29//! **does not recurse** into a type that is already on the current path. When a
30//! cycle is detected the node is still reported to `enter`/`leave`, but its
31//! children are skipped automatically — the visitor does not need to handle this.
32
33use alloc::vec::Vec;
34
35use facet_core::{ConstTypeId, Def, Shape, Type, UserType};
36
37use crate::{Path, PathStep};
38
39/// Decision returned by [`ShapeVisitor::enter`] to control traversal.
40#[derive(Debug, Clone, Copy, PartialEq, Eq)]
41#[non_exhaustive]
42pub enum VisitDecision {
43    /// Descend into this node's children, then call [`ShapeVisitor::leave`].
44    Recurse,
45    /// Skip this node's descendants. [`ShapeVisitor::leave`] is still called.
46    SkipChildren,
47    /// Stop the entire walk immediately. No further callbacks are made.
48    Stop,
49}
50
51/// Outcome of [`walk_shape`].
52#[derive(Debug, Clone, Copy, PartialEq, Eq)]
53pub enum WalkStatus {
54    /// The walk visited every reachable node.
55    Completed,
56    /// The walk was terminated early by [`VisitDecision::Stop`].
57    Stopped,
58}
59
60/// Visitor trait for shape-only traversal.
61///
62/// Implement this trait to receive callbacks as [`walk_shape`] descends through
63/// a [`Shape`] tree.
64pub trait ShapeVisitor {
65    /// Called when the walker enters a node, **before** visiting children.
66    ///
67    /// Return a [`VisitDecision`] to control whether children are visited.
68    fn enter(&mut self, path: &Path, shape: &'static Shape) -> VisitDecision;
69
70    /// Called when the walker leaves a node, **after** visiting children (or
71    /// after skipping them if `enter` returned [`VisitDecision::SkipChildren`]).
72    ///
73    /// Not called if `enter` returned [`VisitDecision::Stop`].
74    fn leave(&mut self, path: &Path, shape: &'static Shape) {
75        let _ = (path, shape);
76    }
77}
78
79/// Walk a [`Shape`] tree depth-first, calling `visitor` at each node.
80///
81/// See the [module docs](self) for traversal order and control semantics.
82pub fn walk_shape(shape: &'static Shape, visitor: &mut impl ShapeVisitor) -> WalkStatus {
83    let mut path = Path::new(shape);
84    let mut ancestors: Vec<ConstTypeId> = Vec::new();
85    if walk_recursive(shape, visitor, &mut path, &mut ancestors) {
86        WalkStatus::Stopped
87    } else {
88        WalkStatus::Completed
89    }
90}
91
92/// Returns `true` if the walk was stopped.
93fn walk_recursive(
94    shape: &'static Shape,
95    visitor: &mut impl ShapeVisitor,
96    path: &mut Path,
97    ancestors: &mut Vec<ConstTypeId>,
98) -> bool {
99    // Cycle detection: if this type is already an ancestor, don't recurse
100    let is_cycle = ancestors.contains(&shape.id);
101
102    let decision = visitor.enter(path, shape);
103    match decision {
104        VisitDecision::Stop => return true,
105        VisitDecision::SkipChildren => {
106            visitor.leave(path, shape);
107            return false;
108        }
109        VisitDecision::Recurse => {
110            if is_cycle {
111                // Cycle detected — treat as SkipChildren automatically
112                visitor.leave(path, shape);
113                return false;
114            }
115        }
116    }
117
118    // Push onto ancestor stack for cycle detection
119    ancestors.push(shape.id);
120
121    let stopped = walk_children(shape, visitor, path, ancestors);
122
123    ancestors.pop();
124
125    if !stopped {
126        visitor.leave(path, shape);
127    }
128
129    stopped
130}
131
132/// Walk the children of a shape. Returns `true` if stopped.
133///
134/// A shape can describe its children through two orthogonal axes:
135///
136/// - `shape.ty` — structural type information (struct fields, enum variants)
137/// - `shape.def` — semantic container information (list element, map key/value, etc.)
138///
139/// Some types use both (e.g. `Option<T>` is an enum in `ty` and has `Def::Option`),
140/// so we must avoid double-visiting. The rule:
141///
142/// 1. `Def::Option` takes priority — Option is a container, not an enum to iterate.
143/// 2. If `ty` is `Struct` or `Enum`, walk children through `ty` (fields/variants).
144/// 3. Otherwise, walk children through `def` (container element shapes).
145/// 4. Walk `shape.inner` only if none of the above provided child access.
146fn walk_children(
147    shape: &'static Shape,
148    visitor: &mut impl ShapeVisitor,
149    path: &mut Path,
150    ancestors: &mut Vec<ConstTypeId>,
151) -> bool {
152    // Track whether we found children via ty or def, to avoid
153    // double-visiting through `inner`.
154    let mut has_structural_children = false;
155
156    // Option<T> is both UserType::Enum and Def::Option. We handle it via
157    // Def::Option (using OptionSome) because Option is semantically a
158    // container — the None/Some distinction is a runtime concern, not
159    // something a shape-only walk should iterate as enum variants.
160    if let Def::Option(od) = shape.def {
161        path.push(PathStep::OptionSome);
162        if walk_recursive(od.t(), visitor, path, ancestors) {
163            return true;
164        }
165        path.pop();
166        // Skip the Enum branch below — we already handled Option's children.
167        return false;
168    }
169
170    match shape.ty {
171        Type::User(UserType::Struct(st)) => {
172            has_structural_children = true;
173            for (i, field) in st.fields.iter().enumerate() {
174                path.push(PathStep::Field(i as u32));
175                if walk_recursive(field.shape(), visitor, path, ancestors) {
176                    return true;
177                }
178                path.pop();
179            }
180        }
181        Type::User(UserType::Enum(et)) => {
182            has_structural_children = true;
183            for (vi, variant) in et.variants.iter().enumerate() {
184                path.push(PathStep::Variant(vi as u32));
185
186                for (fi, field) in variant.data.fields.iter().enumerate() {
187                    path.push(PathStep::Field(fi as u32));
188                    if walk_recursive(field.shape(), visitor, path, ancestors) {
189                        return true;
190                    }
191                    path.pop();
192                }
193
194                path.pop();
195            }
196        }
197        _ => {}
198    }
199
200    // For types without structural children (Opaque, Primitive, Sequence, Pointer),
201    // descend through the semantic definition.
202    if !has_structural_children {
203        match shape.def {
204            Def::List(ld) => {
205                has_structural_children = true;
206                path.push(PathStep::Index(0));
207                if walk_recursive(ld.t(), visitor, path, ancestors) {
208                    return true;
209                }
210                path.pop();
211            }
212            Def::Array(ad) => {
213                has_structural_children = true;
214                path.push(PathStep::Index(0));
215                if walk_recursive(ad.t(), visitor, path, ancestors) {
216                    return true;
217                }
218                path.pop();
219            }
220            Def::Slice(sd) => {
221                has_structural_children = true;
222                path.push(PathStep::Index(0));
223                if walk_recursive(sd.t(), visitor, path, ancestors) {
224                    return true;
225                }
226                path.pop();
227            }
228            Def::NdArray(nd) => {
229                has_structural_children = true;
230                path.push(PathStep::Index(0));
231                if walk_recursive(nd.t(), visitor, path, ancestors) {
232                    return true;
233                }
234                path.pop();
235            }
236            Def::Set(sd) => {
237                has_structural_children = true;
238                path.push(PathStep::Index(0));
239                if walk_recursive(sd.t(), visitor, path, ancestors) {
240                    return true;
241                }
242                path.pop();
243            }
244            Def::Map(md) => {
245                has_structural_children = true;
246                path.push(PathStep::MapKey(0));
247                if walk_recursive(md.k(), visitor, path, ancestors) {
248                    return true;
249                }
250                path.pop();
251
252                path.push(PathStep::MapValue(0));
253                if walk_recursive(md.v(), visitor, path, ancestors) {
254                    return true;
255                }
256                path.pop();
257            }
258            Def::Option(od) => {
259                has_structural_children = true;
260                path.push(PathStep::OptionSome);
261                if walk_recursive(od.t(), visitor, path, ancestors) {
262                    return true;
263                }
264                path.pop();
265            }
266            Def::Result(rd) => {
267                has_structural_children = true;
268                // Result<T, E> is Opaque in ty, so we walk both T and E here.
269                // Use Variant(0) for Ok, Variant(1) for Err to match Result's
270                // semantic structure.
271                path.push(PathStep::Variant(0));
272                path.push(PathStep::Field(0));
273                if walk_recursive(rd.t(), visitor, path, ancestors) {
274                    return true;
275                }
276                path.pop();
277                path.pop();
278
279                path.push(PathStep::Variant(1));
280                path.push(PathStep::Field(0));
281                if walk_recursive(rd.e(), visitor, path, ancestors) {
282                    return true;
283                }
284                path.pop();
285                path.pop();
286            }
287            Def::Pointer(pd) => {
288                if let Some(pointee) = pd.pointee() {
289                    has_structural_children = true;
290                    path.push(PathStep::Deref);
291                    if walk_recursive(pointee, visitor, path, ancestors) {
292                        return true;
293                    }
294                    path.pop();
295                }
296            }
297            _ => {}
298        }
299    }
300
301    // Transparent inner type — only if nothing above already provided children.
302    // Types like Vec<T> set both Def::List and inner, and we don't want to
303    // visit T twice.
304    if !has_structural_children && let Some(inner) = shape.inner {
305        path.push(PathStep::Inner);
306        if walk_recursive(inner, visitor, path, ancestors) {
307            return true;
308        }
309        path.pop();
310    }
311
312    false
313}