domainstack_derive/
lib.rs

1//! # domainstack-derive
2//!
3//! Derive macros for domainstack validation and schema generation.
4//!
5//! ## Provided Macros
6//!
7//! - **`#[derive(Validate)]`** - Automatic validation implementation with `#[validate(...)]` attributes
8//! - **`#[derive(ToSchema)]`** - OpenAPI 3.0 schema generation from validation rules
9//! - **`#[derive(ValidateOnDeserialize)]`** - Validate automatically during serde deserialization (requires `serde` feature)
10//!
11//! ## `#[derive(Validate)]`
12//!
13//! Generates a `Validate` trait implementation that validates all fields with `#[validate(...)]` attributes.
14//!
15//! ### Structs with Named Fields
16//!
17//! ```rust
18//! use domainstack::Validate;
19//!
20//! #[derive(Debug, Validate)]
21//! struct User {
22//!     #[validate(length(min = 2, max = 50))]
23//!     name: String,
24//!
25//!     #[validate(range(min = 18, max = 120))]
26//!     age: u8,
27//! }
28//!
29//! let user = User { name: "Alice".to_string(), age: 30 };
30//! assert!(user.validate().is_ok());
31//! ```
32//!
33//! ### Tuple Structs (Newtypes)
34//!
35//! Perfect for type-safe wrappers with validation:
36//!
37//! ```rust
38//! use domainstack::Validate;
39//!
40//! #[derive(Debug, Validate)]
41//! struct Email(#[validate(email)] String);
42//!
43//! #[derive(Debug, Validate)]
44//! struct Age(#[validate(range(min = 0, max = 150))] u8);
45//!
46//! let email = Email("user@example.com".to_string());
47//! assert!(email.validate().is_ok());
48//!
49//! let age = Age(25);
50//! assert!(age.validate().is_ok());
51//! ```
52//!
53//! ### Enums
54//!
55//! Supports unit, tuple, and struct variants:
56//!
57//! ```rust
58//! use domainstack::Validate;
59//!
60//! #[derive(Debug, Validate)]
61//! enum PaymentMethod {
62//!     // Unit variant - always valid
63//!     Cash,
64//!
65//!     // Tuple variant
66//!     Card(#[validate(length(min = 13, max = 19))] String),
67//!
68//!     // Struct variant
69//!     BankTransfer {
70//!         #[validate(alphanumeric)]
71//!         account: String,
72//!         #[validate(length(min = 6, max = 11))]
73//!         routing: String,
74//!     },
75//! }
76//!
77//! let cash = PaymentMethod::Cash;
78//! assert!(cash.validate().is_ok());
79//!
80//! let card = PaymentMethod::Card("4111111111111111".to_string());
81//! assert!(card.validate().is_ok());
82//! ```
83//!
84//! ## `#[derive(ToSchema)]`
85//!
86//! Generates a `ToSchema` trait implementation that produces OpenAPI 3.0 schemas from validation rules.
87//!
88//! ```rust,ignore
89//! use domainstack_derive::{Validate, ToSchema};
90//!
91//! #[derive(Validate, ToSchema)]
92//! struct User {
93//!     #[validate(email)]
94//!     #[validate(max_len = 255)]
95//!     email: String,
96//!
97//!     #[validate(range(min = 18, max = 120))]
98//!     age: u8,
99//! }
100//!
101//! let schema = User::schema();
102//! // → email: { type: "string", format: "email", maxLength: 255 }
103//! // → age: { type: "integer", minimum: 18, maximum: 120 }
104//! ```
105//!
106//! ## `#[derive(ValidateOnDeserialize)]`
107//!
108//! Validates during serde deserialization, returning validation errors instead of serde errors.
109//!
110//! ```rust,ignore
111//! use domainstack_derive::ValidateOnDeserialize;
112//!
113//! #[derive(ValidateOnDeserialize)]
114//! struct User {
115//!     #[validate(email)]
116//!     email: String,
117//!
118//!     #[validate(range(min = 18, max = 120))]
119//!     age: u8,
120//! }
121//!
122//! // Validation happens automatically during deserialization
123//! let user: User = serde_json::from_str(r#"{"email": "alice@example.com", "age": 30}"#)?;
124//! // ↑ If this succeeds, user is guaranteed valid!
125//! ```
126
127use proc_macro::TokenStream;
128use quote::quote;
129use syn::{parse_macro_input, Attribute, Data, DeriveInput, Expr, Field, Fields, Lit, Meta};
130
131mod schema;
132
133#[proc_macro_derive(Validate, attributes(validate))]
134pub fn derive_validate(input: TokenStream) -> TokenStream {
135    let input = parse_macro_input!(input as DeriveInput);
136
137    match generate_validate_impl(&input) {
138        Ok(tokens) => tokens.into(),
139        Err(err) => err.to_compile_error().into(),
140    }
141}
142
143#[proc_macro_derive(ToSchema, attributes(schema, validate))]
144pub fn derive_to_schema(input: TokenStream) -> TokenStream {
145    let input = parse_macro_input!(input as DeriveInput);
146
147    match schema::derive_to_schema_impl(input) {
148        Ok(tokens) => tokens.into(),
149        Err(err) => err.to_compile_error().into(),
150    }
151}
152
153#[cfg(feature = "serde")]
154#[proc_macro_derive(ValidateOnDeserialize, attributes(validate, serde))]
155pub fn derive_validate_on_deserialize(input: TokenStream) -> TokenStream {
156    let input = parse_macro_input!(input as DeriveInput);
157
158    match generate_validate_on_deserialize_impl(&input) {
159        Ok(tokens) => tokens.into(),
160        Err(err) => err.to_compile_error().into(),
161    }
162}
163
164#[cfg(feature = "serde")]
165fn generate_validate_on_deserialize_impl(
166    input: &DeriveInput,
167) -> syn::Result<proc_macro2::TokenStream> {
168    let name = &input.ident;
169    let generics = &input.generics;
170    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
171
172    // Only support structs with named fields
173    let fields = match &input.data {
174        Data::Struct(data) => match &data.fields {
175            Fields::Named(fields) => &fields.named,
176            _ => {
177                return Err(syn::Error::new_spanned(
178                    input,
179                    "#[derive(ValidateOnDeserialize)] only supports structs with named fields",
180                ))
181            }
182        },
183        _ => {
184            return Err(syn::Error::new_spanned(
185                input,
186                "#[derive(ValidateOnDeserialize)] only supports structs",
187            ))
188        }
189    };
190
191    // Parse struct-level validation checks (reuse existing function)
192    let struct_validations = parse_struct_attributes(input)?;
193
194    // Parse validation rules for each field (reuse existing function)
195    let mut field_validations = Vec::new();
196    for field in fields {
197        let field_name = field.ident.as_ref().unwrap().clone();
198        let field_type = field.ty.clone();
199        let rules = parse_field_attributes(field)?;
200
201        if !rules.is_empty() {
202            field_validations.push(FieldValidation {
203                field_name,
204                field_type,
205                rules,
206            });
207        }
208    }
209
210    // Generate validation code for each field (reuse existing function)
211    let field_validation_code = field_validations.iter().map(generate_field_validation);
212
213    // Generate validation code for struct-level checks (reuse existing function)
214    let struct_validation_code = struct_validations.iter().map(generate_struct_validation);
215
216    // Generate intermediate struct name
217    let intermediate_name = syn::Ident::new(&format!("{}Intermediate", name), name.span());
218
219    // Extract field names and types
220    let field_names: Vec<_> = fields.iter().map(|f| f.ident.as_ref().unwrap()).collect();
221
222    let field_types: Vec<_> = fields.iter().map(|f| &f.ty).collect();
223
224    // Forward serde attributes from the original struct to the intermediate struct
225    // This ensures rename, rename_all, etc. work correctly
226    let struct_serde_attrs: Vec<_> = input
227        .attrs
228        .iter()
229        .filter(|attr| attr.path().is_ident("serde"))
230        .collect();
231
232    // Forward serde attributes for each field
233    let field_serde_attrs: Vec<Vec<_>> = fields
234        .iter()
235        .map(|f| {
236            f.attrs
237                .iter()
238                .filter(|attr| attr.path().is_ident("serde"))
239                .collect()
240        })
241        .collect();
242
243    // Generate the expanded code
244    let expanded = quote! {
245        // Intermediate struct for deserialization
246        #[derive(::serde::Deserialize)]
247        #[doc(hidden)]
248        #( #struct_serde_attrs )*
249        struct #intermediate_name #generics {
250            #(
251                #( #field_serde_attrs )*
252                #field_names: #field_types,
253            )*
254        }
255
256        // Implement Deserialize for the main struct
257        impl<'de> ::serde::Deserialize<'de> for #name #ty_generics #where_clause {
258            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
259            where
260                D: ::serde::Deserializer<'de>,
261            {
262                // Phase 1: Deserialize into intermediate struct
263                let intermediate = #intermediate_name::deserialize(deserializer)?;
264
265                // Phase 2: Construct the final struct
266                let value = #name {
267                    #( #field_names: intermediate.#field_names, )*
268                };
269
270                // Phase 3: Validate using fully qualified syntax
271                <#name #ty_generics as ::domainstack::Validate>::validate(&value)
272                    .map_err(|e| ::serde::de::Error::custom(format!("Validation failed: {}", e)))?;
273
274                Ok(value)
275            }
276        }
277
278        // Generate Validate implementation with actual validation logic
279        impl #impl_generics ::domainstack::Validate for #name #ty_generics #where_clause {
280            fn validate(&self) -> Result<(), ::domainstack::ValidationError> {
281                let mut err = ::domainstack::ValidationError::default();
282
283                // Field-level validations
284                #(#field_validation_code)*
285
286                // Struct-level validations (cross-field checks)
287                #(#struct_validation_code)*
288
289                if err.is_empty() { Ok(()) } else { Err(err) }
290            }
291        }
292    };
293
294    Ok(expanded)
295}
296
297#[derive(Debug, Clone)]
298#[allow(dead_code)]
299enum ValidationRule {
300    // Existing rules
301    Length {
302        min: Option<usize>,
303        max: Option<usize>,
304        code: Option<String>,
305        message: Option<String>,
306    },
307    Range {
308        min: Option<proc_macro2::TokenStream>,
309        max: Option<proc_macro2::TokenStream>,
310        code: Option<String>,
311        message: Option<String>,
312    },
313    Nested,
314    Each(Box<ValidationRule>),
315    Custom(String),
316
317    // New rich syntax rules
318    Email,
319    Url,
320    MinLen(usize),
321    MaxLen(usize),
322    Alphanumeric,
323    Ascii,
324    AlphaOnly,
325    NumericString,
326    NonEmpty,
327    NonBlank,
328    NoWhitespace,
329    Contains(String),
330    StartsWith(String),
331    EndsWith(String),
332    MatchesRegex(String),
333    Min(proc_macro2::TokenStream),
334    Max(proc_macro2::TokenStream),
335    Positive,
336    Negative,
337    NonZero,
338    Finite,
339    MultipleOf(proc_macro2::TokenStream),
340    Equals(proc_macro2::TokenStream),
341    NotEquals(proc_macro2::TokenStream),
342    OneOf(Vec<String>),
343    MinItems(usize),
344    MaxItems(usize),
345    Unique,
346}
347
348#[derive(Debug, Clone)]
349struct StructValidation {
350    check: String,
351    code: Option<String>,
352    message: Option<String>,
353    when: Option<String>,
354}
355
356#[derive(Debug)]
357#[allow(dead_code)]
358struct FieldValidation {
359    field_name: syn::Ident,
360    field_type: syn::Type,
361    rules: Vec<ValidationRule>,
362}
363
364fn generate_validate_impl(input: &DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
365    let name = &input.ident;
366    let generics = &input.generics;
367    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
368
369    match &input.data {
370        Data::Struct(data) => match &data.fields {
371            Fields::Named(fields) => generate_named_struct_validate(
372                name,
373                &impl_generics,
374                &ty_generics,
375                where_clause,
376                &fields.named,
377                input,
378            ),
379            Fields::Unnamed(fields) => generate_tuple_struct_validate(
380                name,
381                &impl_generics,
382                &ty_generics,
383                where_clause,
384                &fields.unnamed,
385                input,
386            ),
387            Fields::Unit => {
388                // Unit structs always pass validation
389                Ok(quote! {
390                    impl #impl_generics domainstack::Validate for #name #ty_generics #where_clause {
391                        fn validate(&self) -> Result<(), domainstack::ValidationError> {
392                            Ok(())
393                        }
394                    }
395                })
396            }
397        },
398        Data::Enum(data) => generate_enum_validate(
399            name,
400            &impl_generics,
401            &ty_generics,
402            where_clause,
403            &data.variants,
404            input,
405        ),
406        Data::Union(_) => Err(syn::Error::new_spanned(
407            input,
408            "#[derive(Validate)] does not support unions",
409        )),
410    }
411}
412
413/// Generate Validate impl for structs with named fields
414fn generate_named_struct_validate(
415    name: &syn::Ident,
416    impl_generics: &syn::ImplGenerics,
417    ty_generics: &syn::TypeGenerics,
418    where_clause: Option<&syn::WhereClause>,
419    fields: &syn::punctuated::Punctuated<Field, syn::Token![,]>,
420    input: &DeriveInput,
421) -> syn::Result<proc_macro2::TokenStream> {
422    // Parse struct-level validation checks
423    let struct_validations = parse_struct_attributes(input)?;
424
425    // Parse validation rules for each field
426    let mut field_validations = Vec::new();
427    for field in fields {
428        let field_name = field.ident.as_ref().unwrap().clone();
429        let field_type = field.ty.clone();
430        let rules = parse_field_attributes(field)?;
431
432        if !rules.is_empty() {
433            field_validations.push(FieldValidation {
434                field_name,
435                field_type,
436                rules,
437            });
438        }
439    }
440
441    // Generate validation code for each field
442    let field_validation_code = field_validations.iter().map(generate_field_validation);
443
444    // Generate validation code for struct-level checks
445    let struct_validation_code = struct_validations.iter().map(generate_struct_validation);
446
447    let expanded = quote! {
448        impl #impl_generics domainstack::Validate for #name #ty_generics #where_clause {
449            fn validate(&self) -> Result<(), domainstack::ValidationError> {
450                let mut err = domainstack::ValidationError::default();
451
452                // Field-level validations
453                #(#field_validation_code)*
454
455                // Struct-level validations (cross-field checks)
456                #(#struct_validation_code)*
457
458                if err.is_empty() { Ok(()) } else { Err(err) }
459            }
460        }
461    };
462
463    Ok(expanded)
464}
465
466/// Validation info for tuple struct fields (indexed)
467#[derive(Debug)]
468#[allow(dead_code)]
469struct TupleFieldValidation {
470    field_index: usize,
471    field_type: syn::Type,
472    rules: Vec<ValidationRule>,
473}
474
475/// Generate Validate impl for tuple structs (newtypes)
476fn generate_tuple_struct_validate(
477    name: &syn::Ident,
478    impl_generics: &syn::ImplGenerics,
479    ty_generics: &syn::TypeGenerics,
480    where_clause: Option<&syn::WhereClause>,
481    fields: &syn::punctuated::Punctuated<Field, syn::Token![,]>,
482    input: &DeriveInput,
483) -> syn::Result<proc_macro2::TokenStream> {
484    // Parse struct-level validation checks
485    let struct_validations = parse_struct_attributes(input)?;
486
487    // Parse validation rules for each field by index
488    let mut field_validations = Vec::new();
489    for (index, field) in fields.iter().enumerate() {
490        let field_type = field.ty.clone();
491        let rules = parse_field_attributes(field)?;
492
493        if !rules.is_empty() {
494            field_validations.push(TupleFieldValidation {
495                field_index: index,
496                field_type,
497                rules,
498            });
499        }
500    }
501
502    // Generate validation code for each field
503    let field_validation_code = field_validations
504        .iter()
505        .map(generate_tuple_field_validation);
506
507    // Generate validation code for struct-level checks
508    let struct_validation_code = struct_validations.iter().map(generate_struct_validation);
509
510    let expanded = quote! {
511        impl #impl_generics domainstack::Validate for #name #ty_generics #where_clause {
512            fn validate(&self) -> Result<(), domainstack::ValidationError> {
513                let mut err = domainstack::ValidationError::default();
514
515                // Field-level validations
516                #(#field_validation_code)*
517
518                // Struct-level validations (cross-field checks)
519                #(#struct_validation_code)*
520
521                if err.is_empty() { Ok(()) } else { Err(err) }
522            }
523        }
524    };
525
526    Ok(expanded)
527}
528
529/// Generate Validate impl for enums
530fn generate_enum_validate(
531    name: &syn::Ident,
532    impl_generics: &syn::ImplGenerics,
533    ty_generics: &syn::TypeGenerics,
534    where_clause: Option<&syn::WhereClause>,
535    variants: &syn::punctuated::Punctuated<syn::Variant, syn::Token![,]>,
536    _input: &DeriveInput,
537) -> syn::Result<proc_macro2::TokenStream> {
538    let mut variant_arms = Vec::new();
539
540    for variant in variants {
541        let variant_name = &variant.ident;
542
543        match &variant.fields {
544            Fields::Named(fields) => {
545                // Struct variant: EnumName::Variant { field1, field2, ... }
546                let field_names: Vec<_> = fields
547                    .named
548                    .iter()
549                    .map(|f| f.ident.as_ref().unwrap())
550                    .collect();
551
552                // Parse validation rules for each field
553                let mut validations = Vec::new();
554                for field in &fields.named {
555                    let field_name = field.ident.as_ref().unwrap();
556                    let field_name_str = field_name.to_string();
557                    let rules = parse_field_attributes(field)?;
558
559                    for rule in rules {
560                        let validation_code =
561                            generate_enum_field_validation(field_name, &field_name_str, &rule);
562                        validations.push(validation_code);
563                    }
564                }
565
566                if validations.is_empty() {
567                    variant_arms.push(quote! {
568                        #name::#variant_name { .. } => {}
569                    });
570                } else {
571                    variant_arms.push(quote! {
572                        #name::#variant_name { #(#field_names),* } => {
573                            #(#validations)*
574                        }
575                    });
576                }
577            }
578            Fields::Unnamed(fields) => {
579                // Tuple variant: EnumName::Variant(field0, field1, ...)
580                let field_bindings: Vec<_> = (0..fields.unnamed.len())
581                    .map(|i| {
582                        syn::Ident::new(&format!("field_{}", i), proc_macro2::Span::call_site())
583                    })
584                    .collect();
585
586                // Parse validation rules for each field
587                let mut validations = Vec::new();
588                for (index, field) in fields.unnamed.iter().enumerate() {
589                    let binding = &field_bindings[index];
590                    let field_name_str = index.to_string();
591                    let rules = parse_field_attributes(field)?;
592
593                    for rule in rules {
594                        let validation_code =
595                            generate_enum_tuple_field_validation(binding, &field_name_str, &rule);
596                        validations.push(validation_code);
597                    }
598                }
599
600                if validations.is_empty() {
601                    variant_arms.push(quote! {
602                        #name::#variant_name(..) => {}
603                    });
604                } else {
605                    variant_arms.push(quote! {
606                        #name::#variant_name(#(#field_bindings),*) => {
607                            #(#validations)*
608                        }
609                    });
610                }
611            }
612            Fields::Unit => {
613                // Unit variant: EnumName::Variant
614                variant_arms.push(quote! {
615                    #name::#variant_name => {}
616                });
617            }
618        }
619    }
620
621    let expanded = quote! {
622        impl #impl_generics domainstack::Validate for #name #ty_generics #where_clause {
623            fn validate(&self) -> Result<(), domainstack::ValidationError> {
624                let mut err = domainstack::ValidationError::default();
625
626                match self {
627                    #(#variant_arms)*
628                }
629
630                if err.is_empty() { Ok(()) } else { Err(err) }
631            }
632        }
633    };
634
635    Ok(expanded)
636}
637
638fn parse_struct_attributes(input: &DeriveInput) -> syn::Result<Vec<StructValidation>> {
639    let mut validations = Vec::new();
640
641    for attr in &input.attrs {
642        if !attr.path().is_ident("validate") {
643            continue;
644        }
645
646        let validation = parse_struct_validate_attribute(attr)?;
647        validations.push(validation);
648    }
649
650    Ok(validations)
651}
652
653fn parse_struct_validate_attribute(attr: &Attribute) -> syn::Result<StructValidation> {
654    let meta = &attr.meta;
655
656    match meta {
657        Meta::List(list) => {
658            let nested: syn::punctuated::Punctuated<Meta, syn::Token![,]> =
659                list.parse_args_with(syn::punctuated::Punctuated::parse_terminated)?;
660
661            let mut check = None;
662            let mut code = None;
663            let mut message = None;
664            let mut when = None;
665
666            for meta in nested {
667                match meta {
668                    Meta::NameValue(nv) => {
669                        if nv.path.is_ident("check") {
670                            check = Some(parse_string_lit(&nv.value)?);
671                        } else if nv.path.is_ident("code") {
672                            code = Some(parse_string_lit(&nv.value)?);
673                        } else if nv.path.is_ident("message") {
674                            message = Some(parse_string_lit(&nv.value)?);
675                        } else if nv.path.is_ident("when") {
676                            when = Some(parse_string_lit(&nv.value)?);
677                        }
678                    }
679                    _ => return Err(syn::Error::new_spanned(meta, "Expected name = value")),
680                }
681            }
682
683            let check = check.ok_or_else(|| {
684                syn::Error::new_spanned(attr, "Struct-level validation requires 'check' parameter")
685            })?;
686
687            Ok(StructValidation {
688                check,
689                code,
690                message,
691                when,
692            })
693        }
694        _ => Err(syn::Error::new_spanned(
695            attr,
696            "Struct-level validation requires #[validate(check = \"...\", ...)]",
697        )),
698    }
699}
700
701fn parse_field_attributes(field: &Field) -> syn::Result<Vec<ValidationRule>> {
702    let mut rules = Vec::new();
703
704    for attr in &field.attrs {
705        if !attr.path().is_ident("validate") {
706            continue;
707        }
708
709        // Parse all rules from this attribute
710        attr.parse_nested_meta(|meta| {
711            // Email
712            if meta.path.is_ident("email") {
713                rules.push(ValidationRule::Email);
714                return Ok(());
715            }
716
717            // URL
718            if meta.path.is_ident("url") {
719                rules.push(ValidationRule::Url);
720                return Ok(());
721            }
722
723            // min_len
724            if meta.path.is_ident("min_len") {
725                let value: syn::Lit = meta.value()?.parse()?;
726                if let syn::Lit::Int(lit_int) = value {
727                    let val = lit_int.base10_parse()?;
728                    rules.push(ValidationRule::MinLen(val));
729                }
730                return Ok(());
731            }
732
733            // max_len
734            if meta.path.is_ident("max_len") {
735                let value: syn::Lit = meta.value()?.parse()?;
736                if let syn::Lit::Int(lit_int) = value {
737                    let val = lit_int.base10_parse()?;
738                    rules.push(ValidationRule::MaxLen(val));
739                }
740                return Ok(());
741            }
742
743            // length (legacy syntax)
744            if meta.path.is_ident("length") {
745                let mut min = None;
746                let mut max = None;
747                let mut code = None;
748                let mut message = None;
749
750                meta.parse_nested_meta(|nested| {
751                    if nested.path.is_ident("min") {
752                        let value: syn::Lit = nested.value()?.parse()?;
753                        if let syn::Lit::Int(lit_int) = value {
754                            min = Some(lit_int.base10_parse()?);
755                        }
756                    } else if nested.path.is_ident("max") {
757                        let value: syn::Lit = nested.value()?.parse()?;
758                        if let syn::Lit::Int(lit_int) = value {
759                            max = Some(lit_int.base10_parse()?);
760                        }
761                    } else if nested.path.is_ident("code") {
762                        let value: syn::Lit = nested.value()?.parse()?;
763                        if let syn::Lit::Str(lit_str) = value {
764                            code = Some(lit_str.value());
765                        }
766                    } else if nested.path.is_ident("message") {
767                        let value: syn::Lit = nested.value()?.parse()?;
768                        if let syn::Lit::Str(lit_str) = value {
769                            message = Some(lit_str.value());
770                        }
771                    }
772                    Ok(())
773                })?;
774
775                rules.push(ValidationRule::Length {
776                    min,
777                    max,
778                    code,
779                    message,
780                });
781                return Ok(());
782            }
783
784            // range
785            if meta.path.is_ident("range") {
786                let mut min = None;
787                let mut max = None;
788                let mut code = None;
789                let mut message = None;
790
791                meta.parse_nested_meta(|nested| {
792                    if nested.path.is_ident("min") {
793                        let value: syn::Expr = nested.value()?.parse()?;
794                        min = Some(quote! { #value });
795                    } else if nested.path.is_ident("max") {
796                        let value: syn::Expr = nested.value()?.parse()?;
797                        max = Some(quote! { #value });
798                    } else if nested.path.is_ident("code") {
799                        let value: syn::Lit = nested.value()?.parse()?;
800                        if let syn::Lit::Str(lit_str) = value {
801                            code = Some(lit_str.value());
802                        }
803                    } else if nested.path.is_ident("message") {
804                        let value: syn::Lit = nested.value()?.parse()?;
805                        if let syn::Lit::Str(lit_str) = value {
806                            message = Some(lit_str.value());
807                        }
808                    }
809                    Ok(())
810                })?;
811
812                rules.push(ValidationRule::Range {
813                    min,
814                    max,
815                    code,
816                    message,
817                });
818                return Ok(());
819            }
820
821            // nested
822            if meta.path.is_ident("nested") {
823                rules.push(ValidationRule::Nested);
824                return Ok(());
825            }
826
827            // each - supports any validation rule
828            if meta.path.is_ident("each") {
829                meta.parse_nested_meta(|nested| {
830                    // Handle nested
831                    if nested.path.is_ident("nested") {
832                        rules.push(ValidationRule::Each(Box::new(ValidationRule::Nested)));
833                        return Ok(());
834                    }
835
836                    // Handle length
837                    if nested.path.is_ident("length") {
838                        let mut min = None;
839                        let mut max = None;
840                        nested.parse_nested_meta(|inner| {
841                            if inner.path.is_ident("min") {
842                                let value: syn::Lit = inner.value()?.parse()?;
843                                if let syn::Lit::Int(lit_int) = value {
844                                    min = Some(lit_int.base10_parse()?);
845                                }
846                            } else if inner.path.is_ident("max") {
847                                let value: syn::Lit = inner.value()?.parse()?;
848                                if let syn::Lit::Int(lit_int) = value {
849                                    max = Some(lit_int.base10_parse()?);
850                                }
851                            }
852                            Ok(())
853                        })?;
854                        rules.push(ValidationRule::Each(Box::new(ValidationRule::Length {
855                            min,
856                            max,
857                            code: None,
858                            message: None,
859                        })));
860                        return Ok(());
861                    }
862
863                    // Handle range
864                    if nested.path.is_ident("range") {
865                        let mut min = None;
866                        let mut max = None;
867                        nested.parse_nested_meta(|inner| {
868                            if inner.path.is_ident("min") {
869                                let value: syn::Expr = inner.value()?.parse()?;
870                                min = Some(quote! { #value });
871                            } else if inner.path.is_ident("max") {
872                                let value: syn::Expr = inner.value()?.parse()?;
873                                max = Some(quote! { #value });
874                            }
875                            Ok(())
876                        })?;
877                        rules.push(ValidationRule::Each(Box::new(ValidationRule::Range {
878                            min,
879                            max,
880                            code: None,
881                            message: None,
882                        })));
883                        return Ok(());
884                    }
885
886                    // Handle all simple string rules
887                    if nested.path.is_ident("email") {
888                        rules.push(ValidationRule::Each(Box::new(ValidationRule::Email)));
889                        return Ok(());
890                    }
891                    if nested.path.is_ident("url") {
892                        rules.push(ValidationRule::Each(Box::new(ValidationRule::Url)));
893                        return Ok(());
894                    }
895                    if nested.path.is_ident("alphanumeric") {
896                        rules.push(ValidationRule::Each(Box::new(ValidationRule::Alphanumeric)));
897                        return Ok(());
898                    }
899                    if nested.path.is_ident("ascii") {
900                        rules.push(ValidationRule::Each(Box::new(ValidationRule::Ascii)));
901                        return Ok(());
902                    }
903                    if nested.path.is_ident("alpha_only") {
904                        rules.push(ValidationRule::Each(Box::new(ValidationRule::AlphaOnly)));
905                        return Ok(());
906                    }
907                    if nested.path.is_ident("numeric_string") {
908                        rules.push(ValidationRule::Each(Box::new(
909                            ValidationRule::NumericString,
910                        )));
911                        return Ok(());
912                    }
913                    if nested.path.is_ident("non_empty") {
914                        rules.push(ValidationRule::Each(Box::new(ValidationRule::NonEmpty)));
915                        return Ok(());
916                    }
917                    if nested.path.is_ident("non_blank") {
918                        rules.push(ValidationRule::Each(Box::new(ValidationRule::NonBlank)));
919                        return Ok(());
920                    }
921
922                    // Handle rules with parameters
923                    if nested.path.is_ident("min_len") {
924                        let value: syn::Lit = nested.value()?.parse()?;
925                        if let syn::Lit::Int(lit_int) = value {
926                            let val = lit_int.base10_parse()?;
927                            rules.push(ValidationRule::Each(Box::new(ValidationRule::MinLen(val))));
928                        }
929                        return Ok(());
930                    }
931                    if nested.path.is_ident("max_len") {
932                        let value: syn::Lit = nested.value()?.parse()?;
933                        if let syn::Lit::Int(lit_int) = value {
934                            let val = lit_int.base10_parse()?;
935                            rules.push(ValidationRule::Each(Box::new(ValidationRule::MaxLen(val))));
936                        }
937                        return Ok(());
938                    }
939                    if nested.path.is_ident("matches_regex") {
940                        let value: syn::Lit = nested.value()?.parse()?;
941                        if let syn::Lit::Str(lit_str) = value {
942                            rules.push(ValidationRule::Each(Box::new(
943                                ValidationRule::MatchesRegex(lit_str.value()),
944                            )));
945                        }
946                        return Ok(());
947                    }
948
949                    Ok(())
950                })?;
951                return Ok(());
952            }
953
954            // custom
955            if meta.path.is_ident("custom") {
956                let value: syn::Lit = meta.value()?.parse()?;
957                if let syn::Lit::Str(lit_str) = value {
958                    rules.push(ValidationRule::Custom(lit_str.value()));
959                }
960                return Ok(());
961            }
962
963            // String pattern rules
964            if meta.path.is_ident("alphanumeric") {
965                rules.push(ValidationRule::Alphanumeric);
966                return Ok(());
967            }
968
969            if meta.path.is_ident("ascii") {
970                rules.push(ValidationRule::Ascii);
971                return Ok(());
972            }
973
974            if meta.path.is_ident("alpha_only") {
975                rules.push(ValidationRule::AlphaOnly);
976                return Ok(());
977            }
978
979            if meta.path.is_ident("numeric_string") {
980                rules.push(ValidationRule::NumericString);
981                return Ok(());
982            }
983
984            if meta.path.is_ident("non_empty") {
985                rules.push(ValidationRule::NonEmpty);
986                return Ok(());
987            }
988
989            if meta.path.is_ident("non_blank") {
990                rules.push(ValidationRule::NonBlank);
991                return Ok(());
992            }
993
994            if meta.path.is_ident("no_whitespace") {
995                rules.push(ValidationRule::NoWhitespace);
996                return Ok(());
997            }
998
999            // String content rules with values
1000            if meta.path.is_ident("contains") {
1001                let value: syn::Lit = meta.value()?.parse()?;
1002                if let syn::Lit::Str(lit_str) = value {
1003                    rules.push(ValidationRule::Contains(lit_str.value()));
1004                }
1005                return Ok(());
1006            }
1007
1008            if meta.path.is_ident("starts_with") {
1009                let value: syn::Lit = meta.value()?.parse()?;
1010                if let syn::Lit::Str(lit_str) = value {
1011                    rules.push(ValidationRule::StartsWith(lit_str.value()));
1012                }
1013                return Ok(());
1014            }
1015
1016            if meta.path.is_ident("ends_with") {
1017                let value: syn::Lit = meta.value()?.parse()?;
1018                if let syn::Lit::Str(lit_str) = value {
1019                    rules.push(ValidationRule::EndsWith(lit_str.value()));
1020                }
1021                return Ok(());
1022            }
1023
1024            if meta.path.is_ident("matches_regex") {
1025                let value: syn::Lit = meta.value()?.parse()?;
1026                if let syn::Lit::Str(lit_str) = value {
1027                    rules.push(ValidationRule::MatchesRegex(lit_str.value()));
1028                }
1029                return Ok(());
1030            }
1031
1032            // Numeric rules
1033            if meta.path.is_ident("min") {
1034                let value: syn::Expr = meta.value()?.parse()?;
1035                rules.push(ValidationRule::Min(quote! { #value }));
1036                return Ok(());
1037            }
1038
1039            if meta.path.is_ident("max") {
1040                let value: syn::Expr = meta.value()?.parse()?;
1041                rules.push(ValidationRule::Max(quote! { #value }));
1042                return Ok(());
1043            }
1044
1045            if meta.path.is_ident("positive") {
1046                rules.push(ValidationRule::Positive);
1047                return Ok(());
1048            }
1049
1050            if meta.path.is_ident("negative") {
1051                rules.push(ValidationRule::Negative);
1052                return Ok(());
1053            }
1054
1055            if meta.path.is_ident("non_zero") {
1056                rules.push(ValidationRule::NonZero);
1057                return Ok(());
1058            }
1059
1060            if meta.path.is_ident("finite") {
1061                rules.push(ValidationRule::Finite);
1062                return Ok(());
1063            }
1064
1065            if meta.path.is_ident("multiple_of") {
1066                let value: syn::Expr = meta.value()?.parse()?;
1067                rules.push(ValidationRule::MultipleOf(quote! { #value }));
1068                return Ok(());
1069            }
1070
1071            // Choice rules
1072            if meta.path.is_ident("equals") {
1073                let value: syn::Expr = meta.value()?.parse()?;
1074                rules.push(ValidationRule::Equals(quote! { #value }));
1075                return Ok(());
1076            }
1077
1078            if meta.path.is_ident("not_equals") {
1079                let value: syn::Expr = meta.value()?.parse()?;
1080                rules.push(ValidationRule::NotEquals(quote! { #value }));
1081                return Ok(());
1082            }
1083
1084            // Collection rules
1085            if meta.path.is_ident("min_items") {
1086                let value: syn::Lit = meta.value()?.parse()?;
1087                if let syn::Lit::Int(lit_int) = value {
1088                    let val = lit_int.base10_parse()?;
1089                    rules.push(ValidationRule::MinItems(val));
1090                }
1091                return Ok(());
1092            }
1093
1094            if meta.path.is_ident("max_items") {
1095                let value: syn::Lit = meta.value()?.parse()?;
1096                if let syn::Lit::Int(lit_int) = value {
1097                    let val = lit_int.base10_parse()?;
1098                    rules.push(ValidationRule::MaxItems(val));
1099                }
1100                return Ok(());
1101            }
1102
1103            if meta.path.is_ident("unique") {
1104                rules.push(ValidationRule::Unique);
1105                return Ok(());
1106            }
1107
1108            // Unknown rule - silently ignore for forward compatibility
1109            Ok(())
1110        })?;
1111    }
1112
1113    Ok(rules)
1114}
1115
1116fn parse_string_lit(expr: &Expr) -> syn::Result<String> {
1117    match expr {
1118        Expr::Lit(lit_expr) => match &lit_expr.lit {
1119            Lit::Str(str_lit) => Ok(str_lit.value()),
1120            _ => Err(syn::Error::new_spanned(expr, "Expected string literal")),
1121        },
1122        _ => Err(syn::Error::new_spanned(expr, "Expected string literal")),
1123    }
1124}
1125
1126fn generate_field_validation(fv: &FieldValidation) -> proc_macro2::TokenStream {
1127    let field_name = &fv.field_name;
1128    let field_name_str = field_name.to_string();
1129
1130    let validations: Vec<_> = fv
1131        .rules
1132        .iter()
1133        .map(|rule| match rule {
1134            // Legacy rules
1135            ValidationRule::Length { min, max, .. } => {
1136                generate_length_validation(field_name, &field_name_str, min, max)
1137            }
1138            ValidationRule::Range { min, max, .. } => {
1139                generate_range_validation(field_name, &field_name_str, min, max)
1140            }
1141            ValidationRule::Nested => generate_nested_validation(field_name, &field_name_str),
1142            ValidationRule::Each(inner_rule) => {
1143                generate_each_validation(field_name, &field_name_str, inner_rule)
1144            }
1145            ValidationRule::Custom(fn_path) => {
1146                generate_custom_validation(field_name, &field_name_str, fn_path)
1147            }
1148
1149            // New rich syntax rules - String rules
1150            ValidationRule::Email => {
1151                generate_simple_string_rule(field_name, &field_name_str, "email")
1152            }
1153            ValidationRule::Url => generate_simple_string_rule(field_name, &field_name_str, "url"),
1154            ValidationRule::MinLen(min) => generate_min_len(field_name, &field_name_str, *min),
1155            ValidationRule::MaxLen(max) => generate_max_len(field_name, &field_name_str, *max),
1156            ValidationRule::Alphanumeric => {
1157                generate_simple_string_rule(field_name, &field_name_str, "alphanumeric")
1158            }
1159            ValidationRule::Ascii => {
1160                generate_simple_string_rule(field_name, &field_name_str, "ascii")
1161            }
1162            ValidationRule::AlphaOnly => {
1163                generate_simple_string_rule(field_name, &field_name_str, "alpha_only")
1164            }
1165            ValidationRule::NumericString => {
1166                generate_simple_string_rule(field_name, &field_name_str, "numeric_string")
1167            }
1168            ValidationRule::NonEmpty => {
1169                generate_simple_string_rule(field_name, &field_name_str, "non_empty")
1170            }
1171            ValidationRule::NonBlank => {
1172                generate_simple_string_rule(field_name, &field_name_str, "non_blank")
1173            }
1174            ValidationRule::NoWhitespace => {
1175                generate_simple_string_rule(field_name, &field_name_str, "no_whitespace")
1176            }
1177            ValidationRule::Contains(substr) => {
1178                generate_string_param_rule(field_name, &field_name_str, "contains", substr)
1179            }
1180            ValidationRule::StartsWith(prefix) => {
1181                generate_string_param_rule(field_name, &field_name_str, "starts_with", prefix)
1182            }
1183            ValidationRule::EndsWith(suffix) => {
1184                generate_string_param_rule(field_name, &field_name_str, "ends_with", suffix)
1185            }
1186            ValidationRule::MatchesRegex(pattern) => {
1187                generate_matches_regex(field_name, &field_name_str, pattern)
1188            }
1189
1190            // Numeric rules
1191            ValidationRule::Min(min) => generate_min_max(field_name, &field_name_str, "min", min),
1192            ValidationRule::Max(max) => generate_min_max(field_name, &field_name_str, "max", max),
1193            ValidationRule::Positive => {
1194                generate_simple_numeric_rule(field_name, &field_name_str, "positive")
1195            }
1196            ValidationRule::Negative => {
1197                generate_simple_numeric_rule(field_name, &field_name_str, "negative")
1198            }
1199            ValidationRule::NonZero => {
1200                generate_simple_numeric_rule(field_name, &field_name_str, "non_zero")
1201            }
1202            ValidationRule::Finite => {
1203                generate_simple_numeric_rule(field_name, &field_name_str, "finite")
1204            }
1205            ValidationRule::MultipleOf(n) => {
1206                generate_min_max(field_name, &field_name_str, "multiple_of", n)
1207            }
1208
1209            // Choice rules
1210            ValidationRule::Equals(val) => {
1211                generate_min_max(field_name, &field_name_str, "equals", val)
1212            }
1213            ValidationRule::NotEquals(val) => {
1214                generate_min_max(field_name, &field_name_str, "not_equals", val)
1215            }
1216            ValidationRule::OneOf(values) => generate_one_of(field_name, &field_name_str, values),
1217
1218            // Collection rules
1219            ValidationRule::MinItems(min) => {
1220                generate_collection_rule(field_name, &field_name_str, "min_items", *min)
1221            }
1222            ValidationRule::MaxItems(max) => {
1223                generate_collection_rule(field_name, &field_name_str, "max_items", *max)
1224            }
1225            ValidationRule::Unique => {
1226                generate_simple_collection_rule(field_name, &field_name_str, "unique")
1227            }
1228        })
1229        .collect();
1230
1231    quote! {
1232        #(#validations)*
1233    }
1234}
1235
1236/// Generate validation code for a single tuple struct field (accessed by index)
1237fn generate_tuple_field_validation(fv: &TupleFieldValidation) -> proc_macro2::TokenStream {
1238    let field_index = syn::Index::from(fv.field_index);
1239    let field_name_str = fv.field_index.to_string();
1240
1241    let validations: Vec<_> = fv
1242        .rules
1243        .iter()
1244        .map(|rule| generate_indexed_field_validation(&field_index, &field_name_str, rule))
1245        .collect();
1246
1247    quote! {
1248        #(#validations)*
1249    }
1250}
1251
1252/// Generate validation code for a single indexed field (used by tuple structs)
1253fn generate_indexed_field_validation(
1254    field_index: &syn::Index,
1255    field_name_str: &str,
1256    rule: &ValidationRule,
1257) -> proc_macro2::TokenStream {
1258    match rule {
1259        ValidationRule::Length { min, max, .. } => {
1260            let rule_expr = match (min, max) {
1261                (Some(min), Some(max)) => {
1262                    quote! { domainstack::rules::min_len(#min).and(domainstack::rules::max_len(#max)) }
1263                }
1264                (Some(min), None) => quote! { domainstack::rules::min_len(#min) },
1265                (None, Some(max)) => quote! { domainstack::rules::max_len(#max) },
1266                (None, None) => return quote! {},
1267            };
1268            quote! {
1269                {
1270                    let rule = #rule_expr;
1271                    if let Err(e) = domainstack::validate(#field_name_str, self.#field_index.as_str(), &rule) {
1272                        err.extend(e);
1273                    }
1274                }
1275            }
1276        }
1277        ValidationRule::Range { min, max, .. } => match (min, max) {
1278            (Some(min), Some(max)) => quote! {
1279                {
1280                    let rule = domainstack::rules::range(#min, #max);
1281                    if let Err(e) = domainstack::validate(#field_name_str, &self.#field_index, &rule) {
1282                        err.extend(e);
1283                    }
1284                }
1285            },
1286            _ => quote! {},
1287        },
1288        ValidationRule::Nested => quote! {
1289            if let Err(e) = self.#field_index.validate() {
1290                err.merge_prefixed(#field_name_str, e);
1291            }
1292        },
1293        ValidationRule::Email => quote! {
1294            {
1295                let rule = domainstack::rules::email();
1296                if let Err(e) = domainstack::validate(#field_name_str, self.#field_index.as_str(), &rule) {
1297                    err.extend(e);
1298                }
1299            }
1300        },
1301        ValidationRule::Url => quote! {
1302            {
1303                let rule = domainstack::rules::url();
1304                if let Err(e) = domainstack::validate(#field_name_str, self.#field_index.as_str(), &rule) {
1305                    err.extend(e);
1306                }
1307            }
1308        },
1309        ValidationRule::MinLen(min) => quote! {
1310            {
1311                let rule = domainstack::rules::min_len(#min);
1312                if let Err(e) = domainstack::validate(#field_name_str, self.#field_index.as_str(), &rule) {
1313                    err.extend(e);
1314                }
1315            }
1316        },
1317        ValidationRule::MaxLen(max) => quote! {
1318            {
1319                let rule = domainstack::rules::max_len(#max);
1320                if let Err(e) = domainstack::validate(#field_name_str, self.#field_index.as_str(), &rule) {
1321                    err.extend(e);
1322                }
1323            }
1324        },
1325        ValidationRule::Alphanumeric => quote! {
1326            {
1327                let rule = domainstack::rules::alphanumeric();
1328                if let Err(e) = domainstack::validate(#field_name_str, self.#field_index.as_str(), &rule) {
1329                    err.extend(e);
1330                }
1331            }
1332        },
1333        ValidationRule::Ascii => quote! {
1334            {
1335                let rule = domainstack::rules::ascii();
1336                if let Err(e) = domainstack::validate(#field_name_str, self.#field_index.as_str(), &rule) {
1337                    err.extend(e);
1338                }
1339            }
1340        },
1341        ValidationRule::NonEmpty => quote! {
1342            {
1343                let rule = domainstack::rules::non_empty();
1344                if let Err(e) = domainstack::validate(#field_name_str, self.#field_index.as_str(), &rule) {
1345                    err.extend(e);
1346                }
1347            }
1348        },
1349        ValidationRule::NonBlank => quote! {
1350            {
1351                let rule = domainstack::rules::non_blank();
1352                if let Err(e) = domainstack::validate(#field_name_str, self.#field_index.as_str(), &rule) {
1353                    err.extend(e);
1354                }
1355            }
1356        },
1357        ValidationRule::MatchesRegex(pattern) => quote! {
1358            {
1359                let rule = domainstack::rules::matches_regex(#pattern);
1360                if let Err(e) = domainstack::validate(#field_name_str, self.#field_index.as_str(), &rule) {
1361                    err.extend(e);
1362                }
1363            }
1364        },
1365        ValidationRule::Min(min) => quote! {
1366            {
1367                let rule = domainstack::rules::min(#min);
1368                if let Err(e) = domainstack::validate(#field_name_str, &self.#field_index, &rule) {
1369                    err.extend(e);
1370                }
1371            }
1372        },
1373        ValidationRule::Max(max) => quote! {
1374            {
1375                let rule = domainstack::rules::max(#max);
1376                if let Err(e) = domainstack::validate(#field_name_str, &self.#field_index, &rule) {
1377                    err.extend(e);
1378                }
1379            }
1380        },
1381        ValidationRule::Positive => quote! {
1382            {
1383                let rule = domainstack::rules::positive();
1384                if let Err(e) = domainstack::validate(#field_name_str, &self.#field_index, &rule) {
1385                    err.extend(e);
1386                }
1387            }
1388        },
1389        ValidationRule::Negative => quote! {
1390            {
1391                let rule = domainstack::rules::negative();
1392                if let Err(e) = domainstack::validate(#field_name_str, &self.#field_index, &rule) {
1393                    err.extend(e);
1394                }
1395            }
1396        },
1397        ValidationRule::NonZero => quote! {
1398            {
1399                let rule = domainstack::rules::non_zero();
1400                if let Err(e) = domainstack::validate(#field_name_str, &self.#field_index, &rule) {
1401                    err.extend(e);
1402                }
1403            }
1404        },
1405        _ => quote! {},
1406    }
1407}
1408
1409/// Generate validation code for enum struct variant fields (accessed by name binding)
1410fn generate_enum_field_validation(
1411    field_name: &syn::Ident,
1412    field_name_str: &str,
1413    rule: &ValidationRule,
1414) -> proc_macro2::TokenStream {
1415    match rule {
1416        ValidationRule::Length { min, max, .. } => {
1417            let rule_expr = match (min, max) {
1418                (Some(min), Some(max)) => {
1419                    quote! { domainstack::rules::min_len(#min).and(domainstack::rules::max_len(#max)) }
1420                }
1421                (Some(min), None) => quote! { domainstack::rules::min_len(#min) },
1422                (None, Some(max)) => quote! { domainstack::rules::max_len(#max) },
1423                (None, None) => return quote! {},
1424            };
1425            quote! {
1426                {
1427                    let rule = #rule_expr;
1428                    if let Err(e) = domainstack::validate(#field_name_str, #field_name.as_str(), &rule) {
1429                        err.extend(e);
1430                    }
1431                }
1432            }
1433        }
1434        ValidationRule::Range { min, max, .. } => match (min, max) {
1435            (Some(min), Some(max)) => quote! {
1436                {
1437                    let rule = domainstack::rules::range(#min, #max);
1438                    if let Err(e) = domainstack::validate(#field_name_str, #field_name, &rule) {
1439                        err.extend(e);
1440                    }
1441                }
1442            },
1443            _ => quote! {},
1444        },
1445        ValidationRule::Nested => quote! {
1446            if let Err(e) = #field_name.validate() {
1447                err.merge_prefixed(#field_name_str, e);
1448            }
1449        },
1450        ValidationRule::Email => quote! {
1451            {
1452                let rule = domainstack::rules::email();
1453                if let Err(e) = domainstack::validate(#field_name_str, #field_name.as_str(), &rule) {
1454                    err.extend(e);
1455                }
1456            }
1457        },
1458        ValidationRule::Url => quote! {
1459            {
1460                let rule = domainstack::rules::url();
1461                if let Err(e) = domainstack::validate(#field_name_str, #field_name.as_str(), &rule) {
1462                    err.extend(e);
1463                }
1464            }
1465        },
1466        ValidationRule::MinLen(min) => quote! {
1467            {
1468                let rule = domainstack::rules::min_len(#min);
1469                if let Err(e) = domainstack::validate(#field_name_str, #field_name.as_str(), &rule) {
1470                    err.extend(e);
1471                }
1472            }
1473        },
1474        ValidationRule::MaxLen(max) => quote! {
1475            {
1476                let rule = domainstack::rules::max_len(#max);
1477                if let Err(e) = domainstack::validate(#field_name_str, #field_name.as_str(), &rule) {
1478                    err.extend(e);
1479                }
1480            }
1481        },
1482        ValidationRule::Alphanumeric => quote! {
1483            {
1484                let rule = domainstack::rules::alphanumeric();
1485                if let Err(e) = domainstack::validate(#field_name_str, #field_name.as_str(), &rule) {
1486                    err.extend(e);
1487                }
1488            }
1489        },
1490        ValidationRule::MatchesRegex(pattern) => quote! {
1491            {
1492                let rule = domainstack::rules::matches_regex(#pattern);
1493                if let Err(e) = domainstack::validate(#field_name_str, #field_name.as_str(), &rule) {
1494                    err.extend(e);
1495                }
1496            }
1497        },
1498        ValidationRule::Min(min) => quote! {
1499            {
1500                let rule = domainstack::rules::min(#min);
1501                if let Err(e) = domainstack::validate(#field_name_str, #field_name, &rule) {
1502                    err.extend(e);
1503                }
1504            }
1505        },
1506        ValidationRule::Max(max) => quote! {
1507            {
1508                let rule = domainstack::rules::max(#max);
1509                if let Err(e) = domainstack::validate(#field_name_str, #field_name, &rule) {
1510                    err.extend(e);
1511                }
1512            }
1513        },
1514        ValidationRule::Positive => quote! {
1515            {
1516                let rule = domainstack::rules::positive();
1517                if let Err(e) = domainstack::validate(#field_name_str, #field_name, &rule) {
1518                    err.extend(e);
1519                }
1520            }
1521        },
1522        ValidationRule::Negative => quote! {
1523            {
1524                let rule = domainstack::rules::negative();
1525                if let Err(e) = domainstack::validate(#field_name_str, #field_name, &rule) {
1526                    err.extend(e);
1527                }
1528            }
1529        },
1530        ValidationRule::NonZero => quote! {
1531            {
1532                let rule = domainstack::rules::non_zero();
1533                if let Err(e) = domainstack::validate(#field_name_str, #field_name, &rule) {
1534                    err.extend(e);
1535                }
1536            }
1537        },
1538        _ => quote! {},
1539    }
1540}
1541
1542/// Generate validation code for enum tuple variant fields (accessed by binding name)
1543fn generate_enum_tuple_field_validation(
1544    binding: &syn::Ident,
1545    field_name_str: &str,
1546    rule: &ValidationRule,
1547) -> proc_macro2::TokenStream {
1548    // Reuse the same logic as enum struct variant fields
1549    generate_enum_field_validation(binding, field_name_str, rule)
1550}
1551
1552fn generate_length_validation(
1553    field_name: &syn::Ident,
1554    field_name_str: &str,
1555    min: &Option<usize>,
1556    max: &Option<usize>,
1557) -> proc_macro2::TokenStream {
1558    let rule = match (min, max) {
1559        (Some(min), Some(max)) => {
1560            quote! { domainstack::rules::min_len(#min).and(domainstack::rules::max_len(#max)) }
1561        }
1562        (Some(min), None) => {
1563            quote! { domainstack::rules::min_len(#min) }
1564        }
1565        (None, Some(max)) => {
1566            quote! { domainstack::rules::max_len(#max) }
1567        }
1568        (None, None) => {
1569            // No constraints - skip
1570            return quote! {};
1571        }
1572    };
1573
1574    quote! {
1575        {
1576            let rule = #rule;
1577            if let Err(e) = domainstack::validate(#field_name_str, self.#field_name.as_str(), &rule) {
1578                err.extend(e);
1579            }
1580        }
1581    }
1582}
1583
1584fn generate_range_validation(
1585    field_name: &syn::Ident,
1586    field_name_str: &str,
1587    min: &Option<proc_macro2::TokenStream>,
1588    max: &Option<proc_macro2::TokenStream>,
1589) -> proc_macro2::TokenStream {
1590    match (min, max) {
1591        (Some(min), Some(max)) => {
1592            quote! {
1593                {
1594                    let rule = domainstack::rules::range(#min, #max);
1595                    if let Err(e) = domainstack::validate(#field_name_str, &self.#field_name, &rule) {
1596                        err.extend(e);
1597                    }
1598                }
1599            }
1600        }
1601        _ => {
1602            // Range requires both min and max
1603            quote! {}
1604        }
1605    }
1606}
1607
1608fn generate_nested_validation(
1609    field_name: &syn::Ident,
1610    field_name_str: &str,
1611) -> proc_macro2::TokenStream {
1612    quote! {
1613        if let Err(e) = self.#field_name.validate() {
1614            err.merge_prefixed(#field_name_str, e);
1615        }
1616    }
1617}
1618
1619fn generate_each_validation(
1620    field_name: &syn::Ident,
1621    field_name_str: &str,
1622    inner_rule: &ValidationRule,
1623) -> proc_macro2::TokenStream {
1624    match inner_rule {
1625        ValidationRule::Nested => {
1626            quote! {
1627                for (i, item) in self.#field_name.iter().enumerate() {
1628                    if let Err(e) = item.validate() {
1629                        let path = domainstack::Path::root().field(#field_name_str).index(i);
1630                        err.merge_prefixed(path, e);
1631                    }
1632                }
1633            }
1634        }
1635        ValidationRule::Length { min, max, .. } => {
1636            let rule = match (min, max) {
1637                (Some(min), Some(max)) => {
1638                    quote! { domainstack::rules::min_len(#min).and(domainstack::rules::max_len(#max)) }
1639                }
1640                (Some(min), None) => {
1641                    quote! { domainstack::rules::min_len(#min) }
1642                }
1643                (None, Some(max)) => {
1644                    quote! { domainstack::rules::max_len(#max) }
1645                }
1646                (None, None) => return quote! {},
1647            };
1648
1649            quote! {
1650                {
1651                    let rule = #rule;
1652                    for (i, item) in self.#field_name.iter().enumerate() {
1653                        let path = domainstack::Path::root().field(#field_name_str).index(i);
1654                        if let Err(e) = domainstack::validate(path, item.as_str(), &rule) {
1655                            err.extend(e);
1656                        }
1657                    }
1658                }
1659            }
1660        }
1661        ValidationRule::Range { min, max, .. } => match (min, max) {
1662            (Some(min), Some(max)) => {
1663                quote! {
1664                    {
1665                        let rule = domainstack::rules::range(#min, #max);
1666                        for (i, item) in self.#field_name.iter().enumerate() {
1667                            let path = domainstack::Path::root().field(#field_name_str).index(i);
1668                            if let Err(e) = domainstack::validate(path, item, &rule) {
1669                                err.extend(e);
1670                            }
1671                        }
1672                    }
1673                }
1674            }
1675            _ => quote! {},
1676        },
1677
1678        // New rich syntax rules - String rules (simple)
1679        ValidationRule::Email => {
1680            quote! {
1681                {
1682                    let rule = domainstack::rules::email();
1683                    for (i, item) in self.#field_name.iter().enumerate() {
1684                        let path = domainstack::Path::root().field(#field_name_str).index(i);
1685                        if let Err(e) = domainstack::validate(path, item.as_str(), &rule) {
1686                            err.extend(e);
1687                        }
1688                    }
1689                }
1690            }
1691        }
1692        ValidationRule::Url => {
1693            quote! {
1694                {
1695                    let rule = domainstack::rules::url();
1696                    for (i, item) in self.#field_name.iter().enumerate() {
1697                        let path = domainstack::Path::root().field(#field_name_str).index(i);
1698                        if let Err(e) = domainstack::validate(path, item.as_str(), &rule) {
1699                            err.extend(e);
1700                        }
1701                    }
1702                }
1703            }
1704        }
1705        ValidationRule::Alphanumeric => {
1706            quote! {
1707                {
1708                    let rule = domainstack::rules::alphanumeric();
1709                    for (i, item) in self.#field_name.iter().enumerate() {
1710                        let path = domainstack::Path::root().field(#field_name_str).index(i);
1711                        if let Err(e) = domainstack::validate(path, item.as_str(), &rule) {
1712                            err.extend(e);
1713                        }
1714                    }
1715                }
1716            }
1717        }
1718        ValidationRule::Ascii => {
1719            quote! {
1720                {
1721                    let rule = domainstack::rules::ascii();
1722                    for (i, item) in self.#field_name.iter().enumerate() {
1723                        let path = domainstack::Path::root().field(#field_name_str).index(i);
1724                        if let Err(e) = domainstack::validate(path, item.as_str(), &rule) {
1725                            err.extend(e);
1726                        }
1727                    }
1728                }
1729            }
1730        }
1731        ValidationRule::AlphaOnly => {
1732            quote! {
1733                {
1734                    let rule = domainstack::rules::alpha_only();
1735                    for (i, item) in self.#field_name.iter().enumerate() {
1736                        let path = domainstack::Path::root().field(#field_name_str).index(i);
1737                        if let Err(e) = domainstack::validate(path, item.as_str(), &rule) {
1738                            err.extend(e);
1739                        }
1740                    }
1741                }
1742            }
1743        }
1744        ValidationRule::NumericString => {
1745            quote! {
1746                {
1747                    let rule = domainstack::rules::numeric_string();
1748                    for (i, item) in self.#field_name.iter().enumerate() {
1749                        let path = domainstack::Path::root().field(#field_name_str).index(i);
1750                        if let Err(e) = domainstack::validate(path, item.as_str(), &rule) {
1751                            err.extend(e);
1752                        }
1753                    }
1754                }
1755            }
1756        }
1757        ValidationRule::NonEmpty => {
1758            quote! {
1759                {
1760                    let rule = domainstack::rules::non_empty();
1761                    for (i, item) in self.#field_name.iter().enumerate() {
1762                        let path = domainstack::Path::root().field(#field_name_str).index(i);
1763                        if let Err(e) = domainstack::validate(path, item.as_str(), &rule) {
1764                            err.extend(e);
1765                        }
1766                    }
1767                }
1768            }
1769        }
1770        ValidationRule::NonBlank => {
1771            quote! {
1772                {
1773                    let rule = domainstack::rules::non_blank();
1774                    for (i, item) in self.#field_name.iter().enumerate() {
1775                        let path = domainstack::Path::root().field(#field_name_str).index(i);
1776                        if let Err(e) = domainstack::validate(path, item.as_str(), &rule) {
1777                            err.extend(e);
1778                        }
1779                    }
1780                }
1781            }
1782        }
1783
1784        // String rules with parameters
1785        ValidationRule::MinLen(min) => {
1786            quote! {
1787                {
1788                    let rule = domainstack::rules::min_len(#min);
1789                    for (i, item) in self.#field_name.iter().enumerate() {
1790                        let path = domainstack::Path::root().field(#field_name_str).index(i);
1791                        if let Err(e) = domainstack::validate(path, item.as_str(), &rule) {
1792                            err.extend(e);
1793                        }
1794                    }
1795                }
1796            }
1797        }
1798        ValidationRule::MaxLen(max) => {
1799            quote! {
1800                {
1801                    let rule = domainstack::rules::max_len(#max);
1802                    for (i, item) in self.#field_name.iter().enumerate() {
1803                        let path = domainstack::Path::root().field(#field_name_str).index(i);
1804                        if let Err(e) = domainstack::validate(path, item.as_str(), &rule) {
1805                            err.extend(e);
1806                        }
1807                    }
1808                }
1809            }
1810        }
1811        ValidationRule::MatchesRegex(pattern) => {
1812            quote! {
1813                {
1814                    let rule = domainstack::rules::matches_regex(#pattern);
1815                    for (i, item) in self.#field_name.iter().enumerate() {
1816                        let path = domainstack::Path::root().field(#field_name_str).index(i);
1817                        if let Err(e) = domainstack::validate(path, item.as_str(), &rule) {
1818                            err.extend(e);
1819                        }
1820                    }
1821                }
1822            }
1823        }
1824
1825        _ => quote! {},
1826    }
1827}
1828
1829fn generate_custom_validation(
1830    field_name: &syn::Ident,
1831    field_name_str: &str,
1832    fn_path: &str,
1833) -> proc_macro2::TokenStream {
1834    let fn_path: proc_macro2::TokenStream = fn_path.parse().unwrap();
1835
1836    quote! {
1837        if let Err(e) = #fn_path(&self.#field_name) {
1838            err.extend(e.prefixed(#field_name_str));
1839        }
1840    }
1841}
1842
1843// Helper functions for new validation rule code generation
1844
1845fn generate_simple_string_rule(
1846    field_name: &syn::Ident,
1847    field_name_str: &str,
1848    rule_fn: &str,
1849) -> proc_macro2::TokenStream {
1850    let rule_fn: proc_macro2::TokenStream = format!("domainstack::rules::{}()", rule_fn)
1851        .parse()
1852        .unwrap();
1853    quote! {
1854        {
1855            let rule = #rule_fn;
1856            if let Err(e) = domainstack::validate(#field_name_str, self.#field_name.as_str(), &rule) {
1857                err.extend(e);
1858            }
1859        }
1860    }
1861}
1862
1863fn generate_min_len(
1864    field_name: &syn::Ident,
1865    field_name_str: &str,
1866    min: usize,
1867) -> proc_macro2::TokenStream {
1868    quote! {
1869        {
1870            let rule = domainstack::rules::min_len(#min);
1871            if let Err(e) = domainstack::validate(#field_name_str, self.#field_name.as_str(), &rule) {
1872                err.extend(e);
1873            }
1874        }
1875    }
1876}
1877
1878fn generate_max_len(
1879    field_name: &syn::Ident,
1880    field_name_str: &str,
1881    max: usize,
1882) -> proc_macro2::TokenStream {
1883    quote! {
1884        {
1885            let rule = domainstack::rules::max_len(#max);
1886            if let Err(e) = domainstack::validate(#field_name_str, self.#field_name.as_str(), &rule) {
1887                err.extend(e);
1888            }
1889        }
1890    }
1891}
1892
1893fn generate_string_param_rule(
1894    field_name: &syn::Ident,
1895    field_name_str: &str,
1896    rule_fn: &str,
1897    param: &str,
1898) -> proc_macro2::TokenStream {
1899    let rule_fn: proc_macro2::TokenStream =
1900        format!("domainstack::rules::{}(\"{}\")", rule_fn, param)
1901            .parse()
1902            .unwrap();
1903    quote! {
1904        {
1905            let rule = #rule_fn;
1906            if let Err(e) = domainstack::validate(#field_name_str, self.#field_name.as_str(), &rule) {
1907                err.extend(e);
1908            }
1909        }
1910    }
1911}
1912
1913fn generate_matches_regex(
1914    field_name: &syn::Ident,
1915    field_name_str: &str,
1916    pattern: &str,
1917) -> proc_macro2::TokenStream {
1918    quote! {
1919        {
1920            let rule = domainstack::rules::matches_regex(#pattern);
1921            if let Err(e) = domainstack::validate(#field_name_str, self.#field_name.as_str(), &rule) {
1922                err.extend(e);
1923            }
1924        }
1925    }
1926}
1927
1928fn generate_simple_numeric_rule(
1929    field_name: &syn::Ident,
1930    field_name_str: &str,
1931    rule_fn: &str,
1932) -> proc_macro2::TokenStream {
1933    let rule_fn: proc_macro2::TokenStream = format!("domainstack::rules::{}()", rule_fn)
1934        .parse()
1935        .unwrap();
1936    quote! {
1937        {
1938            let rule = #rule_fn;
1939            if let Err(e) = domainstack::validate(#field_name_str, &self.#field_name, &rule) {
1940                err.extend(e);
1941            }
1942        }
1943    }
1944}
1945
1946fn generate_min_max(
1947    field_name: &syn::Ident,
1948    field_name_str: &str,
1949    rule_fn: &str,
1950    val: &proc_macro2::TokenStream,
1951) -> proc_macro2::TokenStream {
1952    let rule_fn: proc_macro2::TokenStream = format!("domainstack::rules::{}({})", rule_fn, val)
1953        .parse()
1954        .unwrap();
1955    quote! {
1956        {
1957            let rule = #rule_fn;
1958            if let Err(e) = domainstack::validate(#field_name_str, &self.#field_name, &rule) {
1959                err.extend(e);
1960            }
1961        }
1962    }
1963}
1964
1965fn generate_one_of(
1966    field_name: &syn::Ident,
1967    field_name_str: &str,
1968    values: &[String],
1969) -> proc_macro2::TokenStream {
1970    let values_str = values.iter().map(|v| quote! { #v }).collect::<Vec<_>>();
1971    quote! {
1972        {
1973            let rule = domainstack::rules::one_of(&[#(#values_str),*]);
1974            if let Err(e) = domainstack::validate(#field_name_str, &self.#field_name, &rule) {
1975                err.extend(e);
1976            }
1977        }
1978    }
1979}
1980
1981fn generate_collection_rule(
1982    field_name: &syn::Ident,
1983    field_name_str: &str,
1984    rule_fn: &str,
1985    val: usize,
1986) -> proc_macro2::TokenStream {
1987    let rule_fn: proc_macro2::TokenStream = format!("domainstack::rules::{}({})", rule_fn, val)
1988        .parse()
1989        .unwrap();
1990    quote! {
1991        {
1992            let rule = #rule_fn;
1993            if let Err(e) = domainstack::validate(#field_name_str, &self.#field_name, &rule) {
1994                err.extend(e);
1995            }
1996        }
1997    }
1998}
1999
2000fn generate_simple_collection_rule(
2001    field_name: &syn::Ident,
2002    field_name_str: &str,
2003    rule_fn: &str,
2004) -> proc_macro2::TokenStream {
2005    let rule_fn: proc_macro2::TokenStream = format!("domainstack::rules::{}()", rule_fn)
2006        .parse()
2007        .unwrap();
2008    quote! {
2009        {
2010            let rule = #rule_fn;
2011            if let Err(e) = domainstack::validate(#field_name_str, &self.#field_name, &rule) {
2012                err.extend(e);
2013            }
2014        }
2015    }
2016}
2017
2018fn generate_struct_validation(sv: &StructValidation) -> proc_macro2::TokenStream {
2019    let check_expr: proc_macro2::TokenStream = sv.check.parse().unwrap();
2020    let code = sv
2021        .code
2022        .as_deref()
2023        .unwrap_or("cross_field_validation_failed");
2024    let message = sv
2025        .message
2026        .as_deref()
2027        .unwrap_or("Cross-field validation failed");
2028
2029    let validation_code = quote! {
2030        if !(#check_expr) {
2031            err.violations.push(domainstack::Violation {
2032                path: domainstack::Path::root(),
2033                code: #code,
2034                message: #message.to_string(),
2035                meta: domainstack::Meta::default(),
2036            });
2037        }
2038    };
2039
2040    // Wrap in conditional if 'when' is specified
2041    if let Some(when_expr) = &sv.when {
2042        let when_tokens: proc_macro2::TokenStream = when_expr.parse().unwrap();
2043        quote! {
2044            if #when_tokens {
2045                #validation_code
2046            }
2047        }
2048    } else {
2049        validation_code
2050    }
2051}