prax_schema/ast/
field.rs

1//! Field definitions for the Prax schema AST.
2
3use serde::{Deserialize, Serialize};
4
5use super::{
6    Attribute, Documentation, EnhancedDocumentation, FieldAttributes, FieldType, FieldValidation,
7    Ident, Span, TypeModifier, ValidationRule, ValidationType,
8};
9
10/// A field in a model or composite type.
11#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
12pub struct Field {
13    /// Field name.
14    pub name: Ident,
15    /// Field type.
16    pub field_type: FieldType,
17    /// Type modifier (optional, list, etc.).
18    pub modifier: TypeModifier,
19    /// Raw attributes as parsed.
20    pub attributes: Vec<Attribute>,
21    /// Documentation comment.
22    pub documentation: Option<Documentation>,
23    /// Validation rules for this field.
24    pub validation: FieldValidation,
25    /// Source location.
26    pub span: Span,
27}
28
29impl Field {
30    /// Create a new field.
31    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    /// Get the field name as a string.
50    pub fn name(&self) -> &str {
51        self.name.as_str()
52    }
53
54    /// Check if the field is optional.
55    pub fn is_optional(&self) -> bool {
56        self.modifier.is_optional()
57    }
58
59    /// Check if the field is a list.
60    pub fn is_list(&self) -> bool {
61        self.modifier.is_list()
62    }
63
64    /// Check if this field has a specific attribute.
65    pub fn has_attribute(&self, name: &str) -> bool {
66        self.attributes.iter().any(|a| a.is(name))
67    }
68
69    /// Get an attribute by name.
70    pub fn get_attribute(&self, name: &str) -> Option<&Attribute> {
71        self.attributes.iter().find(|a| a.is(name))
72    }
73
74    /// Check if this is a primary key field.
75    pub fn is_id(&self) -> bool {
76        self.has_attribute("id")
77    }
78
79    /// Check if this field has a unique constraint.
80    pub fn is_unique(&self) -> bool {
81        self.has_attribute("unique")
82    }
83
84    /// Check if this is a relation field.
85    pub fn is_relation(&self) -> bool {
86        self.field_type.is_relation() || self.has_attribute("relation")
87    }
88
89    /// Extract structured field attributes.
90    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                    // Parse native type like @db.VarChar(255)
111                    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                    // Parse relation attributes
122                    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                    // First positional arg is the relation name
131                    if let Some(val) = attr.first_arg() {
132                        rel.name = val.as_string().map(String::from);
133                    }
134
135                    // Named arguments
136                    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    /// Set documentation.
167    pub fn with_documentation(mut self, doc: Documentation) -> Self {
168        self.documentation = Some(doc);
169        self
170    }
171
172    /// Set enhanced documentation (with validation extraction).
173    pub fn with_enhanced_documentation(mut self, doc: EnhancedDocumentation) -> Self {
174        self.documentation = Some(Documentation::new(&doc.text, doc.span));
175        // Merge validation rules from documentation
176        for rule in doc.validation.rules {
177            self.validation.add_rule(rule);
178        }
179        self
180    }
181
182    /// Set validation rules.
183    pub fn with_validation(mut self, validation: FieldValidation) -> Self {
184        self.validation = validation;
185        self
186    }
187
188    /// Add a validation rule.
189    pub fn add_validation_rule(&mut self, rule: ValidationRule) {
190        self.validation.add_rule(rule);
191    }
192
193    /// Check if this field has any validation rules.
194    pub fn has_validation(&self) -> bool {
195        !self.validation.is_empty()
196    }
197
198    /// Get all validation rules for this field.
199    pub fn validation_rules(&self) -> &[ValidationRule] {
200        &self.validation.rules
201    }
202
203    /// Check if this field is required (via validation).
204    pub fn is_validated_required(&self) -> bool {
205        self.validation.is_required()
206    }
207
208    /// Extract validation rules from @validate attributes.
209    ///
210    /// This parses attributes like:
211    /// - `@validate.email`
212    /// - `@validate.minLength(5)`
213    /// - `@validate.range(0, 100)`
214    pub fn extract_validation_from_attributes(&mut self) {
215        for attr in &self.attributes {
216            let attr_name = attr.name();
217
218            // Check for @validate prefix
219            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                // Parse @validate(email, minLength(5), ...)
225                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    /// Parse a single @validate.* attribute.
235    fn parse_validate_attribute(
236        &self,
237        validator_name: &str,
238        attr: &Attribute,
239    ) -> Option<ValidationRule> {
240        let span = attr.span;
241
242        // Parse based on validator name
243        let rule_type = match validator_name {
244            // String validators
245            "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            // Numeric validators
269            "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            // Array validators
277            "unique" => ValidationType::Unique,
278            "nonEmpty" => ValidationType::NonEmpty,
279
280            // Date validators
281            "past" => ValidationType::Past,
282            "future" => ValidationType::Future,
283            "pastOrPresent" => ValidationType::PastOrPresent,
284            "futureOrPresent" => ValidationType::FutureOrPresent,
285
286            // General validators
287            "required" => ValidationType::Required,
288            "notEmpty" => ValidationType::NotEmpty,
289
290            // Validators with arguments
291            "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    /// Parse a @validate(...) argument.
399    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                // Simple validators like @validate(email, uuid)
405                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                // Validators with args like @validate(minLength(5))
448                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        // Type with modifier
506        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        // Attributes
514        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    // ==================== Field Construction Tests ====================
557
558    #[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    // ==================== Field Name Tests ====================
602
603    #[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    // ==================== Field Modifier Tests ====================
614
615    #[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    // ==================== Field Attribute Tests ====================
667
668    #[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    // ==================== Field Relation Tests ====================
751
752    #[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    // ==================== Extract Attributes Tests ====================
781
782    #[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    // ==================== Field Display Tests ====================
1027
1028    #[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    // ==================== Field Equality Tests ====================
1113
1114    #[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    // ==================== Validation Tests ====================
1175
1176    #[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        // Unknown validators should be ignored
2127        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    // ==================== Comprehensive validate() attribute tests ====================
2151
2152    #[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}