1use std::collections::{HashMap, HashSet};
2
3use heck::ToUpperCamelCase;
4
5use crate::validation::{ValidationError, ValidationErrors, ValidationLocation};
6
7#[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}