Skip to main content

nautilus_schema/
visitor.rs

1//! Visitor pattern for traversing the AST.
2//!
3//! This module provides a trait-based visitor pattern for flexible AST traversal.
4//! Implement the [`Visitor`] trait to define custom operations on AST nodes.
5//!
6//! # Example
7//!
8//! ```ignore
9//! use nautilus_schema::{visitor::Visitor, ast::*};
10//!
11//! struct ModelCounter {
12//!     count: usize,
13//! }
14//!
15//! impl Visitor for ModelCounter {
16//!     fn visit_model(&mut self, model: &ModelDecl) -> Result<()> {
17//!         self.count += 1;
18//!         walk_model(self, model)
19//!     }
20//! }
21//! ```
22
23use crate::ast::*;
24use crate::error::Result;
25
26/// Visitor trait for traversing AST nodes.
27///
28/// All methods have default implementations that continue traversing child nodes.
29/// Override specific methods to implement custom behavior.
30pub trait Visitor: Sized {
31    /// Visit the entire schema.
32    fn visit_schema(&mut self, schema: &Schema) -> Result<()> {
33        walk_schema(self, schema)
34    }
35
36    /// Visit a top-level declaration.
37    fn visit_declaration(&mut self, decl: &Declaration) -> Result<()> {
38        walk_declaration(self, decl)
39    }
40
41    /// Visit a datasource declaration.
42    fn visit_datasource(&mut self, datasource: &DatasourceDecl) -> Result<()> {
43        walk_datasource(self, datasource)
44    }
45
46    /// Visit a generator declaration.
47    fn visit_generator(&mut self, generator: &GeneratorDecl) -> Result<()> {
48        walk_generator(self, generator)
49    }
50
51    /// Visit a model declaration.
52    fn visit_model(&mut self, model: &ModelDecl) -> Result<()> {
53        walk_model(self, model)
54    }
55
56    /// Visit an enum declaration.
57    fn visit_enum(&mut self, enum_decl: &EnumDecl) -> Result<()> {
58        walk_enum(self, enum_decl)
59    }
60
61    /// Visit a field declaration.
62    fn visit_field(&mut self, field: &FieldDecl) -> Result<()> {
63        walk_field(self, field)
64    }
65
66    /// Visit an enum variant.
67    fn visit_enum_variant(&mut self, variant: &EnumVariant) -> Result<()> {
68        walk_enum_variant(self, variant)
69    }
70
71    /// Visit a field attribute.
72    fn visit_field_attribute(&mut self, attr: &FieldAttribute) -> Result<()> {
73        walk_field_attribute(self, attr)
74    }
75
76    /// Visit a model attribute.
77    fn visit_model_attribute(&mut self, attr: &ModelAttribute) -> Result<()> {
78        walk_model_attribute(self, attr)
79    }
80
81    /// Visit an expression.
82    fn visit_expr(&mut self, expr: &Expr) -> Result<()> {
83        walk_expr(self, expr)
84    }
85
86    /// Visit a configuration field.
87    fn visit_config_field(&mut self, field: &ConfigField) -> Result<()> {
88        walk_config_field(self, field)
89    }
90
91    /// Visit a composite type declaration.
92    fn visit_type_decl(&mut self, type_decl: &TypeDecl) -> Result<()> {
93        walk_type_decl(self, type_decl)
94    }
95}
96
97/// Walk through a schema, visiting all declarations.
98pub fn walk_schema<V: Visitor>(visitor: &mut V, schema: &Schema) -> Result<()> {
99    for decl in &schema.declarations {
100        visitor.visit_declaration(decl)?;
101    }
102    Ok(())
103}
104
105/// Walk through a declaration.
106pub fn walk_declaration<V: Visitor>(visitor: &mut V, decl: &Declaration) -> Result<()> {
107    match decl {
108        Declaration::Datasource(ds) => visitor.visit_datasource(ds),
109        Declaration::Generator(gen) => visitor.visit_generator(gen),
110        Declaration::Model(model) => visitor.visit_model(model),
111        Declaration::Enum(enum_decl) => visitor.visit_enum(enum_decl),
112        Declaration::Type(type_decl) => visitor.visit_type_decl(type_decl),
113    }
114}
115
116/// Walk through a datasource declaration.
117pub fn walk_datasource<V: Visitor>(visitor: &mut V, datasource: &DatasourceDecl) -> Result<()> {
118    for field in &datasource.fields {
119        visitor.visit_config_field(field)?;
120    }
121    Ok(())
122}
123
124/// Walk through a generator declaration.
125pub fn walk_generator<V: Visitor>(visitor: &mut V, generator: &GeneratorDecl) -> Result<()> {
126    for field in &generator.fields {
127        visitor.visit_config_field(field)?;
128    }
129    Ok(())
130}
131
132/// Walk through a model declaration.
133pub fn walk_model<V: Visitor>(visitor: &mut V, model: &ModelDecl) -> Result<()> {
134    for field in &model.fields {
135        visitor.visit_field(field)?;
136    }
137    for attr in &model.attributes {
138        visitor.visit_model_attribute(attr)?;
139    }
140    Ok(())
141}
142
143/// Walk through an enum declaration.
144pub fn walk_enum<V: Visitor>(visitor: &mut V, enum_decl: &EnumDecl) -> Result<()> {
145    for variant in &enum_decl.variants {
146        visitor.visit_enum_variant(variant)?;
147    }
148    Ok(())
149}
150
151/// Walk through a field declaration.
152pub fn walk_field<V: Visitor>(visitor: &mut V, field: &FieldDecl) -> Result<()> {
153    for attr in &field.attributes {
154        visitor.visit_field_attribute(attr)?;
155    }
156    Ok(())
157}
158
159/// Walk through an enum variant.
160pub fn walk_enum_variant<V: Visitor>(_visitor: &mut V, _variant: &EnumVariant) -> Result<()> {
161    Ok(())
162}
163
164/// Walk through a field attribute.
165pub fn walk_field_attribute<V: Visitor>(visitor: &mut V, attr: &FieldAttribute) -> Result<()> {
166    match attr {
167        FieldAttribute::Default(expr, _) => visitor.visit_expr(expr),
168        FieldAttribute::Relation { .. } => {
169            // Relation fields are identifiers, not expressions
170            Ok(())
171        }
172        _ => Ok(()),
173    }
174}
175
176/// Walk through a model attribute.
177pub fn walk_model_attribute<V: Visitor>(_visitor: &mut V, _attr: &ModelAttribute) -> Result<()> {
178    Ok(())
179}
180
181/// Walk through an expression.
182pub fn walk_expr<V: Visitor>(visitor: &mut V, expr: &Expr) -> Result<()> {
183    match expr {
184        Expr::FunctionCall { args, .. } => {
185            for arg in args {
186                visitor.visit_expr(arg)?;
187            }
188            Ok(())
189        }
190        Expr::Array { elements, .. } => {
191            for elem in elements {
192                visitor.visit_expr(elem)?;
193            }
194            Ok(())
195        }
196        Expr::NamedArg { value, .. } => visitor.visit_expr(value),
197        Expr::Literal(_) | Expr::Ident(_) => Ok(()),
198    }
199}
200
201/// Walk through a configuration field.
202pub fn walk_config_field<V: Visitor>(visitor: &mut V, field: &ConfigField) -> Result<()> {
203    visitor.visit_expr(&field.value)
204}
205
206/// Walk through a composite type declaration.
207pub fn walk_type_decl<V: Visitor>(visitor: &mut V, type_decl: &TypeDecl) -> Result<()> {
208    for field in &type_decl.fields {
209        visitor.visit_field(field)?;
210    }
211    Ok(())
212}
213
214/// Example visitor that counts nodes in the AST.
215#[derive(Debug, Default)]
216pub struct CountingVisitor {
217    /// Number of models.
218    pub models: usize,
219    /// Number of enums.
220    pub enums: usize,
221    /// Number of fields.
222    pub fields: usize,
223    /// Number of datasources.
224    pub datasources: usize,
225    /// Number of generators.
226    pub generators: usize,
227}
228
229impl Visitor for CountingVisitor {
230    fn visit_datasource(&mut self, datasource: &DatasourceDecl) -> Result<()> {
231        self.datasources += 1;
232        walk_datasource(self, datasource)
233    }
234
235    fn visit_generator(&mut self, generator: &GeneratorDecl) -> Result<()> {
236        self.generators += 1;
237        walk_generator(self, generator)
238    }
239
240    fn visit_model(&mut self, model: &ModelDecl) -> Result<()> {
241        self.models += 1;
242        walk_model(self, model)
243    }
244
245    fn visit_enum(&mut self, enum_decl: &EnumDecl) -> Result<()> {
246        self.enums += 1;
247        walk_enum(self, enum_decl)
248    }
249
250    fn visit_field(&mut self, field: &FieldDecl) -> Result<()> {
251        self.fields += 1;
252        walk_field(self, field)
253    }
254}
255
256#[cfg(test)]
257mod tests {
258    use super::*;
259    use crate::span::Span;
260
261    #[test]
262    fn test_walk_expression() {
263        let expr = Expr::FunctionCall {
264            name: Ident::new("now".to_string(), Span::new(0, 3)),
265            args: vec![],
266            span: Span::new(0, 5),
267        };
268
269        let mut visitor = CountingVisitor::default();
270        visitor.visit_expr(&expr).unwrap();
271    }
272
273    #[test]
274    fn test_walk_nested_expr() {
275        let expr = Expr::Array {
276            elements: vec![
277                Expr::Literal(Literal::String("test".to_string(), Span::new(0, 4))),
278                Expr::FunctionCall {
279                    name: Ident::new("env".to_string(), Span::new(0, 3)),
280                    args: vec![Expr::Literal(Literal::String(
281                        "VAR".to_string(),
282                        Span::new(0, 3),
283                    ))],
284                    span: Span::new(0, 10),
285                },
286            ],
287            span: Span::new(0, 20),
288        };
289
290        let mut visitor = CountingVisitor::default();
291        visitor.visit_expr(&expr).unwrap();
292    }
293}