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