Skip to main content

vitrail_pg_core/
schema.rs

1use std::collections::{HashMap, HashSet};
2
3use heck::ToUpperCamelCase;
4
5use crate::validation::{ValidationError, ValidationErrors, ValidationLocation};
6
7/// Schema definition for `vitrail-pg`.
8#[derive(Clone, Debug, Default, Eq, PartialEq)]
9pub struct Schema {
10    models: Vec<Model>,
11    external_tables: Vec<String>,
12}
13
14impl Schema {
15    pub fn builder() -> SchemaBuilder {
16        SchemaBuilder::new()
17    }
18
19    pub fn models(&self) -> &[Model] {
20        &self.models
21    }
22
23    pub fn external_tables(&self) -> &[String] {
24        &self.external_tables
25    }
26
27    pub fn model(&self, name: &str) -> Option<&Model> {
28        self.models.iter().find(|model| model.name == name)
29    }
30
31    pub(crate) fn validate(&self) -> Result<(), ValidationErrors> {
32        let mut errors = Vec::new();
33
34        if self.models.is_empty() {
35            errors.push(ValidationError::new(
36                ValidationLocation::Schema,
37                "schema must declare at least one model",
38            ));
39            return Err(ValidationErrors::from(errors));
40        }
41
42        let mut seen_models = HashMap::<&str, usize>::new();
43
44        for (index, model) in self.models.iter().enumerate() {
45            if let Some(previous_index) = seen_models.insert(model.name.as_str(), index) {
46                errors.push(ValidationError::new(
47                    ValidationLocation::Model {
48                        model: model.name.clone(),
49                    },
50                    format!("duplicate model `{}`", model.name),
51                ));
52                errors.push(ValidationError::new(
53                    ValidationLocation::Model {
54                        model: self.models[previous_index].name.clone(),
55                    },
56                    "first declaration of this model",
57                ));
58            }
59        }
60
61        for model in &self.models {
62            model.validate_shallow(&mut errors);
63        }
64
65        for model in &self.models {
66            model.validate_types(self, &mut errors);
67        }
68
69        for model in &self.models {
70            model.validate_relations(self, &mut errors);
71        }
72
73        let mut seen_external_tables = HashSet::new();
74        for table in &self.external_tables {
75            let normalized = match normalize_external_table_name(table) {
76                Ok(normalized) => normalized,
77                Err(message) => {
78                    errors.push(ValidationError::new(
79                        ValidationLocation::ExternalTable {
80                            table: table.clone(),
81                        },
82                        message,
83                    ));
84                    continue;
85                }
86            };
87
88            if !seen_external_tables.insert(normalized.clone()) {
89                errors.push(ValidationError::new(
90                    ValidationLocation::ExternalTable {
91                        table: table.clone(),
92                    },
93                    format!("duplicate external table `{}`", table),
94                ));
95            }
96
97            if self.model(&normalized).is_some() {
98                errors.push(ValidationError::new(
99                    ValidationLocation::ExternalTable {
100                        table: table.clone(),
101                    },
102                    format!(
103                        "external table `{}` conflicts with managed model `{}`",
104                        table, normalized
105                    ),
106                ));
107            }
108        }
109
110        if errors.is_empty() {
111            Ok(())
112        } else {
113            Err(ValidationErrors::from(errors))
114        }
115    }
116
117    pub(crate) fn resolve_model(&self, requested: &str) -> Resolution<'_> {
118        let mut matches = Vec::new();
119
120        for model in &self.models {
121            if requested == model.name || requested == model.name.to_upper_camel_case() {
122                matches.push(model);
123            }
124        }
125
126        match matches.len() {
127            0 => Resolution::NotFound,
128            1 => Resolution::Found(matches[0]),
129            _ => Resolution::Ambiguous(matches),
130        }
131    }
132}
133
134#[derive(Clone, Debug, Default, Eq, PartialEq)]
135pub struct SchemaBuilder {
136    models: Vec<Model>,
137    external_tables: Vec<String>,
138}
139
140impl SchemaBuilder {
141    pub fn new() -> Self {
142        Self::default()
143    }
144
145    pub fn model(mut self, model: Model) -> Self {
146        self.models.push(model);
147        self
148    }
149
150    pub fn models(mut self, models: Vec<Model>) -> Self {
151        self.models = models;
152        self
153    }
154
155    pub fn external_table(mut self, table: impl Into<String>) -> Self {
156        self.external_tables.push(table.into());
157        self
158    }
159
160    pub fn external_tables(mut self, tables: Vec<String>) -> Self {
161        self.external_tables = tables;
162        self
163    }
164
165    pub fn build(self) -> Result<Schema, ValidationErrors> {
166        let schema = Schema {
167            models: self.models,
168            external_tables: self.external_tables,
169        };
170        schema.validate()?;
171        Ok(schema)
172    }
173}
174
175fn normalize_external_table_name(table: &str) -> Result<String, String> {
176    if table.is_empty() {
177        return Err("external table name must not be empty".to_owned());
178    }
179
180    if let Some((schema, table_name)) = table.split_once('.') {
181        if schema != "public" {
182            return Err(format!(
183                "external table `{}` must target the `public` schema",
184                table
185            ));
186        }
187
188        if table_name.is_empty() {
189            return Err(format!(
190                "external table `{}` must include a table name",
191                table
192            ));
193        }
194
195        return Ok(table_name.to_owned());
196    }
197
198    Ok(table.to_owned())
199}
200
201#[derive(Clone, Debug, Eq, PartialEq)]
202pub struct Model {
203    name: String,
204    fields: Vec<Field>,
205    attributes: Vec<ModelAttribute>,
206}
207
208impl Model {
209    pub fn builder(name: impl Into<String>) -> ModelBuilder {
210        ModelBuilder::new(name)
211    }
212
213    pub fn name(&self) -> &str {
214        &self.name
215    }
216
217    pub fn fields(&self) -> &[Field] {
218        &self.fields
219    }
220
221    pub fn attributes(&self) -> &[ModelAttribute] {
222        &self.attributes
223    }
224
225    pub fn primary_key_columns(&self) -> Vec<&str> {
226        if let Some(primary_key) = self.primary_key_attribute() {
227            return primary_key.fields.iter().map(String::as_str).collect();
228        }
229
230        self.fields
231            .iter()
232            .filter(|field| field.has_id())
233            .map(|field| field.name.as_str())
234            .collect()
235    }
236
237    pub fn unique_column_sets(&self) -> Vec<Vec<&str>> {
238        self.attributes
239            .iter()
240            .filter_map(|attribute| match attribute {
241                ModelAttribute::Unique(unique) => {
242                    Some(unique.fields.iter().map(String::as_str).collect::<Vec<_>>())
243                }
244                ModelAttribute::Id(_) | ModelAttribute::Index(_) => None,
245            })
246            .collect()
247    }
248
249    pub fn index_column_sets(&self) -> Vec<Vec<&str>> {
250        self.attributes
251            .iter()
252            .filter_map(|attribute| match attribute {
253                ModelAttribute::Index(index) => {
254                    Some(index.fields.iter().map(String::as_str).collect::<Vec<_>>())
255                }
256                ModelAttribute::Id(_) | ModelAttribute::Unique(_) => None,
257            })
258            .collect()
259    }
260
261    fn primary_key_attribute(&self) -> Option<&ModelPrimaryKeyAttribute> {
262        self.attributes
263            .iter()
264            .find_map(|attribute| match attribute {
265                ModelAttribute::Id(primary_key) => Some(primary_key),
266                ModelAttribute::Unique(_) | ModelAttribute::Index(_) => None,
267            })
268    }
269
270    fn validate_shallow(&self, errors: &mut Vec<ValidationError>) {
271        if self.fields.is_empty() {
272            errors.push(ValidationError::new(
273                ValidationLocation::Model {
274                    model: self.name.clone(),
275                },
276                "model must declare at least one field",
277            ));
278            return;
279        }
280
281        let mut seen_fields = HashMap::<&str, usize>::new();
282        let mut id_fields = Vec::new();
283        let mut seen_model_id = false;
284
285        for attribute in &self.attributes {
286            match attribute {
287                ModelAttribute::Id(primary_key) => {
288                    if seen_model_id {
289                        errors.push(ValidationError::new(
290                            ValidationLocation::ModelAttribute {
291                                model: self.name.clone(),
292                                attribute: "@@id".to_owned(),
293                            },
294                            "duplicate `@@id` attribute",
295                        ));
296                    } else {
297                        seen_model_id = true;
298                    }
299
300                    primary_key.validate(self, errors);
301                }
302                ModelAttribute::Unique(unique) => unique.validate(self, errors),
303                ModelAttribute::Index(index) => index.validate(self, errors),
304            }
305        }
306
307        for (index, field) in self.fields.iter().enumerate() {
308            if let Some(previous_index) = seen_fields.insert(field.name.as_str(), index) {
309                errors.push(ValidationError::new(
310                    ValidationLocation::Field {
311                        model: self.name.clone(),
312                        field: field.name.clone(),
313                    },
314                    format!("duplicate field `{}` in model `{}`", field.name, self.name),
315                ));
316                errors.push(ValidationError::new(
317                    ValidationLocation::Field {
318                        model: self.name.clone(),
319                        field: self.fields[previous_index].name.clone(),
320                    },
321                    "first declaration of this field",
322                ));
323            }
324
325            field.validate_attributes(&self.name, errors);
326
327            if field.has_id() {
328                id_fields.push(field);
329            }
330        }
331
332        let has_model_id = self.primary_key_attribute().is_some();
333
334        match (id_fields.len(), has_model_id) {
335            (0, false) => errors.push(ValidationError::new(
336                ValidationLocation::Model {
337                    model: self.name.clone(),
338                },
339                format!(
340                    "model `{}` must declare exactly one primary key using `@id` or `@@id`",
341                    self.name
342                ),
343            )),
344            (0, true) | (1, false) => {}
345            (_, true) if !id_fields.is_empty() => {
346                errors.push(ValidationError::new(
347                    ValidationLocation::Model {
348                        model: self.name.clone(),
349                    },
350                    format!(
351                        "model `{}` cannot mix field-level `@id` with model-level `@@id`",
352                        self.name
353                    ),
354                ));
355
356                for field in id_fields {
357                    errors.push(ValidationError::new(
358                        ValidationLocation::Field {
359                            model: self.name.clone(),
360                            field: field.name.clone(),
361                        },
362                        "remove this `@id` because the model already declares `@@id`",
363                    ));
364                }
365            }
366            _ => {
367                errors.push(ValidationError::new(
368                    ValidationLocation::Model {
369                        model: self.name.clone(),
370                    },
371                    format!(
372                        "model `{}` declares multiple `@id` fields; use `@@id([...])` for a compound primary key",
373                        self.name
374                    ),
375                ));
376
377                for field in id_fields.into_iter().skip(1) {
378                    errors.push(ValidationError::new(
379                        ValidationLocation::Field {
380                            model: self.name.clone(),
381                            field: field.name.clone(),
382                        },
383                        "extra `@id` field declared here",
384                    ));
385                }
386            }
387        }
388    }
389
390    fn validate_types(&self, schema: &Schema, errors: &mut Vec<ValidationError>) {
391        for field in &self.fields {
392            field.validate_type(schema, &self.name, errors);
393        }
394    }
395
396    fn validate_relations(&self, schema: &Schema, errors: &mut Vec<ValidationError>) {
397        for field in &self.fields {
398            if field.kind().is_scalar() {
399                continue;
400            }
401
402            let Some(relation) = field.relation() else {
403                self.validate_inferred_relation_cardinality(schema, field, errors);
404                continue;
405            };
406
407            let target_model = match schema.resolve_model(field.ty.name()) {
408                Resolution::Found(model) => model,
409                Resolution::NotFound => {
410                    errors.push(ValidationError::new(
411                        ValidationLocation::FieldType {
412                            model: self.name.clone(),
413                            field: field.name.clone(),
414                            ty: field.ty.name().to_owned(),
415                        },
416                        format!(
417                            "unknown relation target model `{}` for field `{}`",
418                            field.ty.name(),
419                            field.name
420                        ),
421                    ));
422                    continue;
423                }
424                Resolution::Ambiguous(models) => {
425                    let candidates = models
426                        .into_iter()
427                        .map(|model| format!("`{}`", model.name))
428                        .collect::<Vec<_>>()
429                        .join(", ");
430
431                    errors.push(ValidationError::new(
432                        ValidationLocation::FieldType {
433                            model: self.name.clone(),
434                            field: field.name.clone(),
435                            ty: field.ty.name().to_owned(),
436                        },
437                        format!(
438                            "ambiguous relation target `{}` for field `{}`; matches {}",
439                            field.ty.name(),
440                            field.name,
441                            candidates
442                        ),
443                    ));
444                    continue;
445                }
446            };
447
448            if relation.fields.is_empty() {
449                errors.push(ValidationError::new(
450                    ValidationLocation::RelationAttribute {
451                        model: self.name.clone(),
452                        field: field.name.clone(),
453                    },
454                    "`@relation(fields: [...])` cannot be empty",
455                ));
456            }
457
458            if relation.references.is_empty() {
459                errors.push(ValidationError::new(
460                    ValidationLocation::RelationAttribute {
461                        model: self.name.clone(),
462                        field: field.name.clone(),
463                    },
464                    "`@relation(references: [...])` cannot be empty",
465                ));
466            }
467
468            if relation.fields.len() != relation.references.len() {
469                errors.push(ValidationError::new(
470                    ValidationLocation::RelationAttribute {
471                        model: self.name.clone(),
472                        field: field.name.clone(),
473                    },
474                    "`@relation(fields: [...], references: [...])` must declare the same number of local and referenced fields",
475                ));
476            }
477
478            let mut local_seen = HashSet::new();
479            for local in &relation.fields {
480                if !local_seen.insert(local.as_str()) {
481                    errors.push(ValidationError::new(
482                        ValidationLocation::RelationField {
483                            model: self.name.clone(),
484                            field: field.name.clone(),
485                            relation_field: local.clone(),
486                        },
487                        format!("duplicate relation field `{}`", local),
488                    ));
489                }
490
491                match self.field_named(local) {
492                    Some(local_field) => {
493                        if !local_field.kind().is_scalar() {
494                            errors.push(ValidationError::new(
495                                ValidationLocation::RelationField {
496                                    model: self.name.clone(),
497                                    field: field.name.clone(),
498                                    relation_field: local.clone(),
499                                },
500                                format!(
501                                    "relation field list can only reference scalar fields, but `{}` is a relation field",
502                                    local
503                                ),
504                            ));
505                        }
506                    }
507                    None => errors.push(ValidationError::new(
508                        ValidationLocation::RelationField {
509                            model: self.name.clone(),
510                            field: field.name.clone(),
511                            relation_field: local.clone(),
512                        },
513                        format!(
514                            "unknown local field `{}` referenced by relation field `{}`",
515                            local, field.name
516                        ),
517                    )),
518                }
519            }
520
521            let mut remote_seen = HashSet::new();
522            for referenced in &relation.references {
523                if !remote_seen.insert(referenced.as_str()) {
524                    errors.push(ValidationError::new(
525                        ValidationLocation::RelationReference {
526                            model: self.name.clone(),
527                            field: field.name.clone(),
528                            referenced_field: referenced.clone(),
529                            target_model: target_model.name.clone(),
530                        },
531                        format!("duplicate referenced field `{}`", referenced),
532                    ));
533                }
534
535                match target_model.field_named(referenced) {
536                    Some(target_field) => {
537                        if !target_field.kind().is_scalar() {
538                            errors.push(ValidationError::new(
539                                ValidationLocation::RelationReference {
540                                    model: self.name.clone(),
541                                    field: field.name.clone(),
542                                    referenced_field: referenced.clone(),
543                                    target_model: target_model.name.clone(),
544                                },
545                                format!(
546                                    "relation references can only target scalar fields, but `{}` on model `{}` is a relation field",
547                                    referenced, target_model.name
548                                ),
549                            ));
550                        }
551                    }
552                    None => errors.push(ValidationError::new(
553                        ValidationLocation::RelationReference {
554                            model: self.name.clone(),
555                            field: field.name.clone(),
556                            referenced_field: referenced.clone(),
557                            target_model: target_model.name.clone(),
558                        },
559                        format!(
560                            "unknown referenced field `{}` on model `{}`",
561                            referenced, target_model.name
562                        ),
563                    )),
564                }
565            }
566        }
567    }
568
569    fn validate_inferred_relation_cardinality(
570        &self,
571        schema: &Schema,
572        field: &Field,
573        errors: &mut Vec<ValidationError>,
574    ) {
575        let target_model = match schema.resolve_model(field.ty.name()) {
576            Resolution::Found(model) => model,
577            Resolution::NotFound | Resolution::Ambiguous(_) => return,
578        };
579
580        let Some(reverse_field) = target_model.fields.iter().find(|candidate| {
581            candidate.ty.name().eq_ignore_ascii_case(&self.name) && candidate.relation().is_some()
582        }) else {
583            return;
584        };
585
586        let reverse_relation = reverse_field
587            .relation()
588            .expect("reverse relation existence checked above");
589
590        if target_model.has_unique_constraint_on_fields(reverse_relation.fields())
591            && (!field.ty.is_optional() || field.ty.is_many())
592        {
593            errors.push(ValidationError::new(
594                ValidationLocation::FieldType {
595                    model: self.name.clone(),
596                    field: field.name.clone(),
597                    ty: field.ty.name().to_owned(),
598                },
599                format!(
600                    "relation back-link field `{}.{}` must be optional because `{}.{}` references it through unique relation fields; use `{}?`",
601                    self.name,
602                    field.name,
603                    target_model.name,
604                    reverse_field.name,
605                    field.ty.name()
606                ),
607            ));
608        }
609    }
610
611    fn has_unique_constraint_on_fields(&self, field_names: &[String]) -> bool {
612        if field_names.len() == 1 {
613            let field_name = &field_names[0];
614
615            if self
616                .field_named(field_name)
617                .is_some_and(|field| field.has_id() || field.has_unique())
618            {
619                return true;
620            }
621        }
622
623        let matches_fields = |candidate_fields: Vec<&str>| {
624            candidate_fields.len() == field_names.len()
625                && candidate_fields
626                    .iter()
627                    .zip(field_names)
628                    .all(|(left, right)| *left == right)
629        };
630
631        matches_fields(self.primary_key_columns())
632            || self.unique_column_sets().into_iter().any(matches_fields)
633    }
634
635    pub fn field_named(&self, name: &str) -> Option<&Field> {
636        self.fields.iter().find(|field| field.name == name)
637    }
638}
639
640#[derive(Clone, Debug, Eq, PartialEq)]
641pub struct ModelBuilder {
642    name: String,
643    fields: Vec<Field>,
644    attributes: Vec<ModelAttribute>,
645}
646
647impl ModelBuilder {
648    pub fn new(name: impl Into<String>) -> Self {
649        Self {
650            name: name.into(),
651            fields: Vec::new(),
652            attributes: Vec::new(),
653        }
654    }
655
656    pub fn field(mut self, field: Field) -> Self {
657        self.fields.push(field);
658        self
659    }
660
661    pub fn fields(mut self, fields: Vec<Field>) -> Self {
662        self.fields = fields;
663        self
664    }
665
666    pub fn attribute(mut self, attribute: ModelAttribute) -> Self {
667        self.attributes.push(attribute);
668        self
669    }
670
671    pub fn attributes(mut self, attributes: Vec<ModelAttribute>) -> Self {
672        self.attributes = attributes;
673        self
674    }
675
676    pub fn build(self) -> Result<Model, ValidationErrors> {
677        let model = Model {
678            name: self.name,
679            fields: self.fields,
680            attributes: self.attributes,
681        };
682
683        let mut errors = Vec::new();
684        model.validate_shallow(&mut errors);
685
686        if errors.is_empty() {
687            Ok(model)
688        } else {
689            Err(ValidationErrors::from(errors))
690        }
691    }
692}
693
694#[derive(Clone, Debug, Eq, PartialEq)]
695pub struct Field {
696    name: String,
697    ty: FieldType,
698    attributes: Vec<Attribute>,
699}
700
701impl Field {
702    pub fn builder(name: impl Into<String>, ty: FieldType) -> FieldBuilder {
703        FieldBuilder::new(name, ty)
704    }
705
706    pub fn name(&self) -> &str {
707        &self.name
708    }
709
710    pub fn ty(&self) -> &FieldType {
711        &self.ty
712    }
713
714    pub fn attributes(&self) -> &[Attribute] {
715        &self.attributes
716    }
717
718    pub fn kind(&self) -> FieldKind {
719        match &self.ty {
720            FieldType::Scalar(_) => FieldKind::Scalar,
721            FieldType::Relation { .. } => FieldKind::Relation,
722        }
723    }
724
725    pub fn has_id(&self) -> bool {
726        self.attributes
727            .iter()
728            .any(|attribute| matches!(attribute, Attribute::Id))
729    }
730
731    pub fn has_unique(&self) -> bool {
732        self.attributes
733            .iter()
734            .any(|attribute| matches!(attribute, Attribute::Unique))
735    }
736
737    pub fn relation(&self) -> Option<&RelationAttribute> {
738        self.attributes
739            .iter()
740            .find_map(|attribute| match attribute {
741                Attribute::Relation(relation) => Some(relation),
742                _ => None,
743            })
744    }
745
746    pub fn rust_type(&self) -> Option<&RustTypeAttribute> {
747        self.attributes
748            .iter()
749            .find_map(|attribute| match attribute {
750                Attribute::RustType(rust_type) => Some(rust_type),
751                _ => None,
752            })
753    }
754
755    pub fn has_db_uuid(&self) -> bool {
756        self.attributes
757            .iter()
758            .any(|attribute| matches!(attribute, Attribute::DbUuid))
759    }
760
761    fn validate_attributes(&self, model_name: &str, errors: &mut Vec<ValidationError>) {
762        let mut seen_id = false;
763        let mut seen_unique = false;
764        let mut seen_index = false;
765        let mut seen_default = false;
766        let mut seen_relation = false;
767        let mut seen_db_uuid = false;
768        let mut seen_rust_type = false;
769
770        for attribute in &self.attributes {
771            match attribute {
772                Attribute::Id => {
773                    if seen_id {
774                        errors.push(ValidationError::new(
775                            ValidationLocation::Attribute {
776                                model: model_name.to_owned(),
777                                field: self.name.clone(),
778                                attribute: "@id".to_owned(),
779                            },
780                            "duplicate `@id` attribute",
781                        ));
782                    } else {
783                        seen_id = true;
784                    }
785
786                    if !self.kind().is_scalar() {
787                        errors.push(ValidationError::new(
788                            ValidationLocation::Attribute {
789                                model: model_name.to_owned(),
790                                field: self.name.clone(),
791                                attribute: "@id".to_owned(),
792                            },
793                            "`@id` can only be used on scalar fields",
794                        ));
795                    }
796                }
797                Attribute::Unique => {
798                    if seen_unique {
799                        errors.push(ValidationError::new(
800                            ValidationLocation::Attribute {
801                                model: model_name.to_owned(),
802                                field: self.name.clone(),
803                                attribute: "@unique".to_owned(),
804                            },
805                            "duplicate `@unique` attribute",
806                        ));
807                    } else {
808                        seen_unique = true;
809                    }
810
811                    if !self.kind().is_scalar() {
812                        errors.push(ValidationError::new(
813                            ValidationLocation::Attribute {
814                                model: model_name.to_owned(),
815                                field: self.name.clone(),
816                                attribute: "@unique".to_owned(),
817                            },
818                            "`@unique` can only be used on scalar fields",
819                        ));
820                    }
821                }
822                Attribute::Index => {
823                    if seen_index {
824                        errors.push(ValidationError::new(
825                            ValidationLocation::Attribute {
826                                model: model_name.to_owned(),
827                                field: self.name.clone(),
828                                attribute: "@index".to_owned(),
829                            },
830                            "duplicate `@index` attribute",
831                        ));
832                    } else {
833                        seen_index = true;
834                    }
835
836                    if !self.kind().is_scalar() {
837                        errors.push(ValidationError::new(
838                            ValidationLocation::Attribute {
839                                model: model_name.to_owned(),
840                                field: self.name.clone(),
841                                attribute: "@index".to_owned(),
842                            },
843                            "`@index` can only be used on scalar fields",
844                        ));
845                    }
846                }
847                Attribute::Default(default) => {
848                    if seen_default {
849                        errors.push(ValidationError::new(
850                            ValidationLocation::Attribute {
851                                model: model_name.to_owned(),
852                                field: self.name.clone(),
853                                attribute: "@default".to_owned(),
854                            },
855                            "duplicate `@default` attribute",
856                        ));
857                    } else {
858                        seen_default = true;
859                    }
860
861                    self.validate_default(model_name, default, errors);
862                }
863                Attribute::Relation(relation) => {
864                    if seen_relation {
865                        errors.push(ValidationError::new(
866                            ValidationLocation::Attribute {
867                                model: model_name.to_owned(),
868                                field: self.name.clone(),
869                                attribute: "@relation".to_owned(),
870                            },
871                            "duplicate `@relation` attribute",
872                        ));
873                    } else {
874                        seen_relation = true;
875                    }
876
877                    if self.kind().is_scalar() {
878                        errors.push(ValidationError::new(
879                            ValidationLocation::Field {
880                                model: model_name.to_owned(),
881                                field: self.name.clone(),
882                            },
883                            format!("scalar field `{}` cannot declare `@relation`", self.name),
884                        ));
885                    }
886
887                    if relation.fields.is_empty() || relation.references.is_empty() {
888                        continue;
889                    }
890                }
891                Attribute::DbUuid => {
892                    if seen_db_uuid {
893                        errors.push(ValidationError::new(
894                            ValidationLocation::Attribute {
895                                model: model_name.to_owned(),
896                                field: self.name.clone(),
897                                attribute: "@db.Uuid".to_owned(),
898                            },
899                            "duplicate `@db.Uuid` attribute",
900                        ));
901                    } else {
902                        seen_db_uuid = true;
903                    }
904
905                    if self.kind().is_relation() {
906                        errors.push(ValidationError::new(
907                            ValidationLocation::Attribute {
908                                model: model_name.to_owned(),
909                                field: self.name.clone(),
910                                attribute: "@db.Uuid".to_owned(),
911                            },
912                            "`@db.Uuid` can only be used on scalar fields",
913                        ));
914                    } else if !matches!(
915                        &self.ty,
916                        FieldType::Scalar(ScalarFieldType {
917                            scalar: ScalarType::String,
918                            ..
919                        })
920                    ) {
921                        errors.push(ValidationError::new(
922                            ValidationLocation::Attribute {
923                                model: model_name.to_owned(),
924                                field: self.name.clone(),
925                                attribute: "@db.Uuid".to_owned(),
926                            },
927                            "`@db.Uuid` is only supported on `String` fields",
928                        ));
929                    } else if seen_rust_type {
930                        errors.push(ValidationError::new(
931                            ValidationLocation::Attribute {
932                                model: model_name.to_owned(),
933                                field: self.name.clone(),
934                                attribute: "@db.Uuid".to_owned(),
935                            },
936                            "`@db.Uuid` cannot be combined with `@rust_ty`",
937                        ));
938                    }
939                }
940                Attribute::RustType(_) => {
941                    if seen_rust_type {
942                        errors.push(ValidationError::new(
943                            ValidationLocation::Attribute {
944                                model: model_name.to_owned(),
945                                field: self.name.clone(),
946                                attribute: "@rust_ty".to_owned(),
947                            },
948                            "duplicate `@rust_ty` attribute",
949                        ));
950                    } else {
951                        seen_rust_type = true;
952                    }
953
954                    if self.kind().is_relation() {
955                        errors.push(ValidationError::new(
956                            ValidationLocation::Attribute {
957                                model: model_name.to_owned(),
958                                field: self.name.clone(),
959                                attribute: "@rust_ty".to_owned(),
960                            },
961                            "`@rust_ty` can only be used on scalar fields",
962                        ));
963                    } else if !matches!(
964                        &self.ty,
965                        FieldType::Scalar(ScalarFieldType {
966                            scalar: ScalarType::String,
967                            ..
968                        })
969                    ) {
970                        errors.push(ValidationError::new(
971                            ValidationLocation::Attribute {
972                                model: model_name.to_owned(),
973                                field: self.name.clone(),
974                                attribute: "@rust_ty".to_owned(),
975                            },
976                            "`@rust_ty` is only supported on `String` fields",
977                        ));
978                    } else if seen_db_uuid {
979                        errors.push(ValidationError::new(
980                            ValidationLocation::Attribute {
981                                model: model_name.to_owned(),
982                                field: self.name.clone(),
983                                attribute: "@rust_ty".to_owned(),
984                            },
985                            "`@rust_ty` cannot be combined with `@db.Uuid`",
986                        ));
987                    }
988                }
989            }
990        }
991    }
992
993    fn validate_type(&self, schema: &Schema, model_name: &str, errors: &mut Vec<ValidationError>) {
994        let FieldType::Relation { model, .. } = &self.ty else {
995            return;
996        };
997
998        match schema.resolve_model(model) {
999            Resolution::Found(_) => {}
1000            Resolution::NotFound => errors.push(ValidationError::new(
1001                ValidationLocation::FieldType {
1002                    model: model_name.to_owned(),
1003                    field: self.name.clone(),
1004                    ty: model.clone(),
1005                },
1006                format!(
1007                    "unknown relation target model `{}` for field `{}`",
1008                    model, self.name
1009                ),
1010            )),
1011            Resolution::Ambiguous(models) => {
1012                let candidates = models
1013                    .into_iter()
1014                    .map(|candidate| format!("`{}`", candidate.name))
1015                    .collect::<Vec<_>>()
1016                    .join(", ");
1017
1018                errors.push(ValidationError::new(
1019                    ValidationLocation::FieldType {
1020                        model: model_name.to_owned(),
1021                        field: self.name.clone(),
1022                        ty: model.clone(),
1023                    },
1024                    format!(
1025                        "ambiguous relation target `{}` for field `{}`; matches {}",
1026                        model, self.name, candidates
1027                    ),
1028                ));
1029            }
1030        }
1031    }
1032
1033    fn validate_default(
1034        &self,
1035        model_name: &str,
1036        default: &DefaultAttribute,
1037        errors: &mut Vec<ValidationError>,
1038    ) {
1039        if self.kind().is_relation() {
1040            errors.push(ValidationError::new(
1041                ValidationLocation::Attribute {
1042                    model: model_name.to_owned(),
1043                    field: self.name.clone(),
1044                    attribute: "@default".to_owned(),
1045                },
1046                "`@default` can only be used on scalar fields",
1047            ));
1048            return;
1049        }
1050
1051        match default.function {
1052            DefaultFunction::Autoincrement => {
1053                if self.ty != FieldType::int() && self.ty != FieldType::big_int() {
1054                    errors.push(ValidationError::new(
1055                        ValidationLocation::Attribute {
1056                            model: model_name.to_owned(),
1057                            field: self.name.clone(),
1058                            attribute: "@default".to_owned(),
1059                        },
1060                        "`@default(autoincrement())` is only supported on `Int` and `BigInt` fields",
1061                    ));
1062                }
1063            }
1064            DefaultFunction::Now => {
1065                if self.ty != FieldType::date_time() {
1066                    errors.push(ValidationError::new(
1067                        ValidationLocation::Attribute {
1068                            model: model_name.to_owned(),
1069                            field: self.name.clone(),
1070                            attribute: "@default".to_owned(),
1071                        },
1072                        "`@default(now())` is only supported on `DateTime` fields",
1073                    ));
1074                }
1075            }
1076            DefaultFunction::Other(ref other) => errors.push(ValidationError::new(
1077                ValidationLocation::Attribute {
1078                    model: model_name.to_owned(),
1079                    field: self.name.clone(),
1080                    attribute: "@default".to_owned(),
1081                },
1082                format!(
1083                    "unsupported default function `{}`; expected `autoincrement` or `now`",
1084                    other
1085                ),
1086            )),
1087        }
1088    }
1089}
1090
1091#[derive(Clone, Debug, Eq, PartialEq)]
1092pub struct FieldBuilder {
1093    name: String,
1094    ty: FieldType,
1095    attributes: Vec<Attribute>,
1096}
1097
1098impl FieldBuilder {
1099    pub fn new(name: impl Into<String>, ty: FieldType) -> Self {
1100        Self {
1101            name: name.into(),
1102            ty,
1103            attributes: Vec::new(),
1104        }
1105    }
1106
1107    pub fn attribute(mut self, attribute: Attribute) -> Self {
1108        self.attributes.push(attribute);
1109        self
1110    }
1111
1112    pub fn attributes(mut self, attributes: Vec<Attribute>) -> Self {
1113        self.attributes = attributes;
1114        self
1115    }
1116
1117    pub fn build(self) -> Result<Field, ValidationErrors> {
1118        self.build_for_model("<field>")
1119    }
1120
1121    pub fn build_for_model(self, model_name: &str) -> Result<Field, ValidationErrors> {
1122        let field = Field {
1123            name: self.name,
1124            ty: self.ty,
1125            attributes: self.attributes,
1126        };
1127
1128        let mut errors = Vec::new();
1129        field.validate_attributes(model_name, &mut errors);
1130
1131        if errors.is_empty() {
1132            Ok(field)
1133        } else {
1134            Err(ValidationErrors::from(errors))
1135        }
1136    }
1137}
1138
1139#[derive(Clone, Debug, Eq, PartialEq)]
1140pub enum FieldType {
1141    Scalar(ScalarFieldType),
1142    Relation {
1143        model: String,
1144        optional: bool,
1145        many: bool,
1146    },
1147}
1148
1149impl FieldType {
1150    pub fn scalar(scalar: ScalarType, optional: bool) -> Self {
1151        Self::Scalar(ScalarFieldType { scalar, optional })
1152    }
1153
1154    pub fn relation(model: impl Into<String>, optional: bool, many: bool) -> Self {
1155        Self::Relation {
1156            model: model.into(),
1157            optional,
1158            many,
1159        }
1160    }
1161
1162    pub fn relation_many(model: impl Into<String>) -> Self {
1163        Self::relation(model, false, true)
1164    }
1165
1166    pub fn int() -> Self {
1167        Self::scalar(ScalarType::Int, false)
1168    }
1169
1170    pub fn big_int() -> Self {
1171        Self::scalar(ScalarType::BigInt, false)
1172    }
1173
1174    pub fn string() -> Self {
1175        Self::scalar(ScalarType::String, false)
1176    }
1177
1178    pub fn date_time() -> Self {
1179        Self::scalar(ScalarType::DateTime, false)
1180    }
1181
1182    pub fn is_optional(&self) -> bool {
1183        match self {
1184            FieldType::Scalar(scalar) => scalar.optional,
1185            FieldType::Relation { optional, .. } => *optional,
1186        }
1187    }
1188
1189    pub fn is_many(&self) -> bool {
1190        match self {
1191            FieldType::Scalar(_) => false,
1192            FieldType::Relation { many, .. } => *many,
1193        }
1194    }
1195
1196    pub fn name(&self) -> &str {
1197        match self {
1198            FieldType::Scalar(scalar) => scalar.scalar.as_str(),
1199            FieldType::Relation { model, .. } => model.as_str(),
1200        }
1201    }
1202}
1203
1204#[derive(Clone, Debug, Eq, PartialEq)]
1205pub struct ScalarFieldType {
1206    scalar: ScalarType,
1207    optional: bool,
1208}
1209
1210impl ScalarFieldType {
1211    pub fn scalar(&self) -> ScalarType {
1212        self.scalar
1213    }
1214
1215    pub fn optional(&self) -> bool {
1216        self.optional
1217    }
1218}
1219
1220#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1221pub enum ScalarType {
1222    Int,
1223    BigInt,
1224    String,
1225    Boolean,
1226    DateTime,
1227    Float,
1228    Decimal,
1229    Bytes,
1230    Json,
1231}
1232
1233impl ScalarType {
1234    pub fn as_str(self) -> &'static str {
1235        match self {
1236            ScalarType::Int => "Int",
1237            ScalarType::BigInt => "BigInt",
1238            ScalarType::String => "String",
1239            ScalarType::Boolean => "Boolean",
1240            ScalarType::DateTime => "DateTime",
1241            ScalarType::Float => "Float",
1242            ScalarType::Decimal => "Decimal",
1243            ScalarType::Bytes => "Bytes",
1244            ScalarType::Json => "Json",
1245        }
1246    }
1247}
1248
1249#[derive(Clone, Debug, Eq, PartialEq)]
1250pub enum Attribute {
1251    Id,
1252    Unique,
1253    Index,
1254    Default(DefaultAttribute),
1255    Relation(RelationAttribute),
1256    DbUuid,
1257    RustType(RustTypeAttribute),
1258}
1259
1260#[derive(Clone, Debug, Eq, PartialEq)]
1261pub struct RustTypeAttribute {
1262    path: String,
1263}
1264
1265impl RustTypeAttribute {
1266    pub fn new(path: impl Into<String>) -> Self {
1267        Self { path: path.into() }
1268    }
1269
1270    pub fn path(&self) -> &str {
1271        &self.path
1272    }
1273}
1274
1275#[derive(Clone, Debug, Eq, PartialEq)]
1276pub enum ModelAttribute {
1277    Id(ModelPrimaryKeyAttribute),
1278    Unique(ModelUniqueAttribute),
1279    Index(ModelIndexAttribute),
1280}
1281
1282#[derive(Clone, Debug, Default, Eq, PartialEq)]
1283pub struct ModelPrimaryKeyAttribute {
1284    fields: Vec<String>,
1285}
1286
1287impl ModelPrimaryKeyAttribute {
1288    pub fn builder() -> ModelPrimaryKeyAttributeBuilder {
1289        ModelPrimaryKeyAttributeBuilder::new()
1290    }
1291
1292    pub fn fields(&self) -> &[String] {
1293        &self.fields
1294    }
1295
1296    fn validate(&self, model: &Model, errors: &mut Vec<ValidationError>) {
1297        if self.fields.is_empty() {
1298            errors.push(ValidationError::new(
1299                ValidationLocation::ModelAttribute {
1300                    model: model.name.clone(),
1301                    attribute: "@@id".to_owned(),
1302                },
1303                "`@@id([...])` cannot be empty",
1304            ));
1305            return;
1306        }
1307
1308        let mut seen = HashSet::new();
1309        for field_name in &self.fields {
1310            if !seen.insert(field_name.as_str()) {
1311                errors.push(ValidationError::new(
1312                    ValidationLocation::ModelPrimaryKeyField {
1313                        model: model.name.clone(),
1314                        field: field_name.clone(),
1315                    },
1316                    format!("duplicate primary key field `{}`", field_name),
1317                ));
1318                continue;
1319            }
1320
1321            match model.field_named(field_name) {
1322                Some(field) => match field.ty() {
1323                    FieldType::Scalar(scalar) => {
1324                        if scalar.optional() {
1325                            errors.push(ValidationError::new(
1326                                ValidationLocation::ModelPrimaryKeyField {
1327                                    model: model.name.clone(),
1328                                    field: field_name.clone(),
1329                                },
1330                                format!("primary key field `{}` must not be optional", field_name),
1331                            ));
1332                        }
1333                    }
1334                    FieldType::Relation { .. } => errors.push(ValidationError::new(
1335                        ValidationLocation::ModelPrimaryKeyField {
1336                            model: model.name.clone(),
1337                            field: field_name.clone(),
1338                        },
1339                        format!("primary key field `{}` must be scalar", field_name),
1340                    )),
1341                },
1342                None => errors.push(ValidationError::new(
1343                    ValidationLocation::ModelPrimaryKeyField {
1344                        model: model.name.clone(),
1345                        field: field_name.clone(),
1346                    },
1347                    format!("unknown primary key field `{}`", field_name),
1348                )),
1349            }
1350        }
1351    }
1352}
1353
1354#[derive(Clone, Debug, Default, Eq, PartialEq)]
1355pub struct ModelPrimaryKeyAttributeBuilder {
1356    fields: Vec<String>,
1357}
1358
1359impl ModelPrimaryKeyAttributeBuilder {
1360    pub fn new() -> Self {
1361        Self::default()
1362    }
1363
1364    pub fn field(mut self, field: impl Into<String>) -> Self {
1365        self.fields.push(field.into());
1366        self
1367    }
1368
1369    pub fn fields(mut self, fields: Vec<String>) -> Self {
1370        self.fields = fields;
1371        self
1372    }
1373
1374    pub fn build(self) -> Result<ModelPrimaryKeyAttribute, ValidationErrors> {
1375        let attribute = ModelPrimaryKeyAttribute {
1376            fields: self.fields,
1377        };
1378
1379        if attribute.fields.is_empty() {
1380            Err(ValidationErrors::from(vec![ValidationError::new(
1381                ValidationLocation::ModelAttribute {
1382                    model: "<model>".to_owned(),
1383                    attribute: "@@id".to_owned(),
1384                },
1385                "`@@id([...])` cannot be empty",
1386            )]))
1387        } else {
1388            Ok(attribute)
1389        }
1390    }
1391}
1392
1393#[derive(Clone, Debug, Default, Eq, PartialEq)]
1394pub struct ModelUniqueAttribute {
1395    fields: Vec<String>,
1396}
1397
1398impl ModelUniqueAttribute {
1399    pub fn builder() -> ModelUniqueAttributeBuilder {
1400        ModelUniqueAttributeBuilder::new()
1401    }
1402
1403    pub fn fields(&self) -> &[String] {
1404        &self.fields
1405    }
1406
1407    fn validate(&self, model: &Model, errors: &mut Vec<ValidationError>) {
1408        if self.fields.is_empty() {
1409            errors.push(ValidationError::new(
1410                ValidationLocation::ModelAttribute {
1411                    model: model.name.clone(),
1412                    attribute: "@@unique".to_owned(),
1413                },
1414                "`@@unique([...])` cannot be empty",
1415            ));
1416            return;
1417        }
1418
1419        let mut seen = HashSet::new();
1420        for field_name in &self.fields {
1421            if !seen.insert(field_name.as_str()) {
1422                errors.push(ValidationError::new(
1423                    ValidationLocation::ModelUniqueField {
1424                        model: model.name.clone(),
1425                        field: field_name.clone(),
1426                    },
1427                    format!("duplicate unique field `{}`", field_name),
1428                ));
1429                continue;
1430            }
1431
1432            match model.field_named(field_name) {
1433                Some(field) => {
1434                    if matches!(field.ty(), FieldType::Relation { .. }) {
1435                        errors.push(ValidationError::new(
1436                            ValidationLocation::ModelUniqueField {
1437                                model: model.name.clone(),
1438                                field: field_name.clone(),
1439                            },
1440                            format!("unique field `{}` must be scalar", field_name),
1441                        ));
1442                    }
1443                }
1444                None => errors.push(ValidationError::new(
1445                    ValidationLocation::ModelUniqueField {
1446                        model: model.name.clone(),
1447                        field: field_name.clone(),
1448                    },
1449                    format!("unknown unique field `{}`", field_name),
1450                )),
1451            }
1452        }
1453    }
1454}
1455
1456#[derive(Clone, Debug, Default, Eq, PartialEq)]
1457pub struct ModelUniqueAttributeBuilder {
1458    fields: Vec<String>,
1459}
1460
1461impl ModelUniqueAttributeBuilder {
1462    pub fn new() -> Self {
1463        Self::default()
1464    }
1465
1466    pub fn field(mut self, field: impl Into<String>) -> Self {
1467        self.fields.push(field.into());
1468        self
1469    }
1470
1471    pub fn fields(mut self, fields: Vec<String>) -> Self {
1472        self.fields = fields;
1473        self
1474    }
1475
1476    pub fn build(self) -> Result<ModelUniqueAttribute, ValidationErrors> {
1477        let attribute = ModelUniqueAttribute {
1478            fields: self.fields,
1479        };
1480
1481        if attribute.fields.is_empty() {
1482            Err(ValidationErrors::from(vec![ValidationError::new(
1483                ValidationLocation::ModelAttribute {
1484                    model: "<model>".to_owned(),
1485                    attribute: "@@unique".to_owned(),
1486                },
1487                "`@@unique([...])` cannot be empty",
1488            )]))
1489        } else {
1490            Ok(attribute)
1491        }
1492    }
1493}
1494
1495#[derive(Clone, Debug, Default, Eq, PartialEq)]
1496pub struct ModelIndexAttribute {
1497    fields: Vec<String>,
1498}
1499
1500impl ModelIndexAttribute {
1501    pub fn builder() -> ModelIndexAttributeBuilder {
1502        ModelIndexAttributeBuilder::new()
1503    }
1504
1505    pub fn fields(&self) -> &[String] {
1506        &self.fields
1507    }
1508
1509    fn validate(&self, model: &Model, errors: &mut Vec<ValidationError>) {
1510        if self.fields.is_empty() {
1511            errors.push(ValidationError::new(
1512                ValidationLocation::ModelAttribute {
1513                    model: model.name.clone(),
1514                    attribute: "@@index".to_owned(),
1515                },
1516                "`@@index([...])` cannot be empty",
1517            ));
1518            return;
1519        }
1520
1521        let mut seen = HashSet::new();
1522        for field_name in &self.fields {
1523            if !seen.insert(field_name.as_str()) {
1524                errors.push(ValidationError::new(
1525                    ValidationLocation::ModelIndexField {
1526                        model: model.name.clone(),
1527                        field: field_name.clone(),
1528                    },
1529                    format!("duplicate index field `{}`", field_name),
1530                ));
1531                continue;
1532            }
1533
1534            match model.field_named(field_name) {
1535                Some(field) => {
1536                    if matches!(field.ty(), FieldType::Relation { .. }) {
1537                        errors.push(ValidationError::new(
1538                            ValidationLocation::ModelIndexField {
1539                                model: model.name.clone(),
1540                                field: field_name.clone(),
1541                            },
1542                            format!("index field `{}` must be scalar", field_name),
1543                        ));
1544                    }
1545                }
1546                None => errors.push(ValidationError::new(
1547                    ValidationLocation::ModelIndexField {
1548                        model: model.name.clone(),
1549                        field: field_name.clone(),
1550                    },
1551                    format!("unknown index field `{}`", field_name),
1552                )),
1553            }
1554        }
1555    }
1556}
1557
1558#[derive(Clone, Debug, Default, Eq, PartialEq)]
1559pub struct ModelIndexAttributeBuilder {
1560    fields: Vec<String>,
1561}
1562
1563impl ModelIndexAttributeBuilder {
1564    pub fn new() -> Self {
1565        Self::default()
1566    }
1567
1568    pub fn field(mut self, field: impl Into<String>) -> Self {
1569        self.fields.push(field.into());
1570        self
1571    }
1572
1573    pub fn fields(mut self, fields: Vec<String>) -> Self {
1574        self.fields = fields;
1575        self
1576    }
1577
1578    pub fn build(self) -> Result<ModelIndexAttribute, ValidationErrors> {
1579        let attribute = ModelIndexAttribute {
1580            fields: self.fields,
1581        };
1582
1583        if attribute.fields.is_empty() {
1584            Err(ValidationErrors::from(vec![ValidationError::new(
1585                ValidationLocation::ModelAttribute {
1586                    model: "<model>".to_owned(),
1587                    attribute: "@@index".to_owned(),
1588                },
1589                "`@@index([...])` cannot be empty",
1590            )]))
1591        } else {
1592            Ok(attribute)
1593        }
1594    }
1595}
1596
1597#[derive(Clone, Debug, Eq, PartialEq)]
1598pub struct DefaultAttribute {
1599    function: DefaultFunction,
1600}
1601
1602impl DefaultAttribute {
1603    pub fn new(function: DefaultFunction) -> Self {
1604        Self { function }
1605    }
1606
1607    pub fn autoincrement() -> Self {
1608        Self::new(DefaultFunction::Autoincrement)
1609    }
1610
1611    pub fn now() -> Self {
1612        Self::new(DefaultFunction::Now)
1613    }
1614
1615    pub fn function(&self) -> &DefaultFunction {
1616        &self.function
1617    }
1618}
1619
1620#[derive(Clone, Debug, Eq, PartialEq)]
1621pub enum DefaultFunction {
1622    Autoincrement,
1623    Now,
1624    Other(String),
1625}
1626
1627#[derive(Clone, Debug, Default, Eq, PartialEq)]
1628pub struct RelationAttribute {
1629    fields: Vec<String>,
1630    references: Vec<String>,
1631}
1632
1633impl RelationAttribute {
1634    pub fn builder() -> RelationAttributeBuilder {
1635        RelationAttributeBuilder::new()
1636    }
1637
1638    pub fn fields(&self) -> &[String] {
1639        &self.fields
1640    }
1641
1642    pub fn references(&self) -> &[String] {
1643        &self.references
1644    }
1645}
1646
1647#[derive(Clone, Debug, Default, Eq, PartialEq)]
1648pub struct RelationAttributeBuilder {
1649    fields: Vec<String>,
1650    references: Vec<String>,
1651}
1652
1653impl RelationAttributeBuilder {
1654    pub fn new() -> Self {
1655        Self::default()
1656    }
1657
1658    pub fn field(mut self, field: impl Into<String>) -> Self {
1659        self.fields.push(field.into());
1660        self
1661    }
1662
1663    pub fn fields(mut self, fields: Vec<String>) -> Self {
1664        self.fields = fields;
1665        self
1666    }
1667
1668    pub fn reference(mut self, reference: impl Into<String>) -> Self {
1669        self.references.push(reference.into());
1670        self
1671    }
1672
1673    pub fn references(mut self, references: Vec<String>) -> Self {
1674        self.references = references;
1675        self
1676    }
1677
1678    pub fn build(self) -> Result<RelationAttribute, ValidationErrors> {
1679        self.build_for_field("<field>", "<field>")
1680    }
1681
1682    pub fn build_for_field(
1683        self,
1684        model_name: &str,
1685        field_name: &str,
1686    ) -> Result<RelationAttribute, ValidationErrors> {
1687        let relation = RelationAttribute {
1688            fields: self.fields,
1689            references: self.references,
1690        };
1691
1692        let mut errors = Vec::new();
1693
1694        if relation.fields.is_empty() {
1695            errors.push(ValidationError::new(
1696                ValidationLocation::RelationAttribute {
1697                    model: model_name.to_owned(),
1698                    field: field_name.to_owned(),
1699                },
1700                "`@relation(fields: [...])` cannot be empty",
1701            ));
1702        }
1703
1704        if relation.references.is_empty() {
1705            errors.push(ValidationError::new(
1706                ValidationLocation::RelationAttribute {
1707                    model: model_name.to_owned(),
1708                    field: field_name.to_owned(),
1709                },
1710                "`@relation(references: [...])` cannot be empty",
1711            ));
1712        }
1713
1714        if errors.is_empty() {
1715            Ok(relation)
1716        } else {
1717            Err(ValidationErrors::from(errors))
1718        }
1719    }
1720}
1721
1722#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1723pub enum FieldKind {
1724    Scalar,
1725    Relation,
1726}
1727
1728impl FieldKind {
1729    pub fn is_scalar(self) -> bool {
1730        matches!(self, Self::Scalar)
1731    }
1732
1733    pub fn is_relation(self) -> bool {
1734        matches!(self, Self::Relation)
1735    }
1736}
1737
1738pub enum Resolution<'a> {
1739    Found(&'a Model),
1740    NotFound,
1741    Ambiguous(Vec<&'a Model>),
1742}