Skip to main content

nautilus_schema/
ast.rs

1//! Abstract Syntax Tree (AST) for the nautilus schema language.
2//!
3//! This module defines the complete AST structure for representing parsed schemas.
4//! All nodes include [`Span`] information for precise error diagnostics.
5//!
6//! The AST supports the Visitor pattern via the [`accept`](Schema::accept) methods,
7//! allowing flexible traversal and transformation operations.
8//!
9//! # Example
10//!
11//! ```ignore
12//! use nautilus_schema::{Lexer, Parser};
13//!
14//! let source = r#"
15//!     model User {
16//!       id    Int    @id @default(autoincrement())
17//!       email String @unique
18//!     }
19//! "#;
20//!
21//! let tokens = Lexer::new(source).collect::<Result<Vec<_>, _>>().unwrap();
22//! let schema = Parser::new(&tokens).parse_schema().unwrap();
23//!
24//! println!("Found {} declarations", schema.declarations.len());
25//! ```
26
27use crate::span::Span;
28use std::fmt;
29
30/// Top-level schema document containing all declarations.
31#[derive(Debug, Clone, PartialEq)]
32pub struct Schema {
33    /// All declarations in the schema (datasources, generators, models, enums).
34    pub declarations: Vec<Declaration>,
35    /// Span covering the entire schema.
36    pub span: Span,
37}
38
39impl Schema {
40    /// Creates a new schema with the given declarations.
41    pub fn new(declarations: Vec<Declaration>, span: Span) -> Self {
42        Self { declarations, span }
43    }
44
45    /// Finds all model declarations in the schema.
46    pub fn models(&self) -> impl Iterator<Item = &ModelDecl> {
47        self.declarations.iter().filter_map(|d| match d {
48            Declaration::Model(m) => Some(m),
49            _ => None,
50        })
51    }
52
53    /// Finds all enum declarations in the schema.
54    pub fn enums(&self) -> impl Iterator<Item = &EnumDecl> {
55        self.declarations.iter().filter_map(|d| match d {
56            Declaration::Enum(e) => Some(e),
57            _ => None,
58        })
59    }
60
61    /// Finds all composite type declarations in the schema.
62    pub fn types(&self) -> impl Iterator<Item = &TypeDecl> {
63        self.declarations.iter().filter_map(|d| match d {
64            Declaration::Type(t) => Some(t),
65            _ => None,
66        })
67    }
68
69    /// Finds the first datasource declaration.
70    pub fn datasource(&self) -> Option<&DatasourceDecl> {
71        self.declarations.iter().find_map(|d| match d {
72            Declaration::Datasource(ds) => Some(ds),
73            _ => None,
74        })
75    }
76
77    /// Finds the first generator declaration.
78    pub fn generator(&self) -> Option<&GeneratorDecl> {
79        self.declarations.iter().find_map(|d| match d {
80            Declaration::Generator(g) => Some(g),
81            _ => None,
82        })
83    }
84}
85
86/// A top-level declaration in the schema.
87#[derive(Debug, Clone, PartialEq)]
88pub enum Declaration {
89    /// A datasource block.
90    Datasource(DatasourceDecl),
91    /// A generator block.
92    Generator(GeneratorDecl),
93    /// A model block.
94    Model(ModelDecl),
95    /// An enum block.
96    Enum(EnumDecl),
97    /// A composite type block.
98    Type(TypeDecl),
99}
100
101impl Declaration {
102    /// Returns the span of this declaration.
103    pub fn span(&self) -> Span {
104        match self {
105            Declaration::Datasource(d) => d.span,
106            Declaration::Generator(g) => g.span,
107            Declaration::Model(m) => m.span,
108            Declaration::Enum(e) => e.span,
109            Declaration::Type(t) => t.span,
110        }
111    }
112
113    /// Returns the name of this declaration.
114    pub fn name(&self) -> &str {
115        match self {
116            Declaration::Datasource(d) => &d.name.value,
117            Declaration::Generator(g) => &g.name.value,
118            Declaration::Model(m) => &m.name.value,
119            Declaration::Enum(e) => &e.name.value,
120            Declaration::Type(t) => &t.name.value,
121        }
122    }
123}
124
125/// A datasource block declaration.
126///
127/// # Example
128///
129/// ```prisma
130/// datasource db {
131///   provider = "postgresql"
132///   url      = env("DATABASE_URL")
133/// }
134/// ```
135#[derive(Debug, Clone, PartialEq)]
136pub struct DatasourceDecl {
137    /// The name of the datasource (e.g., "db").
138    pub name: Ident,
139    /// Configuration fields (key-value pairs).
140    pub fields: Vec<ConfigField>,
141    /// Span covering the entire datasource block.
142    pub span: Span,
143}
144
145impl DatasourceDecl {
146    /// Finds a configuration field by name.
147    pub fn find_field(&self, name: &str) -> Option<&ConfigField> {
148        self.fields.iter().find(|f| f.name.value == name)
149    }
150
151    /// Gets the provider value if present.
152    pub fn provider(&self) -> Option<&str> {
153        self.find_field("provider").and_then(|f| match &f.value {
154            Expr::Literal(Literal::String(s, _)) => Some(s.as_str()),
155            _ => None,
156        })
157    }
158}
159
160/// A generator block declaration.
161///
162/// # Example
163///
164/// ```prisma
165/// generator client {
166///   provider = "nautilus-client-rs"
167///   output   = "../generated"
168/// }
169/// ```
170#[derive(Debug, Clone, PartialEq)]
171pub struct GeneratorDecl {
172    /// The name of the generator (e.g., "client").
173    pub name: Ident,
174    /// Configuration fields (key-value pairs).
175    pub fields: Vec<ConfigField>,
176    /// Span covering the entire generator block.
177    pub span: Span,
178}
179
180impl GeneratorDecl {
181    /// Finds a configuration field by name.
182    pub fn find_field(&self, name: &str) -> Option<&ConfigField> {
183        self.fields.iter().find(|f| f.name.value == name)
184    }
185}
186
187/// A configuration field in a datasource or generator block.
188#[derive(Debug, Clone, PartialEq)]
189pub struct ConfigField {
190    /// The field name.
191    pub name: Ident,
192    /// The field value (typically a string or function call).
193    pub value: Expr,
194    /// Span covering the entire field declaration.
195    pub span: Span,
196}
197
198/// A model block declaration.
199///
200/// # Example
201///
202/// ```prisma
203/// model User {
204///   id    Int    @id @default(autoincrement())
205///   email String @unique
206///   @@map("users")
207/// }
208/// ```
209#[derive(Debug, Clone, PartialEq)]
210pub struct ModelDecl {
211    /// The model name (e.g., "User").
212    pub name: Ident,
213    /// Field declarations.
214    pub fields: Vec<FieldDecl>,
215    /// Model-level attributes (@@map, @@id, etc.).
216    pub attributes: Vec<ModelAttribute>,
217    /// Span covering the entire model block.
218    pub span: Span,
219}
220
221impl ModelDecl {
222    /// Finds a field by name.
223    pub fn find_field(&self, name: &str) -> Option<&FieldDecl> {
224        self.fields.iter().find(|f| f.name.value == name)
225    }
226
227    /// Gets the physical table name from @@map attribute, or the model name.
228    pub fn table_name(&self) -> &str {
229        self.attributes
230            .iter()
231            .find_map(|attr| match attr {
232                ModelAttribute::Map(name) => Some(name.as_str()),
233                _ => None,
234            })
235            .unwrap_or(&self.name.value)
236    }
237
238    /// Checks if this model has a composite primary key (@@id).
239    pub fn has_composite_key(&self) -> bool {
240        self.attributes
241            .iter()
242            .any(|attr| matches!(attr, ModelAttribute::Id(_)))
243    }
244
245    /// Returns all fields that are part of relations.
246    /// This includes fields with user-defined types (model/enum references).
247    pub fn relation_fields(&self) -> impl Iterator<Item = &FieldDecl> {
248        self.fields.iter().filter(|f| {
249            f.has_relation_attribute() || matches!(f.field_type, FieldType::UserType(_))
250        })
251    }
252}
253
254/// A field declaration within a model.
255#[derive(Debug, Clone, PartialEq)]
256pub struct FieldDecl {
257    /// The field name.
258    pub name: Ident,
259    /// The field type.
260    pub field_type: FieldType,
261    /// Optional or array modifier.
262    pub modifier: FieldModifier,
263    /// Field-level attributes (@id, @unique, etc.).
264    pub attributes: Vec<FieldAttribute>,
265    /// Span covering the entire field declaration.
266    pub span: Span,
267}
268
269impl FieldDecl {
270    /// Checks if this field is optional (has `?` modifier).
271    pub fn is_optional(&self) -> bool {
272        matches!(self.modifier, FieldModifier::Optional)
273    }
274
275    /// Checks if this field has an explicit not-null modifier (`!`).
276    pub fn is_not_null(&self) -> bool {
277        matches!(self.modifier, FieldModifier::NotNull)
278    }
279
280    /// Checks if this field is an array (has `[]` modifier).
281    pub fn is_array(&self) -> bool {
282        matches!(self.modifier, FieldModifier::Array)
283    }
284
285    /// Finds a field attribute by kind.
286    pub fn find_attribute(&self, kind: &str) -> Option<&FieldAttribute> {
287        self.attributes.iter().find(|attr| {
288            matches!(
289                (kind, attr),
290                ("id", FieldAttribute::Id)
291                    | ("unique", FieldAttribute::Unique)
292                    | ("default", FieldAttribute::Default(_, _))
293                    | ("map", FieldAttribute::Map(_))
294                    | ("relation", FieldAttribute::Relation { .. })
295                    | ("check", FieldAttribute::Check { .. })
296            )
297        })
298    }
299
300    /// Checks if this field has a @relation attribute.
301    pub fn has_relation_attribute(&self) -> bool {
302        self.attributes
303            .iter()
304            .any(|attr| matches!(attr, FieldAttribute::Relation { .. }))
305    }
306
307    /// Gets the physical column name from @map attribute, or the field name.
308    pub fn column_name(&self) -> &str {
309        self.attributes
310            .iter()
311            .find_map(|attr| match attr {
312                FieldAttribute::Map(name) => Some(name.as_str()),
313                _ => None,
314            })
315            .unwrap_or(&self.name.value)
316    }
317}
318
319/// Field type modifiers (optional, not-null, or array).
320#[derive(Debug, Clone, Copy, PartialEq, Eq)]
321pub enum FieldModifier {
322    /// No modifier (required field).
323    None,
324    /// Optional field (`?`).
325    Optional,
326    /// Explicit not-null field (`!`).
327    NotNull,
328    /// Array field (`[]`).
329    Array,
330}
331
332/// A field type in a model.
333#[derive(Debug, Clone, PartialEq)]
334pub enum FieldType {
335    /// String type.
336    String,
337    /// Boolean type.
338    Boolean,
339    /// Int type (32-bit).
340    Int,
341    /// BigInt type (64-bit).
342    BigInt,
343    /// Float type.
344    Float,
345    /// Decimal type with precision and scale.
346    Decimal {
347        /// Precision (total digits).
348        precision: u32,
349        /// Scale (digits after decimal point).
350        scale: u32,
351    },
352    /// DateTime type.
353    DateTime,
354    /// Bytes type.
355    Bytes,
356    /// JSON type.
357    Json,
358    /// UUID type.
359    Uuid,
360    /// JSONB type (PostgreSQL only).
361    Jsonb,
362    /// XML type (PostgreSQL only).
363    Xml,
364    /// Fixed-length character type.
365    Char {
366        /// Column length.
367        length: u32,
368    },
369    /// Variable-length character type.
370    VarChar {
371        /// Maximum column length.
372        length: u32,
373    },
374    /// User-defined type (model or enum reference).
375    UserType(String),
376}
377
378impl fmt::Display for FieldType {
379    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
380        match self {
381            FieldType::String => write!(f, "String"),
382            FieldType::Boolean => write!(f, "Boolean"),
383            FieldType::Int => write!(f, "Int"),
384            FieldType::BigInt => write!(f, "BigInt"),
385            FieldType::Float => write!(f, "Float"),
386            FieldType::Decimal { precision, scale } => {
387                write!(f, "Decimal({}, {})", precision, scale)
388            }
389            FieldType::DateTime => write!(f, "DateTime"),
390            FieldType::Bytes => write!(f, "Bytes"),
391            FieldType::Json => write!(f, "Json"),
392            FieldType::Uuid => write!(f, "Uuid"),
393            FieldType::Jsonb => write!(f, "Jsonb"),
394            FieldType::Xml => write!(f, "Xml"),
395            FieldType::Char { length } => write!(f, "Char({})", length),
396            FieldType::VarChar { length } => write!(f, "VarChar({})", length),
397            FieldType::UserType(name) => write!(f, "{}", name),
398        }
399    }
400}
401
402/// Storage strategy for array fields on databases without native array support.
403#[derive(Debug, Clone, Copy, PartialEq, Eq)]
404pub enum StorageStrategy {
405    /// Native database array type (PostgreSQL).
406    Native,
407    /// JSON-serialized array storage (MySQL, SQLite).
408    Json,
409}
410
411/// Whether a computed column is physically stored or computed on every read.
412#[derive(Debug, Clone, Copy, PartialEq, Eq)]
413pub enum ComputedKind {
414    /// Column value persisted on disk (PostgreSQL, MySQL, SQLite).
415    Stored,
416    /// Column value computed on read, not stored (MySQL and SQLite only).
417    Virtual,
418}
419
420/// A field-level attribute (@id, @unique, etc.).
421#[derive(Debug, Clone, PartialEq)]
422pub enum FieldAttribute {
423    /// @id attribute.
424    Id,
425    /// @unique attribute.
426    Unique,
427    /// @default(value) attribute.
428    /// The `Span` covers the full `@default(...)` token range.
429    Default(Expr, Span),
430    /// @map("name") attribute.
431    Map(String),
432    /// @store(json) attribute for array storage strategy.
433    Store {
434        /// Storage strategy (currently only "json" supported).
435        strategy: StorageStrategy,
436        /// Span of the entire attribute.
437        span: Span,
438    },
439    /// @relation(...) attribute.
440    Relation {
441        /// name: "relationName" (optional, required for multiple relations)
442        name: Option<String>,
443        /// fields: [field1, field2]
444        fields: Option<Vec<Ident>>,
445        /// references: [field1, field2]
446        references: Option<Vec<Ident>>,
447        /// onDelete: Cascade | SetNull | ...
448        on_delete: Option<ReferentialAction>,
449        /// onUpdate: Cascade | SetNull | ...
450        on_update: Option<ReferentialAction>,
451        /// Span of the entire attribute.
452        span: Span,
453    },
454    /// @updatedAt — auto-set to current timestamp on every write.
455    UpdatedAt {
456        /// Span covering `@updatedAt`.
457        span: Span,
458    },
459    /// @computed(expr, Stored | Virtual) — database-generated column.
460    Computed {
461        /// Parsed SQL expression (e.g. `price * quantity`).
462        expr: crate::sql_expr::SqlExpr,
463        /// Whether the value is stored on disk or computed on every read.
464        kind: ComputedKind,
465        /// Span of the entire `@computed(...)` attribute.
466        span: Span,
467    },
468    /// @check(bool_expr) — column-level CHECK constraint.
469    Check {
470        /// Parsed boolean expression (e.g. `age >= 0 AND age <= 150`).
471        expr: crate::bool_expr::BoolExpr,
472        /// Span of the entire `@check(...)` attribute.
473        span: Span,
474    },
475}
476
477/// Referential actions for foreign key constraints.
478#[derive(Debug, Clone, Copy, PartialEq, Eq)]
479pub enum ReferentialAction {
480    /// CASCADE action.
481    Cascade,
482    /// RESTRICT action.
483    Restrict,
484    /// NO ACTION.
485    NoAction,
486    /// SET NULL.
487    SetNull,
488    /// SET DEFAULT.
489    SetDefault,
490}
491
492impl fmt::Display for ReferentialAction {
493    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
494        match self {
495            ReferentialAction::Cascade => write!(f, "Cascade"),
496            ReferentialAction::Restrict => write!(f, "Restrict"),
497            ReferentialAction::NoAction => write!(f, "NoAction"),
498            ReferentialAction::SetNull => write!(f, "SetNull"),
499            ReferentialAction::SetDefault => write!(f, "SetDefault"),
500        }
501    }
502}
503
504/// A model-level attribute (@@map, @@id, etc.).
505#[derive(Debug, Clone, PartialEq)]
506pub enum ModelAttribute {
507    /// @@map("table_name") attribute.
508    Map(String),
509    /// @@id([field1, field2]) composite primary key.
510    Id(Vec<Ident>),
511    /// @@unique([field1, field2]) composite unique constraint.
512    Unique(Vec<Ident>),
513    /// @@index([field1, field2], type: Hash, name: "idx_name", map: "db_idx") index.
514    Index {
515        /// Fields that form the index key.
516        fields: Vec<Ident>,
517        /// Optional index type (`type:` argument). `None` -> let the DBMS choose.
518        index_type: Option<Ident>,
519        /// Optional logical name (`name:` argument).
520        name: Option<String>,
521        /// Optional physical DB name (`map:` argument).
522        map: Option<String>,
523    },
524    /// @@check(bool_expr) — table-level CHECK constraint.
525    Check {
526        /// Parsed boolean expression (e.g. `start_date < end_date`).
527        expr: crate::bool_expr::BoolExpr,
528        /// Span of the entire `@@check(...)` attribute.
529        span: Span,
530    },
531}
532
533/// An enum block declaration.
534///
535/// # Example
536///
537/// ```prisma
538/// enum Role {
539///   USER
540///   ADMIN
541/// }
542/// ```
543#[derive(Debug, Clone, PartialEq)]
544pub struct EnumDecl {
545    /// The enum name (e.g., "Role").
546    pub name: Ident,
547    /// Enum variants.
548    pub variants: Vec<EnumVariant>,
549    /// Span covering the entire enum block.
550    pub span: Span,
551}
552
553/// A composite type block declaration.
554///
555/// Composite types define named struct-like types that can be embedded in models.
556/// On PostgreSQL they map to native composite types; on MySQL/SQLite they are
557/// serialised to JSON (`@store(Json)` is required on the model field).
558///
559/// # Example
560///
561/// ```prisma
562/// type Address {
563///   street String
564///   city   String
565///   zip    String
566/// }
567/// ```
568#[derive(Debug, Clone, PartialEq)]
569pub struct TypeDecl {
570    /// The type name (e.g., "Address").
571    pub name: Ident,
572    /// Field declarations (scalars, enums, and arrays — no relations).
573    pub fields: Vec<FieldDecl>,
574    /// Span covering the entire type block.
575    pub span: Span,
576}
577
578impl TypeDecl {
579    /// Finds a field by name.
580    pub fn find_field(&self, name: &str) -> Option<&FieldDecl> {
581        self.fields.iter().find(|f| f.name.value == name)
582    }
583}
584
585/// An enum variant.
586#[derive(Debug, Clone, PartialEq)]
587pub struct EnumVariant {
588    /// The variant name.
589    pub name: Ident,
590    /// Span covering the variant.
591    pub span: Span,
592}
593
594/// An identifier.
595#[derive(Debug, Clone, PartialEq, Eq, Hash)]
596pub struct Ident {
597    /// The identifier value.
598    pub value: String,
599    /// Span of the identifier.
600    pub span: Span,
601}
602
603impl Ident {
604    /// Creates a new identifier.
605    pub fn new(value: String, span: Span) -> Self {
606        Self { value, span }
607    }
608}
609
610impl fmt::Display for Ident {
611    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
612        write!(f, "{}", self.value)
613    }
614}
615
616/// An expression (used in attribute arguments).
617#[derive(Debug, Clone, PartialEq)]
618pub enum Expr {
619    /// A literal value.
620    Literal(Literal),
621    /// A function call: `name(arg1, arg2, ...)`.
622    FunctionCall {
623        /// Function name.
624        name: Ident,
625        /// Arguments.
626        args: Vec<Expr>,
627        /// Span of the entire call.
628        span: Span,
629    },
630    /// An array: `[item1, item2, ...]`.
631    Array {
632        /// Array elements.
633        elements: Vec<Expr>,
634        /// Span of the entire array.
635        span: Span,
636    },
637    /// A named argument: `name: value`.
638    NamedArg {
639        /// Argument name.
640        name: Ident,
641        /// Argument value.
642        value: Box<Expr>,
643        /// Span of the entire named argument.
644        span: Span,
645    },
646    /// An identifier reference.
647    Ident(Ident),
648}
649
650impl Expr {
651    /// Returns the span of this expression.
652    pub fn span(&self) -> Span {
653        match self {
654            Expr::Literal(lit) => lit.span(),
655            Expr::FunctionCall { span, .. } => *span,
656            Expr::Array { span, .. } => *span,
657            Expr::NamedArg { span, .. } => *span,
658            Expr::Ident(ident) => ident.span,
659        }
660    }
661}
662
663/// A literal value.
664#[derive(Debug, Clone, PartialEq)]
665pub enum Literal {
666    /// String literal.
667    String(String, Span),
668    /// Number literal (stored as string, can be int or float).
669    Number(String, Span),
670    /// Boolean literal.
671    Boolean(bool, Span),
672}
673
674impl Literal {
675    /// Returns the span of this literal.
676    pub fn span(&self) -> Span {
677        match self {
678            Literal::String(_, span) => *span,
679            Literal::Number(_, span) => *span,
680            Literal::Boolean(_, span) => *span,
681        }
682    }
683}
684
685#[cfg(test)]
686mod tests {
687    use super::*;
688
689    #[test]
690    fn test_field_modifier() {
691        assert_eq!(FieldModifier::None, FieldModifier::None);
692        assert_ne!(FieldModifier::Optional, FieldModifier::Array);
693    }
694
695    #[test]
696    fn test_field_type_display() {
697        assert_eq!(FieldType::String.to_string(), "String");
698        assert_eq!(FieldType::Int.to_string(), "Int");
699        assert_eq!(
700            FieldType::Decimal {
701                precision: 10,
702                scale: 2
703            }
704            .to_string(),
705            "Decimal(10, 2)"
706        );
707    }
708
709    #[test]
710    fn test_ident() {
711        let ident = Ident::new("test".to_string(), Span::new(0, 4));
712        assert_eq!(ident.value, "test");
713        assert_eq!(ident.to_string(), "test");
714    }
715
716    #[test]
717    fn test_referential_action_display() {
718        assert_eq!(ReferentialAction::Cascade.to_string(), "Cascade");
719        assert_eq!(ReferentialAction::SetNull.to_string(), "SetNull");
720    }
721
722    #[test]
723    fn test_model_table_name() {
724        let model = ModelDecl {
725            name: Ident::new("User".to_string(), Span::new(0, 4)),
726            fields: vec![],
727            attributes: vec![ModelAttribute::Map("users".to_string())],
728            span: Span::new(0, 10),
729        };
730        assert_eq!(model.table_name(), "users");
731    }
732
733    #[test]
734    fn test_model_table_name_default() {
735        let model = ModelDecl {
736            name: Ident::new("User".to_string(), Span::new(0, 4)),
737            fields: vec![],
738            attributes: vec![],
739            span: Span::new(0, 10),
740        };
741        assert_eq!(model.table_name(), "User");
742    }
743
744    #[test]
745    fn test_field_column_name() {
746        let field = FieldDecl {
747            name: Ident::new("userId".to_string(), Span::new(0, 6)),
748            field_type: FieldType::Int,
749            modifier: FieldModifier::None,
750            attributes: vec![FieldAttribute::Map("user_id".to_string())],
751            span: Span::new(0, 20),
752        };
753        assert_eq!(field.column_name(), "user_id");
754    }
755
756    #[test]
757    fn test_schema_helpers() {
758        let schema = Schema {
759            declarations: vec![
760                Declaration::Model(ModelDecl {
761                    name: Ident::new("User".to_string(), Span::new(0, 4)),
762                    fields: vec![],
763                    attributes: vec![],
764                    span: Span::new(0, 10),
765                }),
766                Declaration::Enum(EnumDecl {
767                    name: Ident::new("Role".to_string(), Span::new(0, 4)),
768                    variants: vec![],
769                    span: Span::new(0, 10),
770                }),
771            ],
772            span: Span::new(0, 100),
773        };
774
775        assert_eq!(schema.models().count(), 1);
776        assert_eq!(schema.enums().count(), 1);
777        assert!(schema.datasource().is_none());
778    }
779}