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