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 attr_name.starts_with("validate.") {
220                let validator_name = &attr_name[9..]; // Remove "validate." prefix
221                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                // Parse @validate(email, minLength(5), ...)
226                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    /// Parse a single @validate.* attribute.
236    fn parse_validate_attribute(
237        &self,
238        validator_name: &str,
239        attr: &Attribute,
240    ) -> Option<ValidationRule> {
241        let span = attr.span;
242
243        // Parse based on validator name
244        let rule_type = match validator_name {
245            // String validators
246            "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            // Numeric validators
270            "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            // Array validators
278            "unique" => ValidationType::Unique,
279            "nonEmpty" => ValidationType::NonEmpty,
280
281            // Date validators
282            "past" => ValidationType::Past,
283            "future" => ValidationType::Future,
284            "pastOrPresent" => ValidationType::PastOrPresent,
285            "futureOrPresent" => ValidationType::FutureOrPresent,
286
287            // General validators
288            "required" => ValidationType::Required,
289            "notEmpty" => ValidationType::NotEmpty,
290
291            // Validators with arguments
292            "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    /// Parse a @validate(...) argument.
395    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                // Simple validators like @validate(email, uuid)
401                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                // Validators with args like @validate(minLength(5))
444                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        // Type with modifier
500        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        // Attributes
508        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    // ==================== Field Construction Tests ====================
551
552    #[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    // ==================== Field Name Tests ====================
596
597    #[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    // ==================== Field Modifier Tests ====================
608
609    #[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    // ==================== Field Attribute Tests ====================
661
662    #[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    // ==================== Field Relation Tests ====================
745
746    #[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    // ==================== Extract Attributes Tests ====================
775
776    #[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    // ==================== Field Display Tests ====================
1021
1022    #[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    // ==================== Field Equality Tests ====================
1107
1108    #[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    // ==================== Validation Tests ====================
1169
1170    #[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        // Unknown validators should be ignored
2100        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    // ==================== Comprehensive validate() attribute tests ====================
2124
2125    #[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}