Skip to main content

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