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