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