1use serde::{Deserialize, Serialize};
4
5use super::{
6 Attribute, Documentation, EnhancedDocumentation, FieldAttributes, FieldType, FieldValidation,
7 Ident, Span, TypeModifier, ValidationRule, ValidationType,
8};
9
10#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
12pub struct Field {
13 pub name: Ident,
15 pub field_type: FieldType,
17 pub modifier: TypeModifier,
19 pub attributes: Vec<Attribute>,
21 pub documentation: Option<Documentation>,
23 pub validation: FieldValidation,
25 pub span: Span,
27}
28
29impl Field {
30 pub fn new(
32 name: Ident,
33 field_type: FieldType,
34 modifier: TypeModifier,
35 attributes: Vec<Attribute>,
36 span: Span,
37 ) -> Self {
38 Self {
39 name,
40 field_type,
41 modifier,
42 attributes,
43 documentation: None,
44 validation: FieldValidation::new(),
45 span,
46 }
47 }
48
49 pub fn name(&self) -> &str {
51 self.name.as_str()
52 }
53
54 pub fn is_optional(&self) -> bool {
56 self.modifier.is_optional()
57 }
58
59 pub fn is_list(&self) -> bool {
61 self.modifier.is_list()
62 }
63
64 pub fn has_attribute(&self, name: &str) -> bool {
66 self.attributes.iter().any(|a| a.is(name))
67 }
68
69 pub fn get_attribute(&self, name: &str) -> Option<&Attribute> {
71 self.attributes.iter().find(|a| a.is(name))
72 }
73
74 pub fn is_id(&self) -> bool {
76 self.has_attribute("id")
77 }
78
79 pub fn is_unique(&self) -> bool {
81 self.has_attribute("unique")
82 }
83
84 pub fn is_relation(&self) -> bool {
86 self.field_type.is_relation() || self.has_attribute("relation")
87 }
88
89 pub fn extract_attributes(&self) -> FieldAttributes {
91 let mut attrs = FieldAttributes::default();
92
93 for attr in &self.attributes {
94 match attr.name() {
95 "id" => attrs.is_id = true,
96 "auto" => attrs.is_auto = true,
97 "unique" => attrs.is_unique = true,
98 "index" => attrs.is_indexed = true,
99 "updated_at" => attrs.is_updated_at = true,
100 "omit" => attrs.is_omit = true,
101 "default" => {
102 attrs.default = attr.first_arg().cloned();
103 }
104 "map" => {
105 if let Some(val) = attr.first_arg() {
106 attrs.map = val.as_string().map(String::from);
107 }
108 }
109 "db" => {
110 if let Some(val) = attr.first_arg() {
112 if let super::AttributeValue::Function(name, args) = val {
113 attrs.native_type =
114 Some(super::NativeType::new(name.clone(), args.clone()));
115 } else if let Some(name) = val.as_ident() {
116 attrs.native_type = Some(super::NativeType::new(name, vec![]));
117 }
118 }
119 }
120 "relation" => {
121 let mut rel = super::RelationAttribute {
123 name: None,
124 fields: vec![],
125 references: vec![],
126 on_delete: None,
127 on_update: None,
128 };
129
130 if let Some(val) = attr.first_arg() {
132 rel.name = val.as_string().map(String::from);
133 }
134
135 if let Some(super::AttributeValue::FieldRefList(fields)) =
137 attr.get_arg("fields")
138 {
139 rel.fields = fields.clone();
140 }
141 if let Some(super::AttributeValue::FieldRefList(refs)) =
142 attr.get_arg("references")
143 {
144 rel.references = refs.clone();
145 }
146 if let Some(val) = attr.get_arg("onDelete") {
147 if let Some(action) = val.as_ident() {
148 rel.on_delete = super::ReferentialAction::from_str(action);
149 }
150 }
151 if let Some(val) = attr.get_arg("onUpdate") {
152 if let Some(action) = val.as_ident() {
153 rel.on_update = super::ReferentialAction::from_str(action);
154 }
155 }
156
157 attrs.relation = Some(rel);
158 }
159 _ => {}
160 }
161 }
162
163 attrs
164 }
165
166 pub fn with_documentation(mut self, doc: Documentation) -> Self {
168 self.documentation = Some(doc);
169 self
170 }
171
172 pub fn with_enhanced_documentation(mut self, doc: EnhancedDocumentation) -> Self {
174 self.documentation = Some(Documentation::new(&doc.text, doc.span));
175 for rule in doc.validation.rules {
177 self.validation.add_rule(rule);
178 }
179 self
180 }
181
182 pub fn with_validation(mut self, validation: FieldValidation) -> Self {
184 self.validation = validation;
185 self
186 }
187
188 pub fn add_validation_rule(&mut self, rule: ValidationRule) {
190 self.validation.add_rule(rule);
191 }
192
193 pub fn has_validation(&self) -> bool {
195 !self.validation.is_empty()
196 }
197
198 pub fn validation_rules(&self) -> &[ValidationRule] {
200 &self.validation.rules
201 }
202
203 pub fn is_validated_required(&self) -> bool {
205 self.validation.is_required()
206 }
207
208 pub fn extract_validation_from_attributes(&mut self) {
215 for attr in &self.attributes {
216 let attr_name = attr.name();
217
218 if let Some(validator_name) = attr_name.strip_prefix("validate.") {
220 if let Some(rule) = self.parse_validate_attribute(validator_name, attr) {
221 self.validation.add_rule(rule);
222 }
223 } else if attr_name == "validate" {
224 for arg in &attr.args {
226 if let Some(rule) = self.parse_validate_arg(arg) {
227 self.validation.add_rule(rule);
228 }
229 }
230 }
231 }
232 }
233
234 fn parse_validate_attribute(
236 &self,
237 validator_name: &str,
238 attr: &Attribute,
239 ) -> Option<ValidationRule> {
240 let span = attr.span;
241
242 let rule_type = match validator_name {
244 "email" => ValidationType::Email,
246 "url" => ValidationType::Url,
247 "uuid" => ValidationType::Uuid,
248 "cuid" => ValidationType::Cuid,
249 "cuid2" => ValidationType::Cuid2,
250 "nanoid" | "nanoId" | "NanoId" => ValidationType::NanoId,
251 "ulid" => ValidationType::Ulid,
252 "alpha" => ValidationType::Alpha,
253 "alphanumeric" => ValidationType::Alphanumeric,
254 "lowercase" => ValidationType::Lowercase,
255 "uppercase" => ValidationType::Uppercase,
256 "trim" => ValidationType::Trim,
257 "noWhitespace" => ValidationType::NoWhitespace,
258 "ip" => ValidationType::Ip,
259 "ipv4" => ValidationType::Ipv4,
260 "ipv6" => ValidationType::Ipv6,
261 "creditCard" => ValidationType::CreditCard,
262 "phone" => ValidationType::Phone,
263 "slug" => ValidationType::Slug,
264 "hex" => ValidationType::Hex,
265 "base64" => ValidationType::Base64,
266 "json" => ValidationType::Json,
267
268 "positive" => ValidationType::Positive,
270 "negative" => ValidationType::Negative,
271 "nonNegative" => ValidationType::NonNegative,
272 "nonPositive" => ValidationType::NonPositive,
273 "integer" => ValidationType::Integer,
274 "finite" => ValidationType::Finite,
275
276 "unique" => ValidationType::Unique,
278 "nonEmpty" => ValidationType::NonEmpty,
279
280 "past" => ValidationType::Past,
282 "future" => ValidationType::Future,
283 "pastOrPresent" => ValidationType::PastOrPresent,
284 "futureOrPresent" => ValidationType::FutureOrPresent,
285
286 "required" => ValidationType::Required,
288 "notEmpty" => ValidationType::NotEmpty,
289
290 "minLength" => {
292 let n = attr.first_arg()?.as_int()? as usize;
293 ValidationType::MinLength(n)
294 }
295 "maxLength" => {
296 let n = attr.first_arg()?.as_int()? as usize;
297 ValidationType::MaxLength(n)
298 }
299 "length" => {
300 let args = &attr.args;
301 if args.len() >= 2 {
302 let min = args[0].value.as_int()? as usize;
303 let max = args[1].value.as_int()? as usize;
304 ValidationType::Length { min, max }
305 } else {
306 return None;
307 }
308 }
309 "min" => {
310 let n = attr
311 .first_arg()?
312 .as_float()
313 .or_else(|| attr.first_arg()?.as_int().map(|i| i as f64))?;
314 ValidationType::Min(n)
315 }
316 "max" => {
317 let n = attr
318 .first_arg()?
319 .as_float()
320 .or_else(|| attr.first_arg()?.as_int().map(|i| i as f64))?;
321 ValidationType::Max(n)
322 }
323 "range" => {
324 let args = &attr.args;
325 if args.len() >= 2 {
326 let min = args[0]
327 .value
328 .as_float()
329 .or_else(|| args[0].value.as_int().map(|i| i as f64))?;
330 let max = args[1]
331 .value
332 .as_float()
333 .or_else(|| args[1].value.as_int().map(|i| i as f64))?;
334 ValidationType::Range { min, max }
335 } else {
336 return None;
337 }
338 }
339 "regex" => {
340 let pattern = attr.first_arg()?.as_string()?.to_string();
341 ValidationType::Regex(pattern)
342 }
343 "startsWith" => {
344 let prefix = attr.first_arg()?.as_string()?.to_string();
345 ValidationType::StartsWith(prefix)
346 }
347 "endsWith" => {
348 let suffix = attr.first_arg()?.as_string()?.to_string();
349 ValidationType::EndsWith(suffix)
350 }
351 "contains" => {
352 let substring = attr.first_arg()?.as_string()?.to_string();
353 ValidationType::Contains(substring)
354 }
355 "minItems" => {
356 let n = attr.first_arg()?.as_int()? as usize;
357 ValidationType::MinItems(n)
358 }
359 "maxItems" => {
360 let n = attr.first_arg()?.as_int()? as usize;
361 ValidationType::MaxItems(n)
362 }
363 "items" => {
364 let args = &attr.args;
365 if args.len() >= 2 {
366 let min = args[0].value.as_int()? as usize;
367 let max = args[1].value.as_int()? as usize;
368 ValidationType::Items { min, max }
369 } else {
370 return None;
371 }
372 }
373 "multipleOf" => {
374 let n = attr
375 .first_arg()?
376 .as_float()
377 .or_else(|| attr.first_arg()?.as_int().map(|i| i as f64))?;
378 ValidationType::MultipleOf(n)
379 }
380 "after" => {
381 let date = attr.first_arg()?.as_string()?.to_string();
382 ValidationType::After(date)
383 }
384 "before" => {
385 let date = attr.first_arg()?.as_string()?.to_string();
386 ValidationType::Before(date)
387 }
388 "custom" => {
389 let name = attr.first_arg()?.as_string()?.to_string();
390 ValidationType::Custom(name)
391 }
392 _ => return None,
393 };
394
395 Some(ValidationRule::new(rule_type, span))
396 }
397
398 fn parse_validate_arg(&self, arg: &super::AttributeArg) -> Option<ValidationRule> {
400 let span = arg.span;
401
402 match &arg.value {
403 super::AttributeValue::Ident(name) => {
404 let rule_type = match name.as_str() {
406 "email" => ValidationType::Email,
407 "url" => ValidationType::Url,
408 "uuid" => ValidationType::Uuid,
409 "cuid" => ValidationType::Cuid,
410 "cuid2" => ValidationType::Cuid2,
411 "nanoid" | "nanoId" | "NanoId" => ValidationType::NanoId,
412 "ulid" => ValidationType::Ulid,
413 "alpha" => ValidationType::Alpha,
414 "alphanumeric" => ValidationType::Alphanumeric,
415 "lowercase" => ValidationType::Lowercase,
416 "uppercase" => ValidationType::Uppercase,
417 "trim" => ValidationType::Trim,
418 "noWhitespace" => ValidationType::NoWhitespace,
419 "ip" => ValidationType::Ip,
420 "ipv4" => ValidationType::Ipv4,
421 "ipv6" => ValidationType::Ipv6,
422 "creditCard" => ValidationType::CreditCard,
423 "phone" => ValidationType::Phone,
424 "slug" => ValidationType::Slug,
425 "hex" => ValidationType::Hex,
426 "base64" => ValidationType::Base64,
427 "json" => ValidationType::Json,
428 "positive" => ValidationType::Positive,
429 "negative" => ValidationType::Negative,
430 "nonNegative" => ValidationType::NonNegative,
431 "nonPositive" => ValidationType::NonPositive,
432 "integer" => ValidationType::Integer,
433 "finite" => ValidationType::Finite,
434 "unique" => ValidationType::Unique,
435 "nonEmpty" => ValidationType::NonEmpty,
436 "past" => ValidationType::Past,
437 "future" => ValidationType::Future,
438 "pastOrPresent" => ValidationType::PastOrPresent,
439 "futureOrPresent" => ValidationType::FutureOrPresent,
440 "required" => ValidationType::Required,
441 "notEmpty" => ValidationType::NotEmpty,
442 _ => return None,
443 };
444 Some(ValidationRule::new(rule_type, span))
445 }
446 super::AttributeValue::Function(name, args) => {
447 let rule_type = match name.as_str() {
449 "minLength" => {
450 let n = args.first()?.as_int()? as usize;
451 ValidationType::MinLength(n)
452 }
453 "maxLength" => {
454 let n = args.first()?.as_int()? as usize;
455 ValidationType::MaxLength(n)
456 }
457 "min" => {
458 let n = args
459 .first()?
460 .as_float()
461 .or_else(|| args.first()?.as_int().map(|i| i as f64))?;
462 ValidationType::Min(n)
463 }
464 "max" => {
465 let n = args
466 .first()?
467 .as_float()
468 .or_else(|| args.first()?.as_int().map(|i| i as f64))?;
469 ValidationType::Max(n)
470 }
471 "range" => {
472 if args.len() >= 2 {
473 let min = args[0]
474 .as_float()
475 .or_else(|| args[0].as_int().map(|i| i as f64))?;
476 let max = args[1]
477 .as_float()
478 .or_else(|| args[1].as_int().map(|i| i as f64))?;
479 ValidationType::Range { min, max }
480 } else {
481 return None;
482 }
483 }
484 "regex" => {
485 let pattern = args.first()?.as_string()?.to_string();
486 ValidationType::Regex(pattern)
487 }
488 "custom" => {
489 let validator_name = args.first()?.as_string()?.to_string();
490 ValidationType::Custom(validator_name)
491 }
492 _ => return None,
493 };
494 Some(ValidationRule::new(rule_type, span))
495 }
496 _ => None,
497 }
498 }
499}
500
501impl std::fmt::Display for Field {
502 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
503 write!(f, "{}", self.name)?;
504
505 match self.modifier {
507 TypeModifier::Required => write!(f, " {}", self.field_type)?,
508 TypeModifier::Optional => write!(f, " {}?", self.field_type)?,
509 TypeModifier::List => write!(f, " {}[]", self.field_type)?,
510 TypeModifier::OptionalList => write!(f, " {}[]?", self.field_type)?,
511 }
512
513 for attr in &self.attributes {
515 write!(f, " @{}", attr.name)?;
516 if !attr.args.is_empty() {
517 write!(f, "(...)")?;
518 }
519 }
520
521 Ok(())
522 }
523}
524
525#[cfg(test)]
526mod tests {
527 use super::*;
528 use crate::ast::{AttributeArg, AttributeValue, ReferentialAction, ScalarType};
529
530 fn make_span() -> Span {
531 Span::new(0, 10)
532 }
533
534 fn make_field(name: &str, field_type: FieldType, modifier: TypeModifier) -> Field {
535 Field::new(
536 Ident::new(name, make_span()),
537 field_type,
538 modifier,
539 vec![],
540 make_span(),
541 )
542 }
543
544 fn make_attribute(name: &str) -> Attribute {
545 Attribute::simple(Ident::new(name, make_span()), make_span())
546 }
547
548 fn make_attribute_with_arg(name: &str, value: AttributeValue) -> Attribute {
549 Attribute::new(
550 Ident::new(name, make_span()),
551 vec![AttributeArg::positional(value, make_span())],
552 make_span(),
553 )
554 }
555
556 #[test]
559 fn test_field_new() {
560 let field = Field::new(
561 Ident::new("id", make_span()),
562 FieldType::Scalar(ScalarType::Int),
563 TypeModifier::Required,
564 vec![],
565 make_span(),
566 );
567
568 assert_eq!(field.name(), "id");
569 assert!(field.field_type.is_scalar());
570 assert_eq!(field.modifier, TypeModifier::Required);
571 assert!(field.attributes.is_empty());
572 assert!(field.documentation.is_none());
573 }
574
575 #[test]
576 fn test_field_with_attributes() {
577 let field = Field::new(
578 Ident::new("email", make_span()),
579 FieldType::Scalar(ScalarType::String),
580 TypeModifier::Required,
581 vec![make_attribute("unique")],
582 make_span(),
583 );
584
585 assert_eq!(field.attributes.len(), 1);
586 }
587
588 #[test]
589 fn test_field_with_documentation() {
590 let field = make_field(
591 "name",
592 FieldType::Scalar(ScalarType::String),
593 TypeModifier::Optional,
594 )
595 .with_documentation(Documentation::new("User's display name", make_span()));
596
597 assert!(field.documentation.is_some());
598 assert_eq!(field.documentation.unwrap().text, "User's display name");
599 }
600
601 #[test]
604 fn test_field_name() {
605 let field = make_field(
606 "created_at",
607 FieldType::Scalar(ScalarType::DateTime),
608 TypeModifier::Required,
609 );
610 assert_eq!(field.name(), "created_at");
611 }
612
613 #[test]
616 fn test_field_is_optional_required() {
617 let field = make_field(
618 "id",
619 FieldType::Scalar(ScalarType::Int),
620 TypeModifier::Required,
621 );
622 assert!(!field.is_optional());
623 }
624
625 #[test]
626 fn test_field_is_optional_true() {
627 let field = make_field(
628 "bio",
629 FieldType::Scalar(ScalarType::String),
630 TypeModifier::Optional,
631 );
632 assert!(field.is_optional());
633 }
634
635 #[test]
636 fn test_field_is_list_false() {
637 let field = make_field(
638 "name",
639 FieldType::Scalar(ScalarType::String),
640 TypeModifier::Required,
641 );
642 assert!(!field.is_list());
643 }
644
645 #[test]
646 fn test_field_is_list_true() {
647 let field = make_field(
648 "tags",
649 FieldType::Scalar(ScalarType::String),
650 TypeModifier::List,
651 );
652 assert!(field.is_list());
653 }
654
655 #[test]
656 fn test_field_optional_list() {
657 let field = make_field(
658 "metadata",
659 FieldType::Scalar(ScalarType::Json),
660 TypeModifier::OptionalList,
661 );
662 assert!(field.is_optional());
663 assert!(field.is_list());
664 }
665
666 #[test]
669 fn test_field_has_attribute_true() {
670 let mut field = make_field(
671 "id",
672 FieldType::Scalar(ScalarType::Int),
673 TypeModifier::Required,
674 );
675 field.attributes.push(make_attribute("id"));
676 field.attributes.push(make_attribute("auto"));
677
678 assert!(field.has_attribute("id"));
679 assert!(field.has_attribute("auto"));
680 }
681
682 #[test]
683 fn test_field_has_attribute_false() {
684 let field = make_field(
685 "name",
686 FieldType::Scalar(ScalarType::String),
687 TypeModifier::Required,
688 );
689 assert!(!field.has_attribute("unique"));
690 }
691
692 #[test]
693 fn test_field_get_attribute() {
694 let mut field = make_field(
695 "email",
696 FieldType::Scalar(ScalarType::String),
697 TypeModifier::Required,
698 );
699 field.attributes.push(make_attribute("unique"));
700
701 let attr = field.get_attribute("unique");
702 assert!(attr.is_some());
703 assert!(attr.unwrap().is("unique"));
704
705 assert!(field.get_attribute("id").is_none());
706 }
707
708 #[test]
709 fn test_field_is_id_true() {
710 let mut field = make_field(
711 "id",
712 FieldType::Scalar(ScalarType::Int),
713 TypeModifier::Required,
714 );
715 field.attributes.push(make_attribute("id"));
716 assert!(field.is_id());
717 }
718
719 #[test]
720 fn test_field_is_id_false() {
721 let field = make_field(
722 "email",
723 FieldType::Scalar(ScalarType::String),
724 TypeModifier::Required,
725 );
726 assert!(!field.is_id());
727 }
728
729 #[test]
730 fn test_field_is_unique_true() {
731 let mut field = make_field(
732 "email",
733 FieldType::Scalar(ScalarType::String),
734 TypeModifier::Required,
735 );
736 field.attributes.push(make_attribute("unique"));
737 assert!(field.is_unique());
738 }
739
740 #[test]
741 fn test_field_is_unique_false() {
742 let field = make_field(
743 "name",
744 FieldType::Scalar(ScalarType::String),
745 TypeModifier::Required,
746 );
747 assert!(!field.is_unique());
748 }
749
750 #[test]
753 fn test_field_is_relation_by_type() {
754 let field = make_field(
755 "author",
756 FieldType::Model("User".into()),
757 TypeModifier::Required,
758 );
759 assert!(field.is_relation());
760 }
761
762 #[test]
763 fn test_field_is_relation_by_attribute() {
764 let mut field = make_field(
765 "author_id",
766 FieldType::Scalar(ScalarType::Int),
767 TypeModifier::Required,
768 );
769 field.attributes.push(make_attribute("relation"));
770 assert!(field.is_relation());
771 }
772
773 #[test]
774 fn test_field_is_relation_list() {
775 let field = make_field("posts", FieldType::Model("Post".into()), TypeModifier::List);
776 assert!(field.is_relation());
777 assert!(field.is_list());
778 }
779
780 #[test]
783 fn test_extract_attributes_empty() {
784 let field = make_field(
785 "name",
786 FieldType::Scalar(ScalarType::String),
787 TypeModifier::Required,
788 );
789 let attrs = field.extract_attributes();
790
791 assert!(!attrs.is_id);
792 assert!(!attrs.is_auto);
793 assert!(!attrs.is_unique);
794 assert!(!attrs.is_indexed);
795 assert!(!attrs.is_updated_at);
796 assert!(!attrs.is_omit);
797 assert!(attrs.default.is_none());
798 assert!(attrs.map.is_none());
799 assert!(attrs.native_type.is_none());
800 assert!(attrs.relation.is_none());
801 }
802
803 #[test]
804 fn test_extract_attributes_id_and_auto() {
805 let mut field = make_field(
806 "id",
807 FieldType::Scalar(ScalarType::Int),
808 TypeModifier::Required,
809 );
810 field.attributes.push(make_attribute("id"));
811 field.attributes.push(make_attribute("auto"));
812
813 let attrs = field.extract_attributes();
814 assert!(attrs.is_id);
815 assert!(attrs.is_auto);
816 }
817
818 #[test]
819 fn test_extract_attributes_unique() {
820 let mut field = make_field(
821 "email",
822 FieldType::Scalar(ScalarType::String),
823 TypeModifier::Required,
824 );
825 field.attributes.push(make_attribute("unique"));
826
827 let attrs = field.extract_attributes();
828 assert!(attrs.is_unique);
829 }
830
831 #[test]
832 fn test_extract_attributes_index() {
833 let mut field = make_field(
834 "name",
835 FieldType::Scalar(ScalarType::String),
836 TypeModifier::Required,
837 );
838 field.attributes.push(make_attribute("index"));
839
840 let attrs = field.extract_attributes();
841 assert!(attrs.is_indexed);
842 }
843
844 #[test]
845 fn test_extract_attributes_updated_at() {
846 let mut field = make_field(
847 "updated_at",
848 FieldType::Scalar(ScalarType::DateTime),
849 TypeModifier::Required,
850 );
851 field.attributes.push(make_attribute("updated_at"));
852
853 let attrs = field.extract_attributes();
854 assert!(attrs.is_updated_at);
855 }
856
857 #[test]
858 fn test_extract_attributes_omit() {
859 let mut field = make_field(
860 "password_hash",
861 FieldType::Scalar(ScalarType::String),
862 TypeModifier::Required,
863 );
864 field.attributes.push(make_attribute("omit"));
865
866 let attrs = field.extract_attributes();
867 assert!(attrs.is_omit);
868 }
869
870 #[test]
871 fn test_extract_attributes_default_int() {
872 let mut field = make_field(
873 "count",
874 FieldType::Scalar(ScalarType::Int),
875 TypeModifier::Required,
876 );
877 field
878 .attributes
879 .push(make_attribute_with_arg("default", AttributeValue::Int(0)));
880
881 let attrs = field.extract_attributes();
882 assert!(attrs.default.is_some());
883 assert_eq!(attrs.default.as_ref().unwrap().as_int(), Some(0));
884 }
885
886 #[test]
887 fn test_extract_attributes_default_function() {
888 let mut field = make_field(
889 "created_at",
890 FieldType::Scalar(ScalarType::DateTime),
891 TypeModifier::Required,
892 );
893 field.attributes.push(make_attribute_with_arg(
894 "default",
895 AttributeValue::Function("now".into(), vec![]),
896 ));
897
898 let attrs = field.extract_attributes();
899 assert!(attrs.default.is_some());
900 if let AttributeValue::Function(name, _) = attrs.default.as_ref().unwrap() {
901 assert_eq!(name.as_str(), "now");
902 } else {
903 panic!("Expected Function");
904 }
905 }
906
907 #[test]
908 fn test_extract_attributes_map() {
909 let mut field = make_field(
910 "email",
911 FieldType::Scalar(ScalarType::String),
912 TypeModifier::Required,
913 );
914 field.attributes.push(make_attribute_with_arg(
915 "map",
916 AttributeValue::String("email_address".into()),
917 ));
918
919 let attrs = field.extract_attributes();
920 assert_eq!(attrs.map, Some("email_address".to_string()));
921 }
922
923 #[test]
924 fn test_extract_attributes_native_type_ident() {
925 let mut field = make_field(
926 "data",
927 FieldType::Scalar(ScalarType::String),
928 TypeModifier::Required,
929 );
930 field.attributes.push(make_attribute_with_arg(
931 "db",
932 AttributeValue::Ident("Text".into()),
933 ));
934
935 let attrs = field.extract_attributes();
936 assert!(attrs.native_type.is_some());
937 let nt = attrs.native_type.unwrap();
938 assert_eq!(nt.name.as_str(), "Text");
939 assert!(nt.args.is_empty());
940 }
941
942 #[test]
943 fn test_extract_attributes_native_type_function() {
944 let mut field = make_field(
945 "name",
946 FieldType::Scalar(ScalarType::String),
947 TypeModifier::Required,
948 );
949 field.attributes.push(make_attribute_with_arg(
950 "db",
951 AttributeValue::Function("VarChar".into(), vec![AttributeValue::Int(255)]),
952 ));
953
954 let attrs = field.extract_attributes();
955 assert!(attrs.native_type.is_some());
956 let nt = attrs.native_type.unwrap();
957 assert_eq!(nt.name.as_str(), "VarChar");
958 assert_eq!(nt.args.len(), 1);
959 }
960
961 #[test]
962 fn test_extract_attributes_relation() {
963 let mut field = make_field(
964 "author",
965 FieldType::Model("User".into()),
966 TypeModifier::Required,
967 );
968 field.attributes.push(Attribute::new(
969 Ident::new("relation", make_span()),
970 vec![
971 AttributeArg::named(
972 Ident::new("fields", make_span()),
973 AttributeValue::FieldRefList(vec!["author_id".into()]),
974 make_span(),
975 ),
976 AttributeArg::named(
977 Ident::new("references", make_span()),
978 AttributeValue::FieldRefList(vec!["id".into()]),
979 make_span(),
980 ),
981 AttributeArg::named(
982 Ident::new("onDelete", make_span()),
983 AttributeValue::Ident("Cascade".into()),
984 make_span(),
985 ),
986 AttributeArg::named(
987 Ident::new("onUpdate", make_span()),
988 AttributeValue::Ident("Restrict".into()),
989 make_span(),
990 ),
991 ],
992 make_span(),
993 ));
994
995 let attrs = field.extract_attributes();
996 assert!(attrs.relation.is_some());
997
998 let rel = attrs.relation.unwrap();
999 assert_eq!(rel.fields, vec!["author_id".to_string()]);
1000 assert_eq!(rel.references, vec!["id".to_string()]);
1001 assert_eq!(rel.on_delete, Some(ReferentialAction::Cascade));
1002 assert_eq!(rel.on_update, Some(ReferentialAction::Restrict));
1003 }
1004
1005 #[test]
1006 fn test_extract_attributes_relation_with_name() {
1007 let mut field = make_field(
1008 "author",
1009 FieldType::Model("User".into()),
1010 TypeModifier::Required,
1011 );
1012 field.attributes.push(Attribute::new(
1013 Ident::new("relation", make_span()),
1014 vec![AttributeArg::positional(
1015 AttributeValue::String("PostAuthor".into()),
1016 make_span(),
1017 )],
1018 make_span(),
1019 ));
1020
1021 let attrs = field.extract_attributes();
1022 assert!(attrs.relation.is_some());
1023 assert_eq!(attrs.relation.unwrap().name, Some("PostAuthor".to_string()));
1024 }
1025
1026 #[test]
1029 fn test_field_display_required() {
1030 let field = make_field(
1031 "id",
1032 FieldType::Scalar(ScalarType::Int),
1033 TypeModifier::Required,
1034 );
1035 assert_eq!(format!("{}", field), "id Int");
1036 }
1037
1038 #[test]
1039 fn test_field_display_optional() {
1040 let field = make_field(
1041 "bio",
1042 FieldType::Scalar(ScalarType::String),
1043 TypeModifier::Optional,
1044 );
1045 assert_eq!(format!("{}", field), "bio String?");
1046 }
1047
1048 #[test]
1049 fn test_field_display_list() {
1050 let field = make_field(
1051 "tags",
1052 FieldType::Scalar(ScalarType::String),
1053 TypeModifier::List,
1054 );
1055 assert_eq!(format!("{}", field), "tags String[]");
1056 }
1057
1058 #[test]
1059 fn test_field_display_optional_list() {
1060 let field = make_field(
1061 "data",
1062 FieldType::Scalar(ScalarType::Json),
1063 TypeModifier::OptionalList,
1064 );
1065 assert_eq!(format!("{}", field), "data Json[]?");
1066 }
1067
1068 #[test]
1069 fn test_field_display_with_simple_attribute() {
1070 let mut field = make_field(
1071 "id",
1072 FieldType::Scalar(ScalarType::Int),
1073 TypeModifier::Required,
1074 );
1075 field.attributes.push(make_attribute("id"));
1076 assert!(format!("{}", field).contains("@id"));
1077 }
1078
1079 #[test]
1080 fn test_field_display_with_attribute_args() {
1081 let mut field = make_field(
1082 "count",
1083 FieldType::Scalar(ScalarType::Int),
1084 TypeModifier::Required,
1085 );
1086 field
1087 .attributes
1088 .push(make_attribute_with_arg("default", AttributeValue::Int(0)));
1089 assert!(format!("{}", field).contains("@default(...)"));
1090 }
1091
1092 #[test]
1093 fn test_field_display_relation() {
1094 let field = make_field(
1095 "author",
1096 FieldType::Model("User".into()),
1097 TypeModifier::Required,
1098 );
1099 assert_eq!(format!("{}", field), "author User");
1100 }
1101
1102 #[test]
1103 fn test_field_display_enum() {
1104 let field = make_field(
1105 "role",
1106 FieldType::Enum("Role".into()),
1107 TypeModifier::Required,
1108 );
1109 assert_eq!(format!("{}", field), "role Role");
1110 }
1111
1112 #[test]
1115 fn test_field_equality() {
1116 let field1 = make_field(
1117 "id",
1118 FieldType::Scalar(ScalarType::Int),
1119 TypeModifier::Required,
1120 );
1121 let field2 = make_field(
1122 "id",
1123 FieldType::Scalar(ScalarType::Int),
1124 TypeModifier::Required,
1125 );
1126 assert_eq!(field1, field2);
1127 }
1128
1129 #[test]
1130 fn test_field_inequality_name() {
1131 let field1 = make_field(
1132 "id",
1133 FieldType::Scalar(ScalarType::Int),
1134 TypeModifier::Required,
1135 );
1136 let field2 = make_field(
1137 "user_id",
1138 FieldType::Scalar(ScalarType::Int),
1139 TypeModifier::Required,
1140 );
1141 assert_ne!(field1, field2);
1142 }
1143
1144 #[test]
1145 fn test_field_inequality_type() {
1146 let field1 = make_field(
1147 "id",
1148 FieldType::Scalar(ScalarType::Int),
1149 TypeModifier::Required,
1150 );
1151 let field2 = make_field(
1152 "id",
1153 FieldType::Scalar(ScalarType::String),
1154 TypeModifier::Required,
1155 );
1156 assert_ne!(field1, field2);
1157 }
1158
1159 #[test]
1160 fn test_field_inequality_modifier() {
1161 let field1 = make_field(
1162 "name",
1163 FieldType::Scalar(ScalarType::String),
1164 TypeModifier::Required,
1165 );
1166 let field2 = make_field(
1167 "name",
1168 FieldType::Scalar(ScalarType::String),
1169 TypeModifier::Optional,
1170 );
1171 assert_ne!(field1, field2);
1172 }
1173
1174 #[test]
1177 fn test_field_with_validation() {
1178 let validation = FieldValidation::new();
1179 let field = make_field(
1180 "email",
1181 FieldType::Scalar(ScalarType::String),
1182 TypeModifier::Required,
1183 )
1184 .with_validation(validation);
1185
1186 assert!(!field.has_validation());
1187 }
1188
1189 #[test]
1190 fn test_field_add_validation_rule() {
1191 let mut field = make_field(
1192 "email",
1193 FieldType::Scalar(ScalarType::String),
1194 TypeModifier::Required,
1195 );
1196
1197 field.add_validation_rule(ValidationRule::new(ValidationType::Email, make_span()));
1198 assert!(field.has_validation());
1199 assert_eq!(field.validation_rules().len(), 1);
1200 }
1201
1202 #[test]
1203 fn test_field_validation_required() {
1204 let mut field = make_field(
1205 "name",
1206 FieldType::Scalar(ScalarType::String),
1207 TypeModifier::Optional,
1208 );
1209
1210 assert!(!field.is_validated_required());
1211 field.add_validation_rule(ValidationRule::new(ValidationType::Required, make_span()));
1212 assert!(field.is_validated_required());
1213 }
1214
1215 #[test]
1216 fn test_extract_validation_email() {
1217 let mut field = make_field(
1218 "email",
1219 FieldType::Scalar(ScalarType::String),
1220 TypeModifier::Required,
1221 );
1222 field.attributes.push(Attribute::simple(
1223 Ident::new("validate.email", make_span()),
1224 make_span(),
1225 ));
1226
1227 field.extract_validation_from_attributes();
1228 assert!(field.has_validation());
1229 assert_eq!(field.validation_rules().len(), 1);
1230 }
1231
1232 #[test]
1233 fn test_extract_validation_url() {
1234 let mut field = make_field(
1235 "website",
1236 FieldType::Scalar(ScalarType::String),
1237 TypeModifier::Optional,
1238 );
1239 field.attributes.push(Attribute::simple(
1240 Ident::new("validate.url", make_span()),
1241 make_span(),
1242 ));
1243
1244 field.extract_validation_from_attributes();
1245 assert!(field.has_validation());
1246 }
1247
1248 #[test]
1249 fn test_extract_validation_uuid() {
1250 let mut field = make_field(
1251 "id",
1252 FieldType::Scalar(ScalarType::String),
1253 TypeModifier::Required,
1254 );
1255 field.attributes.push(Attribute::simple(
1256 Ident::new("validate.uuid", make_span()),
1257 make_span(),
1258 ));
1259
1260 field.extract_validation_from_attributes();
1261 assert!(field.has_validation());
1262 }
1263
1264 #[test]
1265 fn test_extract_validation_min_length() {
1266 let mut field = make_field(
1267 "name",
1268 FieldType::Scalar(ScalarType::String),
1269 TypeModifier::Required,
1270 );
1271 field.attributes.push(Attribute::new(
1272 Ident::new("validate.minLength", make_span()),
1273 vec![AttributeArg::positional(
1274 AttributeValue::Int(3),
1275 make_span(),
1276 )],
1277 make_span(),
1278 ));
1279
1280 field.extract_validation_from_attributes();
1281 assert!(field.has_validation());
1282 }
1283
1284 #[test]
1285 fn test_extract_validation_max_length() {
1286 let mut field = make_field(
1287 "bio",
1288 FieldType::Scalar(ScalarType::String),
1289 TypeModifier::Optional,
1290 );
1291 field.attributes.push(Attribute::new(
1292 Ident::new("validate.maxLength", make_span()),
1293 vec![AttributeArg::positional(
1294 AttributeValue::Int(500),
1295 make_span(),
1296 )],
1297 make_span(),
1298 ));
1299
1300 field.extract_validation_from_attributes();
1301 assert!(field.has_validation());
1302 }
1303
1304 #[test]
1305 fn test_extract_validation_min() {
1306 let mut field = make_field(
1307 "age",
1308 FieldType::Scalar(ScalarType::Int),
1309 TypeModifier::Required,
1310 );
1311 field.attributes.push(Attribute::new(
1312 Ident::new("validate.min", make_span()),
1313 vec![AttributeArg::positional(
1314 AttributeValue::Int(0),
1315 make_span(),
1316 )],
1317 make_span(),
1318 ));
1319
1320 field.extract_validation_from_attributes();
1321 assert!(field.has_validation());
1322 }
1323
1324 #[test]
1325 fn test_extract_validation_max() {
1326 let mut field = make_field(
1327 "percentage",
1328 FieldType::Scalar(ScalarType::Float),
1329 TypeModifier::Required,
1330 );
1331 field.attributes.push(Attribute::new(
1332 Ident::new("validate.max", make_span()),
1333 vec![AttributeArg::positional(
1334 AttributeValue::Float(100.0),
1335 make_span(),
1336 )],
1337 make_span(),
1338 ));
1339
1340 field.extract_validation_from_attributes();
1341 assert!(field.has_validation());
1342 }
1343
1344 #[test]
1345 fn test_extract_validation_range() {
1346 let mut field = make_field(
1347 "rating",
1348 FieldType::Scalar(ScalarType::Int),
1349 TypeModifier::Required,
1350 );
1351 field.attributes.push(Attribute::new(
1352 Ident::new("validate.range", make_span()),
1353 vec![
1354 AttributeArg::positional(AttributeValue::Int(1), make_span()),
1355 AttributeArg::positional(AttributeValue::Int(5), make_span()),
1356 ],
1357 make_span(),
1358 ));
1359
1360 field.extract_validation_from_attributes();
1361 assert!(field.has_validation());
1362 }
1363
1364 #[test]
1365 fn test_extract_validation_regex() {
1366 let mut field = make_field(
1367 "phone",
1368 FieldType::Scalar(ScalarType::String),
1369 TypeModifier::Required,
1370 );
1371 field.attributes.push(Attribute::new(
1372 Ident::new("validate.regex", make_span()),
1373 vec![AttributeArg::positional(
1374 AttributeValue::String("^\\+[0-9]+$".into()),
1375 make_span(),
1376 )],
1377 make_span(),
1378 ));
1379
1380 field.extract_validation_from_attributes();
1381 assert!(field.has_validation());
1382 }
1383
1384 #[test]
1385 fn test_extract_validation_positive() {
1386 let mut field = make_field(
1387 "amount",
1388 FieldType::Scalar(ScalarType::Float),
1389 TypeModifier::Required,
1390 );
1391 field.attributes.push(Attribute::simple(
1392 Ident::new("validate.positive", make_span()),
1393 make_span(),
1394 ));
1395
1396 field.extract_validation_from_attributes();
1397 assert!(field.has_validation());
1398 }
1399
1400 #[test]
1401 fn test_extract_validation_negative() {
1402 let mut field = make_field(
1403 "debt",
1404 FieldType::Scalar(ScalarType::Float),
1405 TypeModifier::Required,
1406 );
1407 field.attributes.push(Attribute::simple(
1408 Ident::new("validate.negative", make_span()),
1409 make_span(),
1410 ));
1411
1412 field.extract_validation_from_attributes();
1413 assert!(field.has_validation());
1414 }
1415
1416 #[test]
1417 fn test_extract_validation_nonNegative() {
1418 let mut field = make_field(
1419 "count",
1420 FieldType::Scalar(ScalarType::Int),
1421 TypeModifier::Required,
1422 );
1423 field.attributes.push(Attribute::simple(
1424 Ident::new("validate.nonNegative", make_span()),
1425 make_span(),
1426 ));
1427
1428 field.extract_validation_from_attributes();
1429 assert!(field.has_validation());
1430 }
1431
1432 #[test]
1433 fn test_extract_validation_alpha() {
1434 let mut field = make_field(
1435 "code",
1436 FieldType::Scalar(ScalarType::String),
1437 TypeModifier::Required,
1438 );
1439 field.attributes.push(Attribute::simple(
1440 Ident::new("validate.alpha", make_span()),
1441 make_span(),
1442 ));
1443
1444 field.extract_validation_from_attributes();
1445 assert!(field.has_validation());
1446 }
1447
1448 #[test]
1449 fn test_extract_validation_alphanumeric() {
1450 let mut field = make_field(
1451 "username",
1452 FieldType::Scalar(ScalarType::String),
1453 TypeModifier::Required,
1454 );
1455 field.attributes.push(Attribute::simple(
1456 Ident::new("validate.alphanumeric", make_span()),
1457 make_span(),
1458 ));
1459
1460 field.extract_validation_from_attributes();
1461 assert!(field.has_validation());
1462 }
1463
1464 #[test]
1465 fn test_extract_validation_lowercase() {
1466 let mut field = make_field(
1467 "slug",
1468 FieldType::Scalar(ScalarType::String),
1469 TypeModifier::Required,
1470 );
1471 field.attributes.push(Attribute::simple(
1472 Ident::new("validate.lowercase", make_span()),
1473 make_span(),
1474 ));
1475
1476 field.extract_validation_from_attributes();
1477 assert!(field.has_validation());
1478 }
1479
1480 #[test]
1481 fn test_extract_validation_uppercase() {
1482 let mut field = make_field(
1483 "country_code",
1484 FieldType::Scalar(ScalarType::String),
1485 TypeModifier::Required,
1486 );
1487 field.attributes.push(Attribute::simple(
1488 Ident::new("validate.uppercase", make_span()),
1489 make_span(),
1490 ));
1491
1492 field.extract_validation_from_attributes();
1493 assert!(field.has_validation());
1494 }
1495
1496 #[test]
1497 fn test_extract_validation_trim() {
1498 let mut field = make_field(
1499 "input",
1500 FieldType::Scalar(ScalarType::String),
1501 TypeModifier::Required,
1502 );
1503 field.attributes.push(Attribute::simple(
1504 Ident::new("validate.trim", make_span()),
1505 make_span(),
1506 ));
1507
1508 field.extract_validation_from_attributes();
1509 assert!(field.has_validation());
1510 }
1511
1512 #[test]
1513 fn test_extract_validation_ip() {
1514 let mut field = make_field(
1515 "ip_address",
1516 FieldType::Scalar(ScalarType::String),
1517 TypeModifier::Required,
1518 );
1519 field.attributes.push(Attribute::simple(
1520 Ident::new("validate.ip", make_span()),
1521 make_span(),
1522 ));
1523
1524 field.extract_validation_from_attributes();
1525 assert!(field.has_validation());
1526 }
1527
1528 #[test]
1529 fn test_extract_validation_ipv4() {
1530 let mut field = make_field(
1531 "ipv4",
1532 FieldType::Scalar(ScalarType::String),
1533 TypeModifier::Required,
1534 );
1535 field.attributes.push(Attribute::simple(
1536 Ident::new("validate.ipv4", make_span()),
1537 make_span(),
1538 ));
1539
1540 field.extract_validation_from_attributes();
1541 assert!(field.has_validation());
1542 }
1543
1544 #[test]
1545 fn test_extract_validation_ipv6() {
1546 let mut field = make_field(
1547 "ipv6",
1548 FieldType::Scalar(ScalarType::String),
1549 TypeModifier::Required,
1550 );
1551 field.attributes.push(Attribute::simple(
1552 Ident::new("validate.ipv6", make_span()),
1553 make_span(),
1554 ));
1555
1556 field.extract_validation_from_attributes();
1557 assert!(field.has_validation());
1558 }
1559
1560 #[test]
1561 fn test_extract_validation_slug() {
1562 let mut field = make_field(
1563 "url_slug",
1564 FieldType::Scalar(ScalarType::String),
1565 TypeModifier::Required,
1566 );
1567 field.attributes.push(Attribute::simple(
1568 Ident::new("validate.slug", make_span()),
1569 make_span(),
1570 ));
1571
1572 field.extract_validation_from_attributes();
1573 assert!(field.has_validation());
1574 }
1575
1576 #[test]
1577 fn test_extract_validation_hex() {
1578 let mut field = make_field(
1579 "color",
1580 FieldType::Scalar(ScalarType::String),
1581 TypeModifier::Required,
1582 );
1583 field.attributes.push(Attribute::simple(
1584 Ident::new("validate.hex", make_span()),
1585 make_span(),
1586 ));
1587
1588 field.extract_validation_from_attributes();
1589 assert!(field.has_validation());
1590 }
1591
1592 #[test]
1593 fn test_extract_validation_base64() {
1594 let mut field = make_field(
1595 "encoded",
1596 FieldType::Scalar(ScalarType::String),
1597 TypeModifier::Required,
1598 );
1599 field.attributes.push(Attribute::simple(
1600 Ident::new("validate.base64", make_span()),
1601 make_span(),
1602 ));
1603
1604 field.extract_validation_from_attributes();
1605 assert!(field.has_validation());
1606 }
1607
1608 #[test]
1609 fn test_extract_validation_json() {
1610 let mut field = make_field(
1611 "json_str",
1612 FieldType::Scalar(ScalarType::String),
1613 TypeModifier::Required,
1614 );
1615 field.attributes.push(Attribute::simple(
1616 Ident::new("validate.json", make_span()),
1617 make_span(),
1618 ));
1619
1620 field.extract_validation_from_attributes();
1621 assert!(field.has_validation());
1622 }
1623
1624 #[test]
1625 fn test_extract_validation_integer() {
1626 let mut field = make_field(
1627 "whole",
1628 FieldType::Scalar(ScalarType::Float),
1629 TypeModifier::Required,
1630 );
1631 field.attributes.push(Attribute::simple(
1632 Ident::new("validate.integer", make_span()),
1633 make_span(),
1634 ));
1635
1636 field.extract_validation_from_attributes();
1637 assert!(field.has_validation());
1638 }
1639
1640 #[test]
1641 fn test_extract_validation_finite() {
1642 let mut field = make_field(
1643 "value",
1644 FieldType::Scalar(ScalarType::Float),
1645 TypeModifier::Required,
1646 );
1647 field.attributes.push(Attribute::simple(
1648 Ident::new("validate.finite", make_span()),
1649 make_span(),
1650 ));
1651
1652 field.extract_validation_from_attributes();
1653 assert!(field.has_validation());
1654 }
1655
1656 #[test]
1657 fn test_extract_validation_unique_array() {
1658 let mut field = make_field(
1659 "tags",
1660 FieldType::Scalar(ScalarType::String),
1661 TypeModifier::List,
1662 );
1663 field.attributes.push(Attribute::simple(
1664 Ident::new("validate.unique", make_span()),
1665 make_span(),
1666 ));
1667
1668 field.extract_validation_from_attributes();
1669 assert!(field.has_validation());
1670 }
1671
1672 #[test]
1673 fn test_extract_validation_nonEmpty() {
1674 let mut field = make_field(
1675 "required_tags",
1676 FieldType::Scalar(ScalarType::String),
1677 TypeModifier::List,
1678 );
1679 field.attributes.push(Attribute::simple(
1680 Ident::new("validate.nonEmpty", make_span()),
1681 make_span(),
1682 ));
1683
1684 field.extract_validation_from_attributes();
1685 assert!(field.has_validation());
1686 }
1687
1688 #[test]
1689 fn test_extract_validation_past() {
1690 let mut field = make_field(
1691 "birth_date",
1692 FieldType::Scalar(ScalarType::DateTime),
1693 TypeModifier::Required,
1694 );
1695 field.attributes.push(Attribute::simple(
1696 Ident::new("validate.past", make_span()),
1697 make_span(),
1698 ));
1699
1700 field.extract_validation_from_attributes();
1701 assert!(field.has_validation());
1702 }
1703
1704 #[test]
1705 fn test_extract_validation_future() {
1706 let mut field = make_field(
1707 "expiry_date",
1708 FieldType::Scalar(ScalarType::DateTime),
1709 TypeModifier::Required,
1710 );
1711 field.attributes.push(Attribute::simple(
1712 Ident::new("validate.future", make_span()),
1713 make_span(),
1714 ));
1715
1716 field.extract_validation_from_attributes();
1717 assert!(field.has_validation());
1718 }
1719
1720 #[test]
1721 fn test_extract_validation_min_items() {
1722 let mut field = make_field(
1723 "items",
1724 FieldType::Scalar(ScalarType::String),
1725 TypeModifier::List,
1726 );
1727 field.attributes.push(Attribute::new(
1728 Ident::new("validate.minItems", make_span()),
1729 vec![AttributeArg::positional(
1730 AttributeValue::Int(1),
1731 make_span(),
1732 )],
1733 make_span(),
1734 ));
1735
1736 field.extract_validation_from_attributes();
1737 assert!(field.has_validation());
1738 }
1739
1740 #[test]
1741 fn test_extract_validation_max_items() {
1742 let mut field = make_field(
1743 "items",
1744 FieldType::Scalar(ScalarType::String),
1745 TypeModifier::List,
1746 );
1747 field.attributes.push(Attribute::new(
1748 Ident::new("validate.maxItems", make_span()),
1749 vec![AttributeArg::positional(
1750 AttributeValue::Int(10),
1751 make_span(),
1752 )],
1753 make_span(),
1754 ));
1755
1756 field.extract_validation_from_attributes();
1757 assert!(field.has_validation());
1758 }
1759
1760 #[test]
1761 fn test_extract_validation_multiple_of() {
1762 let mut field = make_field(
1763 "amount",
1764 FieldType::Scalar(ScalarType::Float),
1765 TypeModifier::Required,
1766 );
1767 field.attributes.push(Attribute::new(
1768 Ident::new("validate.multipleOf", make_span()),
1769 vec![AttributeArg::positional(
1770 AttributeValue::Float(0.01),
1771 make_span(),
1772 )],
1773 make_span(),
1774 ));
1775
1776 field.extract_validation_from_attributes();
1777 assert!(field.has_validation());
1778 }
1779
1780 #[test]
1781 fn test_extract_validation_starts_with() {
1782 let mut field = make_field(
1783 "prefix_field",
1784 FieldType::Scalar(ScalarType::String),
1785 TypeModifier::Required,
1786 );
1787 field.attributes.push(Attribute::new(
1788 Ident::new("validate.startsWith", make_span()),
1789 vec![AttributeArg::positional(
1790 AttributeValue::String("PREFIX_".into()),
1791 make_span(),
1792 )],
1793 make_span(),
1794 ));
1795
1796 field.extract_validation_from_attributes();
1797 assert!(field.has_validation());
1798 }
1799
1800 #[test]
1801 fn test_extract_validation_ends_with() {
1802 let mut field = make_field(
1803 "suffix_field",
1804 FieldType::Scalar(ScalarType::String),
1805 TypeModifier::Required,
1806 );
1807 field.attributes.push(Attribute::new(
1808 Ident::new("validate.endsWith", make_span()),
1809 vec![AttributeArg::positional(
1810 AttributeValue::String(".json".into()),
1811 make_span(),
1812 )],
1813 make_span(),
1814 ));
1815
1816 field.extract_validation_from_attributes();
1817 assert!(field.has_validation());
1818 }
1819
1820 #[test]
1821 fn test_extract_validation_contains() {
1822 let mut field = make_field(
1823 "text",
1824 FieldType::Scalar(ScalarType::String),
1825 TypeModifier::Required,
1826 );
1827 field.attributes.push(Attribute::new(
1828 Ident::new("validate.contains", make_span()),
1829 vec![AttributeArg::positional(
1830 AttributeValue::String("keyword".into()),
1831 make_span(),
1832 )],
1833 make_span(),
1834 ));
1835
1836 field.extract_validation_from_attributes();
1837 assert!(field.has_validation());
1838 }
1839
1840 #[test]
1841 fn test_extract_validation_after() {
1842 let mut field = make_field(
1843 "start_date",
1844 FieldType::Scalar(ScalarType::DateTime),
1845 TypeModifier::Required,
1846 );
1847 field.attributes.push(Attribute::new(
1848 Ident::new("validate.after", make_span()),
1849 vec![AttributeArg::positional(
1850 AttributeValue::String("2024-01-01".into()),
1851 make_span(),
1852 )],
1853 make_span(),
1854 ));
1855
1856 field.extract_validation_from_attributes();
1857 assert!(field.has_validation());
1858 }
1859
1860 #[test]
1861 fn test_extract_validation_before() {
1862 let mut field = make_field(
1863 "end_date",
1864 FieldType::Scalar(ScalarType::DateTime),
1865 TypeModifier::Required,
1866 );
1867 field.attributes.push(Attribute::new(
1868 Ident::new("validate.before", make_span()),
1869 vec![AttributeArg::positional(
1870 AttributeValue::String("2025-12-31".into()),
1871 make_span(),
1872 )],
1873 make_span(),
1874 ));
1875
1876 field.extract_validation_from_attributes();
1877 assert!(field.has_validation());
1878 }
1879
1880 #[test]
1881 fn test_extract_validation_custom() {
1882 let mut field = make_field(
1883 "password",
1884 FieldType::Scalar(ScalarType::String),
1885 TypeModifier::Required,
1886 );
1887 field.attributes.push(Attribute::new(
1888 Ident::new("validate.custom", make_span()),
1889 vec![AttributeArg::positional(
1890 AttributeValue::String("strongPassword".into()),
1891 make_span(),
1892 )],
1893 make_span(),
1894 ));
1895
1896 field.extract_validation_from_attributes();
1897 assert!(field.has_validation());
1898 }
1899
1900 #[test]
1901 fn test_extract_validation_length() {
1902 let mut field = make_field(
1903 "bio",
1904 FieldType::Scalar(ScalarType::String),
1905 TypeModifier::Required,
1906 );
1907 field.attributes.push(Attribute::new(
1908 Ident::new("validate.length", make_span()),
1909 vec![
1910 AttributeArg::positional(AttributeValue::Int(10), make_span()),
1911 AttributeArg::positional(AttributeValue::Int(500), make_span()),
1912 ],
1913 make_span(),
1914 ));
1915
1916 field.extract_validation_from_attributes();
1917 assert!(field.has_validation());
1918 }
1919
1920 #[test]
1921 fn test_extract_validation_cuid() {
1922 let mut field = make_field(
1923 "cuid_field",
1924 FieldType::Scalar(ScalarType::String),
1925 TypeModifier::Required,
1926 );
1927 field.attributes.push(Attribute::simple(
1928 Ident::new("validate.cuid", make_span()),
1929 make_span(),
1930 ));
1931
1932 field.extract_validation_from_attributes();
1933 assert!(field.has_validation());
1934 }
1935
1936 #[test]
1937 fn test_extract_validation_cuid2() {
1938 let mut field = make_field(
1939 "cuid2_field",
1940 FieldType::Scalar(ScalarType::String),
1941 TypeModifier::Required,
1942 );
1943 field.attributes.push(Attribute::simple(
1944 Ident::new("validate.cuid2", make_span()),
1945 make_span(),
1946 ));
1947
1948 field.extract_validation_from_attributes();
1949 assert!(field.has_validation());
1950 }
1951
1952 #[test]
1953 fn test_extract_validation_nanoid() {
1954 let mut field = make_field(
1955 "nanoid_field",
1956 FieldType::Scalar(ScalarType::String),
1957 TypeModifier::Required,
1958 );
1959 field.attributes.push(Attribute::simple(
1960 Ident::new("validate.nanoid", make_span()),
1961 make_span(),
1962 ));
1963
1964 field.extract_validation_from_attributes();
1965 assert!(field.has_validation());
1966 }
1967
1968 #[test]
1969 fn test_extract_validation_ulid() {
1970 let mut field = make_field(
1971 "ulid_field",
1972 FieldType::Scalar(ScalarType::String),
1973 TypeModifier::Required,
1974 );
1975 field.attributes.push(Attribute::simple(
1976 Ident::new("validate.ulid", make_span()),
1977 make_span(),
1978 ));
1979
1980 field.extract_validation_from_attributes();
1981 assert!(field.has_validation());
1982 }
1983
1984 #[test]
1985 fn test_extract_validation_noWhitespace() {
1986 let mut field = make_field(
1987 "username",
1988 FieldType::Scalar(ScalarType::String),
1989 TypeModifier::Required,
1990 );
1991 field.attributes.push(Attribute::simple(
1992 Ident::new("validate.noWhitespace", make_span()),
1993 make_span(),
1994 ));
1995
1996 field.extract_validation_from_attributes();
1997 assert!(field.has_validation());
1998 }
1999
2000 #[test]
2001 fn test_extract_validation_creditCard() {
2002 let mut field = make_field(
2003 "card_number",
2004 FieldType::Scalar(ScalarType::String),
2005 TypeModifier::Required,
2006 );
2007 field.attributes.push(Attribute::simple(
2008 Ident::new("validate.creditCard", make_span()),
2009 make_span(),
2010 ));
2011
2012 field.extract_validation_from_attributes();
2013 assert!(field.has_validation());
2014 }
2015
2016 #[test]
2017 fn test_extract_validation_phone() {
2018 let mut field = make_field(
2019 "phone_number",
2020 FieldType::Scalar(ScalarType::String),
2021 TypeModifier::Required,
2022 );
2023 field.attributes.push(Attribute::simple(
2024 Ident::new("validate.phone", make_span()),
2025 make_span(),
2026 ));
2027
2028 field.extract_validation_from_attributes();
2029 assert!(field.has_validation());
2030 }
2031
2032 #[test]
2033 fn test_extract_validation_nonPositive() {
2034 let mut field = make_field(
2035 "debt",
2036 FieldType::Scalar(ScalarType::Float),
2037 TypeModifier::Required,
2038 );
2039 field.attributes.push(Attribute::simple(
2040 Ident::new("validate.nonPositive", make_span()),
2041 make_span(),
2042 ));
2043
2044 field.extract_validation_from_attributes();
2045 assert!(field.has_validation());
2046 }
2047
2048 #[test]
2049 fn test_extract_validation_pastOrPresent() {
2050 let mut field = make_field(
2051 "login_date",
2052 FieldType::Scalar(ScalarType::DateTime),
2053 TypeModifier::Required,
2054 );
2055 field.attributes.push(Attribute::simple(
2056 Ident::new("validate.pastOrPresent", make_span()),
2057 make_span(),
2058 ));
2059
2060 field.extract_validation_from_attributes();
2061 assert!(field.has_validation());
2062 }
2063
2064 #[test]
2065 fn test_extract_validation_futureOrPresent() {
2066 let mut field = make_field(
2067 "schedule_date",
2068 FieldType::Scalar(ScalarType::DateTime),
2069 TypeModifier::Required,
2070 );
2071 field.attributes.push(Attribute::simple(
2072 Ident::new("validate.futureOrPresent", make_span()),
2073 make_span(),
2074 ));
2075
2076 field.extract_validation_from_attributes();
2077 assert!(field.has_validation());
2078 }
2079
2080 #[test]
2081 fn test_extract_validation_required() {
2082 let mut field = make_field(
2083 "important",
2084 FieldType::Scalar(ScalarType::String),
2085 TypeModifier::Optional,
2086 );
2087 field.attributes.push(Attribute::simple(
2088 Ident::new("validate.required", make_span()),
2089 make_span(),
2090 ));
2091
2092 field.extract_validation_from_attributes();
2093 assert!(field.has_validation());
2094 assert!(field.is_validated_required());
2095 }
2096
2097 #[test]
2098 fn test_extract_validation_notEmpty() {
2099 let mut field = make_field(
2100 "content",
2101 FieldType::Scalar(ScalarType::String),
2102 TypeModifier::Required,
2103 );
2104 field.attributes.push(Attribute::simple(
2105 Ident::new("validate.notEmpty", make_span()),
2106 make_span(),
2107 ));
2108
2109 field.extract_validation_from_attributes();
2110 assert!(field.has_validation());
2111 }
2112
2113 #[test]
2114 fn test_extract_validation_unknown_validator() {
2115 let mut field = make_field(
2116 "field",
2117 FieldType::Scalar(ScalarType::String),
2118 TypeModifier::Required,
2119 );
2120 field.attributes.push(Attribute::simple(
2121 Ident::new("validate.unknownValidator", make_span()),
2122 make_span(),
2123 ));
2124
2125 field.extract_validation_from_attributes();
2126 assert!(!field.has_validation());
2128 }
2129
2130 #[test]
2131 fn test_extract_validation_items() {
2132 let mut field = make_field(
2133 "tags",
2134 FieldType::Scalar(ScalarType::String),
2135 TypeModifier::List,
2136 );
2137 field.attributes.push(Attribute::new(
2138 Ident::new("validate.items", make_span()),
2139 vec![
2140 AttributeArg::positional(AttributeValue::Int(1), make_span()),
2141 AttributeArg::positional(AttributeValue::Int(10), make_span()),
2142 ],
2143 make_span(),
2144 ));
2145
2146 field.extract_validation_from_attributes();
2147 assert!(field.has_validation());
2148 }
2149
2150 #[test]
2153 fn test_extract_validate_attribute_with_ident() {
2154 let mut field = make_field(
2155 "email",
2156 FieldType::Scalar(ScalarType::String),
2157 TypeModifier::Required,
2158 );
2159 field.attributes.push(Attribute::new(
2160 Ident::new("validate", make_span()),
2161 vec![AttributeArg::positional(
2162 AttributeValue::Ident("email".into()),
2163 make_span(),
2164 )],
2165 make_span(),
2166 ));
2167
2168 field.extract_validation_from_attributes();
2169 assert!(field.has_validation());
2170 assert_eq!(field.validation_rules().len(), 1);
2171 }
2172
2173 #[test]
2174 fn test_extract_validate_attribute_with_function() {
2175 let mut field = make_field(
2176 "name",
2177 FieldType::Scalar(ScalarType::String),
2178 TypeModifier::Required,
2179 );
2180 field.attributes.push(Attribute::new(
2181 Ident::new("validate", make_span()),
2182 vec![AttributeArg::positional(
2183 AttributeValue::Function("minLength".into(), vec![AttributeValue::Int(3)]),
2184 make_span(),
2185 )],
2186 make_span(),
2187 ));
2188
2189 field.extract_validation_from_attributes();
2190 assert!(field.has_validation());
2191 assert_eq!(field.validation_rules().len(), 1);
2192 }
2193
2194 #[test]
2195 fn test_extract_validate_multiple_validators() {
2196 let mut field = make_field(
2197 "email",
2198 FieldType::Scalar(ScalarType::String),
2199 TypeModifier::Required,
2200 );
2201 field.attributes.push(Attribute::new(
2202 Ident::new("validate", make_span()),
2203 vec![
2204 AttributeArg::positional(AttributeValue::Ident("email".into()), make_span()),
2205 AttributeArg::positional(
2206 AttributeValue::Function("maxLength".into(), vec![AttributeValue::Int(255)]),
2207 make_span(),
2208 ),
2209 ],
2210 make_span(),
2211 ));
2212
2213 field.extract_validation_from_attributes();
2214 assert!(field.has_validation());
2215 assert_eq!(field.validation_rules().len(), 2);
2216 }
2217}