Skip to main content

rydantic_derive/
lib.rs

1use proc_macro::TokenStream;
2use proc_macro2::TokenStream as Tokens;
3use quote::format_ident;
4use syn::{
5    parse_macro_input, parse_quote, spanned::Spanned, Data, DeriveInput, Expr, Fields, Ident,
6    Member, Type,
7};
8mod attrs;
9#[cfg(feature = "capnp")]
10mod auto_protocol;
11mod native;
12mod sql_model;
13
14// Generated locals must not shadow callback paths (or default expressions) from
15// the input. Mixed-site hygiene applies only to tokens written by the macro;
16// interpolated user tokens retain their original spans and name resolution.
17// Keep this after the module declarations: it scopes the policy to Model's
18// expansion here, rather than changing the other derives' quote invocations.
19macro_rules! quote {
20    ($($tokens:tt)*) => {
21        ::quote::quote_spanned!(::proc_macro2::Span::mixed_site()=> $($tokens)*)
22    };
23}
24
25fn field_ident(index: usize) -> Ident {
26    format_ident!(
27        "__rydantic_field_{index}",
28        span = proc_macro2::Span::mixed_site()
29    )
30}
31
32/// Derive SQL schema metadata, SQLx row decoding and validated CRUD operations.
33#[proc_macro_derive(SqlModel, attributes(sql, model))]
34pub fn derive_sql_model(input: TokenStream) -> TokenStream {
35    sql_model::expand(parse_macro_input!(input as DeriveInput))
36        .unwrap_or_else(syn::Error::into_compile_error)
37        .into()
38}
39
40/// Derive a complete Cap'n Proto protocol and its bindings from an inline model module.
41/// Every model must explicitly derive `Model` (an imported alias also works).
42/// This attribute does not insert or resolve derive macros.
43#[cfg(feature = "capnp")]
44#[proc_macro_attribute]
45pub fn capnp_models(args: TokenStream, input: TokenStream) -> TokenStream {
46    auto_protocol::prepare(args.into(), input.into(), false)
47        .unwrap_or_else(syn::Error::into_compile_error)
48        .into()
49}
50
51// The runtime re-export selects its own helper path explicitly. A consumer may
52// also declare an inactive or differently versioned direct proc-macro dependency.
53#[doc(hidden)]
54#[cfg(feature = "capnp")]
55#[proc_macro_attribute]
56pub fn __rydantic_capnp_models_runtime(args: TokenStream, input: TokenStream) -> TokenStream {
57    auto_protocol::prepare(args.into(), input.into(), true)
58        .unwrap_or_else(syn::Error::into_compile_error)
59        .into()
60}
61
62// Internal compiler-driven cfg normalization for `capnp_models`.
63#[doc(hidden)]
64#[cfg(feature = "capnp")]
65#[proc_macro_derive(__RydanticCapnpConfigured, attributes(__rydantic_capnp_options))]
66pub fn capnp_configured(input: TokenStream) -> TokenStream {
67    auto_protocol::configured(input.into())
68        .unwrap_or_else(syn::Error::into_compile_error)
69        .into()
70}
71
72#[doc(hidden)]
73#[cfg(feature = "capnp")]
74#[proc_macro_attribute]
75pub fn __rydantic_capnp_discard(_: TokenStream, _: TokenStream) -> TokenStream {
76    TokenStream::new()
77}
78
79#[proc_macro_derive(Model, attributes(model))]
80pub fn derive_model(input: TokenStream) -> TokenStream {
81    expand(parse_macro_input!(input as DeriveInput))
82        .unwrap_or_else(syn::Error::into_compile_error)
83        .into()
84}
85
86struct Field {
87    ident: Ident,
88    member: Member,
89    ty: Type,
90    attr: attrs::Field,
91    input: String,
92    output: String,
93    doc: ::core::option::Option<String>,
94}
95#[derive(Clone, Copy, PartialEq)]
96enum Shape {
97    Named,
98    Tuple,
99    Unit,
100}
101fn shape(fields: &Fields) -> Shape {
102    match fields {
103        Fields::Named(_) => Shape::Named,
104        Fields::Unnamed(_) => Shape::Tuple,
105        Fields::Unit => Shape::Unit,
106    }
107}
108fn fields(
109    source: &Fields,
110    tag: ::core::option::Option<&str>,
111    container: &attrs::Container,
112) -> syn::Result<Vec<Field>> {
113    let mut inputs = std::collections::HashSet::new();
114    let mut outputs = std::collections::HashSet::new();
115    let mut extras = 0;
116    let result=source.iter().enumerate().map(|(i,field)|{
117        let ident=field.ident.clone().unwrap_or_else(||format_ident!("__tuple_{i}"));
118        let member=field.ident.clone().map(Member::Named).unwrap_or_else(||Member::Unnamed(i.into()));
119        let attr=attrs::field(&field.attrs)?;
120        if attr.extra{extras+=1;}
121        if matches!(source,Fields::Unnamed(_)) && (attr.flatten||attr.extra||attr.rename.is_some()||attr.validation_alias.is_some()||attr.serialization_alias.is_some()||attr.exclude||attr.has_default()){
122            return Err(syn::Error::new(field.span(),"tuple fields support validation and transformations; names, flatten, extra, exclude and defaults require named fields"));
123        }
124        if (attr.flatten||attr.extra)&&(attr.rename.is_some()||attr.validation_alias.is_some()||attr.serialization_alias.is_some()||attr.before.is_some()||attr.coerce||attr.has_rules()){
125            return Err(syn::Error::new(field.span(),"flatten and extra fields use their inner model's names, transformations and constraints"));
126        }
127        let base=attr.rename.clone().unwrap_or_else(||if field.ident.is_some(){ident.to_string().trim_start_matches("r#").to_owned()}else{i.to_string()});
128        let input=attr.validation_alias.clone().unwrap_or_else(||base.clone());let output=attr.serialization_alias.clone().unwrap_or(base);
129        if !attr.flatten&&!attr.extra{
130            if !inputs.insert(input.clone())||tag==Some(input.as_str()){return Err(syn::Error::new(ident.span(),"duplicate input field name or collision with enum tag"));}
131            if !attr.exclude&&(!outputs.insert(output.clone())||tag==Some(output.as_str())){return Err(syn::Error::new(ident.span(),"duplicate output field name or collision with enum tag"));}
132        }
133        let doc=attr.description.clone().or_else(||{
134            let text=field.attrs.iter().filter_map(|attr|{if !attr.path().is_ident("doc"){return None;}let syn::Meta::NameValue(v)=&attr.meta else{return None;};let Expr::Lit(v)=&v.value else{return None;};let syn::Lit::Str(v)=&v.lit else{return None;};Some(v.value().trim().to_owned())}).collect::<Vec<_>>();(!text.is_empty()).then(||text.join("\n"))
135        });
136        Ok(Field{ident,member,ty:field.ty.clone(),attr,input,output,doc})
137    }).collect::<syn::Result<Vec<_>>>()?;
138    if extras > 1 {
139        return Err(syn::Error::new(
140            source.span(),
141            "a model may have only one extra field",
142        ));
143    }
144    if container.allow_extra && extras != 1 {
145        return Err(syn::Error::new(
146            source.span(),
147            "extra = \"allow\" requires one #[model(extra)] map field",
148        ));
149    }
150    if !container.allow_extra && extras != 0 {
151        return Err(syn::Error::new(
152            source.span(),
153            "#[model(extra)] requires #[model(extra = \"allow\")]",
154        ));
155    }
156    Ok(result)
157}
158fn option<T: quote::ToTokens>(value: &::core::option::Option<T>) -> Tokens {
159    match value {
160        Some(v) => quote!(Some(#v)),
161        None => quote!(None),
162    }
163}
164fn rules(field: &Field, rd: &Tokens) -> Tokens {
165    let a = &field.attr;
166    let trim = a.trim;
167    let lower = a.lowercase;
168    let upper = a.uppercase;
169    let email = a.email;
170    let min = option(&a.min_length);
171    let max = option(&a.max_length);
172    let number = |v: &::core::option::Option<String>| match v {
173        Some(v) => quote!(Some(#rd::__private::number(#v))),
174        None => quote!(None),
175    };
176    let ge = number(&a.ge);
177    let gt = number(&a.gt);
178    let le = number(&a.le);
179    let lt = number(&a.lt);
180    let pattern = match &a.pattern {
181        Some(v) => {
182            quote!(Some(#rd::__private::regex::Regex::new(#v).expect("regex checked by Model derive")))
183        }
184        None => quote!(None),
185    };
186    quote!({static RULES: ::std::sync::OnceLock<#rd::rules::Rules>=::std::sync::OnceLock::new();RULES.get_or_init(||#rd::rules::Rules{trim:#trim,lowercase:#lower,uppercase:#upper,min_length:#min,max_length:#max,ge:#ge,gt:#gt,le:#le,lt:#lt,pattern:#pattern,email:#email})})
187}
188struct Mode<'a> {
189    rd: &'a Tokens,
190    borrow: bool,
191}
192impl Mode<'_> {
193    fn tr(&self) -> Tokens {
194        let rd = self.rd;
195        if self.borrow {
196            quote!(#rd::BorrowType<'__rydantic_de>)
197        } else {
198            quote!(#rd::JsonType)
199        }
200    }
201    fn decode(&self, ty: &Type, raw: Tokens, coerce: bool) -> Tokens {
202        let tr = self.tr();
203        if self.borrow {
204            quote!(<#ty as #tr>::decode(#raw,__rydantic_ctx,#coerce))
205        } else {
206            quote!(<#ty as #tr>::decode_input(#raw,__rydantic_ctx,#coerce))
207        }
208    }
209}
210fn default_expr(field: &Field, borrow: bool) -> ::core::option::Option<Tokens> {
211    let ty = &field.ty;
212    let a = &field.attr;
213    if a.default_trait {
214        Some(quote!(<#ty as ::core::default::Default>::default()))
215    } else if let Some(expr) = &a.default {
216        Some(
217            if matches!(
218                expr,
219                Expr::Lit(syn::ExprLit {
220                    lit: syn::Lit::Str(_),
221                    ..
222                })
223            ) {
224                if borrow {
225                    quote!(::core::convert::From::from(#expr))
226                } else {
227                    quote!((#expr).to_owned())
228                }
229            } else {
230                quote!(#expr)
231            },
232        )
233    } else {
234        a.default_factory.as_ref().map(|path| quote!(#path()))
235    }
236}
237fn field_value(field: &Field, raw: Tokens, mode: &Mode) -> Tokens {
238    let rd = mode.rd;
239    let tr = mode.tr();
240    let ty = &field.ty;
241    let rule = rules(field, rd);
242    let missing_error = quote!({__rydantic_ctx.push(#rd::ValidationError::new("missing","field is required").with_stage(#rd::Stage::Parse));None});
243    let default = default_expr(field, mode.borrow);
244    let missing = if let Some(value) = default {
245        quote!(Some({let value:#ty=#value;value}))
246    } else if field.attr.required {
247        missing_error.clone()
248    } else {
249        quote!(match <#ty as #tr>::missing(){Some(value)=>Some(value),None=>#missing_error})
250    };
251    let before=field.attr.before.as_ref().map(|path|quote!{
252        let value=__rydantic_raw.into_value(__rydantic_ctx)?;
253        __rydantic_raw=match #path(value){Ok(value)=>#rd::Input::Owned(value),Err(error)=>{__rydantic_ctx.push(error.with_stage(#rd::Stage::Parse));return None;}};
254    });
255    let decoded = mode.decode(ty, quote!(__rydantic_raw), field.attr.coerce);
256    let default_flow = if field.attr.before.is_some() {
257        quote! {let value:#ty=#missing?;let value=<#ty as #tr>::encode_input(&value,__rydantic_ctx);let mut __rydantic_raw=#rd::Input::Owned(value);#before #decoded?}
258    } else {
259        quote! {let value:#ty=#missing?;<#ty as #tr>::prepare_default(value,__rydantic_ctx)?}
260    };
261    let custom=field.attr.validate.as_ref().map(|path|quote!{if __rydantic_ctx.error_count()==__rydantic_start{if let Err(error)=#path(&__rydantic_value){__rydantic_ctx.push(error);}}});
262    let builtin=field.attr.has_rules().then(||quote!{let rules=#rule;__rydantic_value=<#ty as #tr>::normalize(__rydantic_value,rules,__rydantic_ctx)?;<#ty as #tr>::validate_rules(&__rydantic_value,rules,__rydantic_ctx);});
263    quote! {
264        let __rydantic_start=__rydantic_ctx.error_count();
265        let mut __rydantic_value=match #raw{Some(mut __rydantic_raw)=>{#before #decoded?},None=>{#default_flow}};
266        #builtin #custom Some(__rydantic_value)
267    }
268}
269fn with_location(field: &Field, index: usize, shape: Shape, body: Tokens, _rd: &Tokens) -> Tokens {
270    if field.attr.flatten || field.attr.extra || (shape == Shape::Tuple && index == usize::MAX) {
271        quote! {(||{#body})()}
272    } else if shape == Shape::Tuple {
273        quote! {__rydantic_ctx.at(#index,|__rydantic_ctx|{#body})}
274    } else {
275        let input = &field.input;
276        quote! {__rydantic_ctx.at(#input,|__rydantic_ctx|{#body})}
277    }
278}
279fn object_names(fields: &[Field], rd: &Tokens, tag: ::core::option::Option<&str>) -> Tokens {
280    let names=fields.iter().filter(|f|!f.attr.extra).map(|f|{let ty=&f.ty;let input=&f.input;let output=&f.output;let exclude=f.attr.exclude;
281        if f.attr.flatten{quote!{if !(mode==#rd::SchemaMode::Output&&#exclude){names.extend(<#ty as #rd::ObjectType>::field_names(mode,seen));}}}
282        else{quote!{if !(mode==#rd::SchemaMode::Output&&#exclude){names.push(if mode==#rd::SchemaMode::Input{#input}else{#output}.to_owned());}}}
283    });
284    let tag = tag.map(|tag| quote!(names.push(#tag.to_owned());));
285    quote! {let mut names=::std::vec::Vec::<::std::string::String>::new();#tag #(#names)* names}
286}
287fn collision_checks(
288    fields: &[Field],
289    values: &[Tokens],
290    rd: &Tokens,
291    tag: ::core::option::Option<&str>,
292) -> Tokens {
293    let tag = tag.map(|key| quote!(names.push(#key.to_owned());));
294    let entries = fields.iter().zip(values).map(|(field, value)| {
295        let ty=&field.ty; let input=&field.input; let output=&field.output; let exclude=field.attr.exclude;
296        if field.attr.flatten || field.attr.extra {
297            quote! { if !(mode==#rd::SchemaMode::Output && #exclude) { names.extend(<#ty as #rd::ObjectType>::reserved_names(#value,mode)); } }
298        } else {
299            quote! { if !(mode==#rd::SchemaMode::Output && #exclude) { names.push(if mode==#rd::SchemaMode::Input { #input } else { #output }.to_owned()); } }
300        }
301    });
302    let captures=fields.iter().filter(|f|f.attr.flatten||f.attr.extra).map(|f|{let ty=&f.ty;quote!(usize::from(<#ty as #rd::ObjectType>::captures_extra(&mut ::std::collections::HashSet::new())))});
303    quote! {
304        if 0usize #(+ #captures)* > 1 { __rydantic_ctx.push(#rd::ValidationError::new("flatten_ambiguous","only one flattened field may capture unknown keys")); }
305        for mode in [#rd::SchemaMode::Input,#rd::SchemaMode::Output] {
306            let mut names=::std::vec::Vec::new(); #tag #(#entries)*
307            #rd::__private::check_names(names,__rydantic_ctx);
308        }
309    }
310}
311fn layout_check(fields: &[Field], rd: &Tokens, tag: ::core::option::Option<&str>) -> Tokens {
312    let names = object_names(fields, rd, tag);
313    let catches=fields.iter().filter(|f|f.attr.extra||f.attr.flatten).map(|f|{let ty=&f.ty;quote!(usize::from(<#ty as #rd::ObjectType>::captures_extra(&mut ::std::collections::HashSet::new())))});
314    quote! {
315        for mode in [#rd::SchemaMode::Input,#rd::SchemaMode::Output]{let seen=&mut ::std::collections::HashSet::<&'static str>::new();let names={#names};#rd::__private::check_names(names,__rydantic_ctx);}
316        if 0usize #(+ #catches)*>1{__rydantic_ctx.push(#rd::ValidationError::new("flatten_ambiguous","only one flattened field may capture unknown keys"));return None;}
317    }
318}
319fn parse_shape(
320    fields: &[Field],
321    shape: Shape,
322    constructor: Tokens,
323    container: &attrs::Container,
324    mode: &Mode,
325    tag: ::core::option::Option<&str>,
326) -> Tokens {
327    let rd = mode.rd;
328    let tr = mode.tr();
329    let count = fields.len();
330    let newtype = shape == Shape::Tuple && count == 1;
331    let mut parses = vec![];
332    let locals = (0..count).map(field_ident).collect::<Vec<_>>();
333    // Declared fields are consumed before flattened maps, independently of Rust field order.
334    let mut order = (0..count).collect::<Vec<_>>();
335    order.sort_by_key(|i| fields[*i].attr.flatten || fields[*i].attr.extra);
336    for i in order {
337        let field = &fields[i];
338        let local = &locals[i];
339        let ty = &field.ty;
340        let input = &field.input;
341        let has_default = field.attr.has_default();
342        let raw = if shape == Shape::Named {
343            if field.attr.flatten || field.attr.extra {
344                quote! {{let names=<#ty as #rd::ObjectType>::field_names(#rd::SchemaMode::Input,&mut ::std::collections::HashSet::new());
345                    let capture=<#ty as #rd::ObjectType>::captures_extra(&mut ::std::collections::HashSet::new());
346                    let mut object=::std::collections::BTreeMap::new();
347                    for name in names{if let Some(value)=__rydantic_object.remove(&name){object.insert(name,value);}}
348                    if capture{object.extend(::core::mem::take(&mut __rydantic_object));}
349                    if object.is_empty()&&(<#ty as #tr>::allows_missing()||#has_default){None}else{Some(#rd::Input::Object(object))}
350                }}
351            } else {
352                quote!(__rydantic_object.remove(#input))
353            }
354        } else if newtype {
355            quote!(Some(__rydantic_raw))
356        } else {
357            quote!(__rydantic_items.next())
358        };
359        let body = field_value(field, raw, mode);
360        let body = with_location(field, if newtype { usize::MAX } else { i }, shape, body, rd);
361        if field.attr.flatten || field.attr.extra {
362            let capture = quote!(<#ty as #rd::ObjectType>::captures_extra(&mut ::std::collections::HashSet::new()));
363            parses.push((
364                i,
365                quote! {if !#capture{#local= #body;}},
366                quote! {if #capture{#local= #body;}},
367            ));
368        } else {
369            parses.push((i, quote! {#local= #body;}, quote!()));
370        }
371    }
372    let first = parses.iter().map(|(_, a, _)| a);
373    let last = parses.iter().map(|(_, _, b)| b);
374    let unknown=(!container.ignore_extra&&shape==Shape::Named).then(||quote!{for (key,_) in __rydantic_object{__rydantic_ctx.at(key,|ctx|ctx.push(#rd::ValidationError::new("extra_forbidden","unknown field").with_stage(#rd::Stage::Parse)));}});
375    let setup = match shape {
376        Shape::Named => {
377            let check = layout_check(fields, rd, tag);
378            quote! {let mut __rydantic_object=__rydantic_raw.object(__rydantic_ctx)?;#check}
379        }
380        Shape::Tuple if !newtype => {
381            quote! {let items=__rydantic_raw.array(__rydantic_ctx)?;if items.len()!=#count{__rydantic_ctx.push(#rd::ValidationError::new("tuple_length",concat!("expected ",stringify!(#count)," tuple items")).with_param("expected",#count).with_param("actual",items.len()));return None;}let mut __rydantic_items=items.into_iter();}
382        }
383        Shape::Unit => {
384            quote! {if !__rydantic_raw.is_null(){__rydantic_ctx.push(#rd::ValidationError::new("type_error","expected null").with_stage(#rd::Stage::Parse));return None;}}
385        }
386        _ => quote!(),
387    };
388    let collision = if shape == Shape::Named {
389        let values = locals
390            .iter()
391            .map(|local| quote!(&#local))
392            .collect::<Vec<_>>();
393        collision_checks(fields, &values, rd, tag)
394    } else {
395        quote!()
396    };
397    let done = if count == 0 {
398        quote!({#collision Some(#constructor)})
399    } else {
400        quote! {match (#(#locals,)*){(#(Some(#locals),)*)=>{#collision Some(#constructor)},_=>None}}
401    };
402    quote! {#setup #(let mut #locals=None;)* #(#first)* #(#last)* #unknown #done}
403}
404fn encode_shape(
405    fields: &[Field],
406    values: &[Tokens],
407    shape: Shape,
408    mode: &Mode,
409    input: bool,
410    tag: ::core::option::Option<(&str, &str)>,
411) -> Tokens {
412    let rd = mode.rd;
413    let tr = mode.tr();
414    let method = if input {
415        format_ident!("encode_input")
416    } else {
417        format_ident!("encode")
418    };
419    let newtype = shape == Shape::Tuple && fields.len() == 1;
420    if shape == Shape::Unit {
421        return quote!(#rd::Value::Null);
422    }
423    let mut entries = vec![];
424    for (i, (field, value)) in fields.iter().zip(values).enumerate() {
425        if !input && field.attr.exclude {
426            continue;
427        }
428        let ty = &field.ty;
429        let key = if input { &field.input } else { &field.output };
430        let encoded = quote!(<#ty as #tr>::#method(#value,__rydantic_ctx));
431        if newtype {
432            return encoded;
433        }
434        if shape == Shape::Tuple {
435            entries.push(
436                quote! {__rydantic_items.push(__rydantic_ctx.at(#i,|__rydantic_ctx|#encoded));},
437            );
438        } else if field.attr.flatten || field.attr.extra {
439            entries.push(quote!{let value=#encoded;#rd::__private::merge(&mut __rydantic_object,value,__rydantic_ctx);});
440        } else {
441            entries.push(quote!{let value=__rydantic_ctx.at(#key,|__rydantic_ctx|#encoded);#rd::__private::merge(&mut __rydantic_object,#rd::__private::serde_json::json!({#key:value}),__rydantic_ctx);});
442        }
443    }
444    if shape == Shape::Tuple {
445        quote! {let mut __rydantic_items=::std::vec::Vec::new();#(#entries)* #rd::Value::Array(__rydantic_items)}
446    } else {
447        let tag=tag.map(|(tag,wire)|quote!{__rydantic_object.insert(#tag.to_owned(),#rd::__private::serde_json::json!(#wire));});
448        quote! {let mut __rydantic_object=#rd::__private::serde_json::Map::new();#tag #(#entries)* #rd::Value::Object(__rydantic_object)}
449    }
450}
451fn validate_fields(fields: &[Field], values: &[Tokens], shape: Shape, mode: &Mode) -> Tokens {
452    let rd = mode.rd;
453    let tr = mode.tr();
454    let checks=fields.iter().zip(values).enumerate().map(|(i,(field,value))|{
455        let ty=&field.ty;let rule=rules(field,rd);
456        let builtin=field.attr.has_rules().then(||quote!(<#ty as #tr>::validate_rules(#value,#rule,__rydantic_ctx);));
457        let custom=field.attr.validate.as_ref().map(|path|quote!(if let Err(error)=#path(#value){__rydantic_ctx.push(error);}));
458        let body=quote!{let start=__rydantic_ctx.error_count();<#ty as #tr>::validate_into(#value,__rydantic_ctx);if start==__rydantic_ctx.error_count(){#builtin #custom}};
459        with_location(field,if shape==Shape::Tuple&&fields.len()==1{usize::MAX}else{i},shape,body,rd)
460    });
461    let collision = if shape == Shape::Named {
462        collision_checks(fields, values, rd, None)
463    } else {
464        quote!()
465    };
466    quote!(#(#checks;)* #collision)
467}
468fn async_fields(fields: &[Field], values: &[Tokens], shape: Shape, rd: &Tokens) -> Tokens {
469    let checks=fields.iter().zip(values).enumerate().map(|(i,(field,value))|{
470        let ty=&field.ty;let input=&field.input;
471        let segment=if field.attr.flatten||field.attr.extra||(shape==Shape::Tuple&&fields.len()==1){quote!(None)}else if shape==Shape::Tuple{quote!(Some(#rd::PathSegment::from(#i)))}else{quote!(Some(#rd::PathSegment::from(#input)))};
472        let custom=field.attr.async_validate.as_ref().map(|path|quote!{if child.error_count()==start{if let Err(error)=#path(#value).await{child.push(error);}}});
473        quote!{let mut child=__rydantic_ctx.child(#segment);let start=child.error_count();<#ty as #rd::AsyncCheck>::check_async(#value,&mut child).await;#custom __rydantic_ctx.absorb(child);}
474    });
475    quote!(#(#checks)*)
476}
477fn property_schema(field: &Field, mode: &Mode) -> Tokens {
478    let rd = mode.rd;
479    let tr = mode.tr();
480    let ty = &field.ty;
481    let coerce = field.attr.coerce;
482    let rule = rules(field, rd);
483    let before=field.attr.before.as_ref().map(|path|quote!{if __rydantic_ctx.mode==#rd::SchemaMode::Input{property=#rd::__private::serde_json::json!({});#rd::schema::annotate_local(&mut property,concat!("before:",stringify!(#path)));}});
484    let condition = if field.attr.before.is_some() {
485        quote!(__rydantic_ctx.mode==#rd::SchemaMode::Output)
486    } else {
487        quote!(true)
488    };
489    let custom=field.attr.validate.iter().map(|path|quote!(#rd::schema::annotate_local(&mut property,concat!("validate:",stringify!(#path)));));
490    let asynchronous=field.attr.async_validate.iter().map(|path|quote!(#rd::schema::annotate_local(&mut property,concat!("async_validate:",stringify!(#path)));));
491    let desc = field
492        .doc
493        .as_ref()
494        .map(|text| quote!(property["description"]=#rd::__private::serde_json::json!(#text);));
495    let literal = field.attr.default.as_ref().and_then(|expr| match expr {
496        Expr::Lit(v) => match &v.lit {
497            syn::Lit::Str(v) => Some(serde_json::json!(v.value())),
498            syn::Lit::Bool(v) => Some(serde_json::json!(v.value)),
499            syn::Lit::Int(v) => v
500                .base10_digits()
501                .parse()
502                .ok()
503                .map(serde_json::Value::Number),
504            syn::Lit::Float(v) => v
505                .base10_digits()
506                .parse()
507                .ok()
508                .map(serde_json::Value::Number),
509            _ => None,
510        },
511        _ => None,
512    });
513    let annotation=literal.map(|v|{let text=v.to_string();quote!(if __rydantic_ctx.mode==#rd::SchemaMode::Input{property["default"]=#rd::__private::serde_json::from_str::<#rd::Value>(#text).expect("literal default");})});
514    quote! {{let mut property=<#ty as #tr>::schema(__rydantic_ctx,#coerce);#before if #condition{#rule.apply_schema(&mut property,__rydantic_ctx.mode,#coerce,<#ty as #tr>::kind());}#(#custom)* #(#asynchronous)* #desc #annotation property}}
515}
516fn schema_shape(
517    fields: &[Field],
518    shape: Shape,
519    container: &attrs::Container,
520    mode: &Mode,
521    tag: ::core::option::Option<(&str, &str)>,
522) -> Tokens {
523    let rd = mode.rd;
524    let tr = mode.tr();
525    if shape == Shape::Unit {
526        return quote!(#rd::__private::serde_json::json!({"type":"null"}));
527    }
528    if shape == Shape::Tuple {
529        let props = fields.iter().map(|f| property_schema(f, mode));
530        let count = fields.len();
531        if count == 0 {
532            return quote!(#rd::__private::serde_json::json!({"type":"array","items":false,"maxItems":0}));
533        }
534        if count == 1 {
535            return props.into_iter().next().expect("one field");
536        }
537        return quote!(#rd::__private::serde_json::json!({"type":"array","prefixItems":[#((#props)),*],"items":false,"minItems":#count,"maxItems":#count}));
538    }
539    let ignore = container.ignore_extra;
540    let known_names = object_names(fields, rd, tag.map(|(key, _)| key));
541    let captures=fields.iter().filter(|f|f.attr.flatten||f.attr.extra).map(|f|{let ty=&f.ty;quote!(usize::from(<#ty as #rd::ObjectType>::captures_extra(&mut ::std::collections::HashSet::new())))});
542    let tag=tag.map(|(key,value)|quote!{schema["properties"][#key]=#rd::__private::serde_json::json!({"type":"string","const":#value});schema["required"].as_array_mut().expect("required").push(#rd::__private::serde_json::json!(#key));});
543    let inserts=fields.iter().map(|f|{let ty=&f.ty;let input=&f.input;let output=&f.output;let exclude=f.attr.exclude;let required=f.attr.required;let default=f.attr.has_default();let property=property_schema(f,mode);
544        let insert=if f.attr.flatten||f.attr.extra{
545            let optional=quote!((__rydantic_ctx.mode==#rd::SchemaMode::Input&&#default)||((!#required||__rydantic_ctx.mode==#rd::SchemaMode::Output)&&<#ty as #tr>::allows_missing()));
546            quote!{let child=#property;#rd::__private::flatten_schema(&mut schema,child,(#optional),<#ty as #rd::ObjectType>::captures_extra(&mut ::std::collections::HashSet::new()),__rydantic_ctx);}
547        }else{quote!{let key=if __rydantic_ctx.mode==#rd::SchemaMode::Input{#input}else{#output};let property=#property;schema["properties"].as_object_mut().expect("properties").insert(key.to_owned(),property);if __rydantic_ctx.mode==#rd::SchemaMode::Output||#required||(!#default&&!<#ty as #tr>::allows_missing()){schema["required"].as_array_mut().expect("required").push(#rd::__private::serde_json::json!(key));}}};
548        quote!{if !(__rydantic_ctx.mode==#rd::SchemaMode::Output&&#exclude){#insert}}
549    });
550    quote! {{let mut schema=#rd::__private::serde_json::json!({"type":"object","properties":{},"required":[],"allOf":[],"additionalProperties":(#ignore&&__rydantic_ctx.mode==#rd::SchemaMode::Input)});#tag #(#inserts)*
551    let captures=0usize #(+ #captures)*;
552    if captures>1 { schema["allOf"].as_array_mut().expect("allOf").push(#rd::Value::Bool(false)); }
553    let mut views=::std::vec::Vec::new();
554    for mode in [#rd::SchemaMode::Input,#rd::SchemaMode::Output] {
555        let seen=&mut ::std::collections::HashSet::<&'static str>::new(); let names={#known_names};
556        let mut unique=::std::collections::HashSet::new();
557        if names.iter().any(|name|!unique.insert(name)) { schema["allOf"].as_array_mut().expect("allOf").push(#rd::Value::Bool(false)); }
558        views.push(names);
559    }
560    let mut required=::std::collections::HashSet::new();
561    schema["required"].as_array_mut().expect("required").retain(|key|required.insert(key.as_str().expect("key").to_owned()));
562    if captures>0 && views[0]!=views[1] && __rydantic_ctx.mode==#rd::SchemaMode::Input {#rd::schema::annotate_local(&mut schema,"extra_alias_collision");}
563    if schema["allOf"].as_array().is_some_and(|v|v.is_empty()){schema.as_object_mut().expect("schema").remove("allOf");}schema}}
564}
565fn constructor(fields: &[Field], shape: Shape, path: Tokens) -> Tokens {
566    let locals = (0..fields.len()).map(field_ident).collect::<Vec<_>>();
567    let names = fields.iter().map(|f| &f.ident);
568    match shape {
569        Shape::Named => quote!(#path{#(#names:#locals,)*}),
570        Shape::Tuple => quote!(#path(#(#locals,)*)),
571        Shape::Unit => path,
572    }
573}
574fn pattern(fields: &[Field], shape: Shape, path: Tokens) -> (Tokens, Vec<Tokens>) {
575    let locals = (0..fields.len()).map(field_ident).collect::<Vec<_>>();
576    let names = fields.iter().map(|f| &f.ident);
577    let values = locals.iter().map(|l| quote!(#l)).collect();
578    let pat = match shape {
579        Shape::Named => quote!(#path{#(#names:#locals,)*}),
580        Shape::Tuple => quote!(#path(#(#locals,)*)),
581        Shape::Unit => path,
582    };
583    (pat, values)
584}
585fn prepare_fields(fields: &[Field], shape: Shape, mode: &Mode, path: Tokens) -> Tokens {
586    let rd = mode.rd;
587    let tr = mode.tr();
588    let (pat, _) = pattern(fields, shape, path.clone());
589    let constructor = constructor(fields, shape, path);
590    let locals = (0..fields.len()).map(field_ident).collect::<Vec<_>>();
591    let prepare=fields.iter().enumerate().map(|(i,f)|{let ty=&f.ty;let local=&locals[i];let rule=rules(f,rd);
592        let raw=if let Some(path)=&f.attr.before{let decoded=mode.decode(ty,quote!(#rd::Input::Owned(raw)),f.attr.coerce);quote!{let raw=<#ty as #tr>::encode_input(&#local,__rydantic_ctx);let raw=match #path(raw){Ok(v)=>v,Err(e)=>{__rydantic_ctx.push(e.with_stage(#rd::Stage::Parse));return None;}};let mut value=#decoded?;}}
593        else{quote!(let mut value=<#ty as #tr>::prepare_default(#local,__rydantic_ctx)?;)};
594        let builtin=f.attr.has_rules().then(||quote!{value=<#ty as #tr>::normalize(value,#rule,__rydantic_ctx)?;<#ty as #tr>::validate_rules(&value,#rule,__rydantic_ctx);});
595        let custom=f.attr.validate.as_ref().map(|path|quote!{if start==__rydantic_ctx.error_count(){if let Err(error)=#path(&value){__rydantic_ctx.push(error);}}});
596        let body=quote!{let start=__rydantic_ctx.error_count();#raw #builtin #custom Some(value)};
597        let body=with_location(f,if shape==Shape::Tuple&&fields.len()==1{usize::MAX}else{i},shape,body,rd);quote!(let #local=#body;)
598    });
599    let done = if fields.is_empty() {
600        quote!(Some(#constructor))
601    } else {
602        quote!(match (#(#locals,)*){(#(Some(#locals),)*)=>Some(#constructor),_=>None})
603    };
604    quote!(#pat=>{#(#prepare)* #done})
605}
606fn expand(input: DeriveInput) -> syn::Result<Tokens> {
607    let rd = match proc_macro_crate::crate_name("rydantic")
608        .map_err(|e| syn::Error::new(input.ident.span(), e))?
609    {
610        proc_macro_crate::FoundCrate::Itself => quote!(::rydantic),
611        proc_macro_crate::FoundCrate::Name(name) => {
612            let name = format_ident!("{name}");
613            quote!(::#name)
614        }
615    };
616    let container = attrs::container(&input.attrs)?;
617    let native_capnp = native::expand(&input, &container, &rd)?;
618    let name = &input.ident;
619    let title = container.title.clone().unwrap_or_else(|| name.to_string());
620    let borrowed = input.generics.lifetimes().next().is_some();
621    let mode = Mode {
622        rd: &rd,
623        borrow: borrowed,
624    };
625    let tr = mode.tr();
626    let mut all_fields = Vec::new();
627    let mut object_impl = None;
628    let (decode, encode, encode_input, validate, schema, asynchronous, kind) = match &input.data {
629        Data::Struct(data) => {
630            if container.tag.is_some() || container.untagged {
631                return Err(syn::Error::new(
632                    name.span(),
633                    "tag and untagged are supported on enums",
634                ));
635            }
636            let fields = fields(&data.fields, None, &container)?;
637            let shape = shape(&data.fields);
638            let ctor = constructor(&fields, shape, quote!(Self));
639            let decode = parse_shape(&fields, shape, ctor, &container, &mode, None);
640            let values = fields
641                .iter()
642                .map(|f| {
643                    let m = &f.member;
644                    quote!(&self.#m)
645                })
646                .collect::<Vec<_>>();
647            let encode = encode_shape(&fields, &values, shape, &mode, false, None);
648            let encode_input = encode_shape(&fields, &values, shape, &mode, true, None);
649            let validate = validate_fields(&fields, &values, shape, &mode);
650            let schema = schema_shape(&fields, shape, &container, &mode, None);
651            let asynchronous = async_fields(&fields, &values, shape, &rd);
652            let kind = match shape {
653                Shape::Named => quote!(#rd::TypeKind::Object),
654                Shape::Tuple if fields.len() == 1 => {
655                    let ty = &fields[0].ty;
656                    quote!(<#ty as #tr>::kind())
657                }
658                Shape::Tuple => quote!(#rd::TypeKind::Array),
659                Shape::Unit => quote!(#rd::TypeKind::Any),
660            };
661            if shape == Shape::Named {
662                let names = object_names(&fields, &rd, None);
663                let present=fields.iter().map(|f|{let ty=&f.ty;let member=&f.member;let input=&f.input;let output=&f.output;let exclude=f.attr.exclude;
664                    if f.attr.flatten||f.attr.extra{quote!{if !(mode==#rd::SchemaMode::Output&&#exclude){names.extend(<#ty as #rd::ObjectType>::reserved_names(&self.#member,mode));}}}
665                    else{quote!{if !(mode==#rd::SchemaMode::Output&&#exclude){names.push(if mode==#rd::SchemaMode::Input{#input}else{#output}.to_owned());}}}
666                });
667                let extra = fields
668                    .iter()
669                    .filter(|f| f.attr.flatten || f.attr.extra)
670                    .map(|f| {
671                        let ty = &f.ty;
672                        quote!(<#ty as #rd::ObjectType>::captures_extra(seen))
673                    });
674                object_impl = Some(quote! {
675                    fn reserved_names(&self,mode:#rd::SchemaMode)->::std::vec::Vec<::std::string::String>{let mut names=::std::vec::Vec::new();#(#present)* names}
676                    fn field_names(mode:#rd::SchemaMode,seen:&mut ::std::collections::HashSet<&'static str>)->::std::vec::Vec<::std::string::String>{if !seen.insert(::core::any::type_name::<Self>()){return ::std::vec::Vec::new();}let names={#names};seen.remove(::core::any::type_name::<Self>());names}
677                    fn captures_extra(seen:&mut ::std::collections::HashSet<&'static str>)->bool{if !seen.insert(::core::any::type_name::<Self>()){return false;}let captures=false #(|| #extra)*;seen.remove(::core::any::type_name::<Self>());captures}
678                });
679            }
680            all_fields.extend(fields);
681            (
682                decode,
683                encode,
684                encode_input,
685                validate,
686                schema,
687                asynchronous,
688                kind,
689            )
690        }
691        Data::Enum(data) => {
692            if data.variants.is_empty() {
693                return Err(syn::Error::new(
694                    name.span(),
695                    "Model requires at least one enum variant",
696                ));
697            }
698            let mut variants = vec![];
699            let mut seen = std::collections::HashSet::new();
700            for v in &data.variants {
701                let wire = attrs::variant_name(&v.attrs, &v.ident.to_string())?;
702                if !seen.insert(wire.clone()) {
703                    return Err(syn::Error::new(
704                        v.ident.span(),
705                        "duplicate enum variant name",
706                    ));
707                }
708                let shape = shape(&v.fields);
709                if container.tag.is_some() && shape == Shape::Tuple {
710                    return Err(syn::Error::new(v.ident.span(),"internally tagged enums require named fields or unit variants; use untagged for tuple variants"));
711                }
712                if container.tag.is_none() && !container.untagged && shape != Shape::Unit {
713                    return Err(syn::Error::new(
714                        v.ident.span(),
715                        "enums with fields require #[model(tag = \"...\")] or #[model(untagged)]",
716                    ));
717                }
718                variants.push((
719                    &v.ident,
720                    wire,
721                    shape,
722                    fields(&v.fields, container.tag.as_deref(), &container)?,
723                ));
724            }
725            let mut decodes = vec![];
726            let mut encodes = vec![];
727            let mut input_encodes = vec![];
728            let mut validates = vec![];
729            let mut asyncs = vec![];
730            let mut schemas = vec![];
731            let wires = variants.iter().map(|(_, w, _, _)| w).collect::<Vec<_>>();
732            for (variant, wire, shape, fields) in &variants {
733                let (pat, values) = pattern(fields, *shape, quote!(Self::#variant));
734                let ctor = constructor(fields, *shape, quote!(Self::#variant));
735                let wire_shape = if container.tag.is_some() {
736                    Shape::Named
737                } else {
738                    *shape
739                };
740                let tag = container.tag.as_deref().map(|t| (t, wire.as_str()));
741                let (encode, encode_input, schema) =
742                    if !container.untagged && container.tag.is_none() {
743                        (
744                            quote!(#rd::__private::serde_json::json!(#wire)),
745                            quote!(#rd::__private::serde_json::json!(#wire)),
746                            quote!(),
747                        )
748                    } else {
749                        (
750                            encode_shape(fields, &values, wire_shape, &mode, false, tag),
751                            encode_shape(fields, &values, wire_shape, &mode, true, tag),
752                            schema_shape(fields, wire_shape, &container, &mode, tag),
753                        )
754                    };
755                encodes.push(quote!(#pat=>{#encode}));
756                input_encodes.push(quote!(#pat=>{#encode_input}));
757                let checks = validate_fields(fields, &values, *shape, &mode);
758                validates.push(quote!(#pat=>{#checks}));
759                let checks = async_fields(fields, &values, *shape, &rd);
760                asyncs.push(quote!(#pat=>{#checks}));
761                schemas.push(schema);
762                if container.untagged {
763                    let body = parse_shape(fields, *shape, ctor, &container, &mode, None);
764                    decodes.push(quote!{
765                        {let mut trial=#rd::Context::new();let result={let __rydantic_ctx=&mut trial;let __rydantic_raw=__rydantic_raw.clone();(||{#body})()};
766                        match trial.finish(result){Ok(value)=>return Some(value),Err(errors)=>__rydantic_branches.push(#rd::__private::serde_json::json!({"variant":#wire,"errors":errors.to_json()}))}}
767                    });
768                } else if container.tag.is_some() {
769                    let body = parse_shape(
770                        fields,
771                        wire_shape,
772                        ctor,
773                        &container,
774                        &mode,
775                        container.tag.as_deref(),
776                    );
777                    decodes.push(quote!(#wire=>{let __rydantic_raw=#rd::Input::Object(__rydantic_object);#body}));
778                } else {
779                    decodes.push(quote!(#wire=>Some(#ctor)));
780                }
781            }
782            let (decode, schema, kind) = if container.untagged {
783                (
784                    quote! {let mut __rydantic_branches=::std::vec::Vec::<#rd::Value>::new();#(#decodes)* __rydantic_ctx.push(#rd::ValidationError::new("union_no_match","no untagged variant accepted the value").with_stage(#rd::Stage::Parse).with_param("branches",#rd::Value::Array(__rydantic_branches)));None},
785                    quote!(#rd::__private::serde_json::json!({"anyOf":[#((#schemas)),*]})),
786                    quote!(#rd::TypeKind::Any),
787                )
788            } else if let Some(tag) = &container.tag {
789                (
790                    quote! {let mut __rydantic_object=__rydantic_raw.object(__rydantic_ctx)?;let tag=match __rydantic_object.remove(#tag){Some(raw)=>{let value=raw.into_value(__rydantic_ctx)?;match value{#rd::Value::String(value)=>value,_=>{__rydantic_ctx.at(#tag,|ctx|ctx.push(#rd::ValidationError::new("union_tag_type","discriminator must be a string").with_stage(#rd::Stage::Parse)));return None;}}},None=>{__rydantic_ctx.at(#tag,|ctx|ctx.push(#rd::ValidationError::new("union_tag_missing","discriminator is required").with_stage(#rd::Stage::Parse)));return None;}};
791                    match tag.as_str(){#(#decodes,)* _=>{__rydantic_ctx.at(#tag,|ctx|ctx.push(#rd::ValidationError::new("union_tag_invalid","unknown discriminator").with_stage(#rd::Stage::Parse).with_param("expected",#rd::__private::serde_json::json!([#(#wires),*]))));None}}},
792                    quote!(#rd::__private::serde_json::json!({"oneOf":[#((#schemas)),*],"discriminator":{"propertyName":#tag}})),
793                    quote!(#rd::TypeKind::Object),
794                )
795            } else {
796                (
797                    quote! {let raw=__rydantic_raw.into_value(__rydantic_ctx)?;match raw.as_str(){Some(wire)=>match wire{#(#decodes,)* _=>{__rydantic_ctx.push(#rd::ValidationError::new("enum","unknown enum value").with_stage(#rd::Stage::Parse).with_param("expected",#rd::__private::serde_json::json!([#(#wires),*])));None}},None=>{__rydantic_ctx.push(#rd::ValidationError::new("type_error","expected enum string").with_stage(#rd::Stage::Parse));None}}},
798                    quote!(#rd::__private::serde_json::json!({"type":"string","enum":[#(#wires),*]})),
799                    quote!(#rd::TypeKind::String),
800                )
801            };
802            for (_, _, _, fields) in variants {
803                all_fields.extend(fields);
804            }
805            (
806                decode,
807                quote!(match self{#(#encodes,)*}),
808                quote!(match self{#(#input_encodes,)*}),
809                quote!(match self{#(#validates,)*}),
810                schema,
811                quote!(match self{#(#asyncs,)*}),
812                kind,
813            )
814        }
815        Data::Union(_) => {
816            return Err(syn::Error::new(
817                name.span(),
818                "Model cannot be derived for unions",
819            ))
820        }
821    };
822    let prepare_default = {
823        let arms = match &input.data {
824            Data::Struct(data) => {
825                let fields = fields(&data.fields, None, &container)?;
826                let arm = prepare_fields(&fields, shape(&data.fields), &mode, quote!(Self));
827                quote!(#arm)
828            }
829            Data::Enum(data) => {
830                let mut arms = vec![];
831                for v in &data.variants {
832                    let variant = &v.ident;
833                    let fields = fields(&v.fields, container.tag.as_deref(), &container)?;
834                    arms.push(prepare_fields(
835                        &fields,
836                        shape(&v.fields),
837                        &mode,
838                        quote!(Self::#variant),
839                    ));
840                }
841                quote!(#(#arms,)*)
842            }
843            _ => unreachable!(),
844        };
845        let custom=container.validate.as_ref().map(|path|quote!{if start==ctx.error_count(){if let Some(value)=&value{if let Err(errors)=#path(value){ctx.extend(errors);}}}});
846        quote! {fn prepare_default(self,ctx:&mut #rd::Context)->::core::option::Option<Self>{let start=ctx.error_count();let value={let __rydantic_ctx=&mut *ctx;match self{#arms}};#custom value}}
847    };
848    let mut generics = input.generics.clone();
849    if borrowed {
850        generics.params.insert(0, parse_quote!('__rydantic_de));
851        for lt in input.generics.lifetimes() {
852            let lt = &lt.lifetime;
853            generics
854                .make_where_clause()
855                .predicates
856                .push(parse_quote!('__rydantic_de:#lt));
857        }
858    }
859    for p in generics.type_params_mut() {
860        p.bounds.push(parse_quote!(#tr));
861    }
862    fn contains_generic(ty: &Type, ident: &Ident) -> bool {
863        match ty{Type::Path(p)=>p.path.segments.iter().any(|s|s.ident==*ident||matches!(&s.arguments,syn::PathArguments::AngleBracketed(a) if a.args.iter().any(|arg|matches!(arg,syn::GenericArgument::Type(t)if contains_generic(t,ident))))),Type::Array(a)=>contains_generic(&a.elem,ident),Type::Reference(r)=>contains_generic(&r.elem,ident),Type::Tuple(t)=>t.elems.iter().any(|t|contains_generic(t,ident)),_=>false}
864    }
865    let mut assertions = vec![];
866    for f in &all_fields {
867        let ty = &f.ty;
868        let a = &f.attr;
869        let generic = input
870            .generics
871            .type_params()
872            .any(|p| contains_generic(ty, &p.ident));
873        let mut bounds = vec![];
874        if a.trim || a.lowercase || a.uppercase || a.email || a.pattern.is_some() {
875            assertions.push(quote!(__rydantic_assert_string::<#ty>();));
876            bounds.push(quote!(#rd::StringType));
877        }
878        if a.min_length.is_some() || a.max_length.is_some() {
879            assertions.push(quote!(__rydantic_assert_length::<#ty>();));
880            bounds.push(quote!(#rd::LengthType));
881        }
882        if a.ge.is_some() || a.gt.is_some() || a.le.is_some() || a.lt.is_some() {
883            assertions.push(quote!(__rydantic_assert_number::<#ty>();));
884            bounds.push(quote!(#rd::NumericType));
885        }
886        if a.flatten {
887            bounds.push(quote!(#rd::ObjectType));
888        }
889        if a.extra {
890            bounds.push(quote!(#rd::ExtraFields));
891            assertions.push(quote!(__rydantic_assert_extra::<#ty>();));
892        }
893        if a.default_trait {
894            bounds.push(quote!(::core::default::Default));
895        }
896        if generic {
897            for bound in bounds {
898                generics
899                    .make_where_clause()
900                    .predicates
901                    .push(parse_quote!(#ty:#bound));
902            }
903        }
904    }
905    let (_, ty_generics, _) = input.generics.split_for_impl();
906    let (impl_generics, _, where_clause) = generics.split_for_impl();
907    let mut de_generics = generics.clone();
908    if !borrowed {
909        de_generics.params.insert(0, parse_quote!('__rydantic_de));
910    }
911    let (de_impl_generics, _, de_where_clause) = de_generics.split_for_impl();
912    let custom_parse=container.validate.as_ref().map(|path|quote!{if __rydantic_ctx.error_count()==start{if let Some(value)=&value{if let Err(errors)=#path(value){__rydantic_ctx.extend(errors);}}}});
913    let custom_validate=container.validate.as_ref().map(|path|quote!{if __rydantic_ctx.error_count()==start{if let Err(errors)=#path(self){__rydantic_ctx.extend(errors);}}});
914    let schema_custom=container.validate.as_ref().map(|path|quote!(#rd::schema::annotate_local(&mut schema,concat!("validate:",stringify!(#path)));));
915    let schema_async=container.async_validate.as_ref().map(|path|quote!(#rd::schema::annotate_local(&mut schema,concat!("async_validate:",stringify!(#path)));));
916    let direct_async = container.async_validate.is_some()
917        || all_fields.iter().any(|f| f.attr.async_validate.is_some());
918    let nested_async = all_fields.iter().map(|f| {
919        let ty = &f.ty;
920        quote!(<#ty as #tr>::async_required(seen))
921    });
922    let model_impl = if borrowed {
923        quote!(impl #impl_generics #rd::BorrowModel<'__rydantic_de> for #name #ty_generics #where_clause {})
924    } else {
925        quote!(impl #impl_generics #rd::Model for #name #ty_generics #where_clause {})
926    };
927    let dump = if borrowed {
928        quote!(<Self as #rd::BorrowModel<'__rydantic_de>>::dump(self))
929    } else {
930        quote!(<Self as #rd::Model>::dump(self))
931    };
932    let deserialize = if borrowed {
933        quote! {let raw=<&'__rydantic_de #rd::__private::serde_json::value::RawValue as #rd::__private::serde::Deserialize>::deserialize(deserializer)?;<Self as #rd::BorrowModel<'__rydantic_de>>::parse_raw(raw).map_err(#rd::__private::serde::de::Error::custom)}
934    } else {
935        quote! {let raw=<::std::boxed::Box<#rd::RawValue> as #rd::__private::serde::Deserialize>::deserialize(deserializer)?;<Self as #rd::Model>::parse_raw(&raw).map_err(#rd::__private::serde::de::Error::custom)}
936    };
937    let object_impl=object_impl.map(|body|quote!(impl #impl_generics #rd::ObjectType for #name #ty_generics #where_clause{#body}));
938    let bridge=(!borrowed).then(||quote!{
939        impl #de_impl_generics #rd::BorrowType<'__rydantic_de> for #name #ty_generics #de_where_clause{
940            fn decode(raw:#rd::Input<'__rydantic_de>,ctx:&mut #rd::Context,coerce:bool)->::core::option::Option<Self>{<Self as #rd::JsonType>::decode_input(raw,ctx,coerce)}
941            fn prepare_default(self,ctx:&mut #rd::Context)->::core::option::Option<Self>{<Self as #rd::JsonType>::prepare_default(self,ctx)}
942            fn encode(&self,ctx:&mut #rd::Context)->#rd::Value{<Self as #rd::JsonType>::encode(self,ctx)}
943            fn encode_input(&self,ctx:&mut #rd::Context)->#rd::Value{<Self as #rd::JsonType>::encode_input(self,ctx)}
944            fn schema(ctx:&mut #rd::SchemaContext,coerce:bool)->#rd::Value{<Self as #rd::JsonType>::schema(ctx,coerce)}
945            fn validate_into(&self,ctx:&mut #rd::Context){<Self as #rd::JsonType>::validate_into(self,ctx)}
946            fn normalize(self,rules:&#rd::rules::Rules,ctx:&mut #rd::Context)->::core::option::Option<Self>{<Self as #rd::JsonType>::normalize(self,rules,ctx)}
947            fn validate_rules(&self,rules:&#rd::rules::Rules,ctx:&mut #rd::Context){<Self as #rd::JsonType>::validate_rules(self,rules,ctx)}
948            fn async_required(seen:&mut ::std::collections::HashSet<&'static str>)->bool{<Self as #rd::JsonType>::async_required(seen)}
949            fn kind()->#rd::TypeKind{<Self as #rd::JsonType>::kind()}
950        }
951    });
952    let mut async_generics = input.generics.clone();
953    for p in async_generics.type_params_mut() {
954        p.bounds.push(parse_quote!(#rd::AsyncCheck));
955    }
956    let (async_impl_generics, _, async_where_clause) = async_generics.split_for_impl();
957    let custom_async=container.async_validate.as_ref().map(|path|quote!{if start==__rydantic_ctx.error_count(){if let Err(errors)=#path(self).await{__rydantic_ctx.extend(errors);}}});
958    let body = quote! {
959        // Mixed-site hygiene does not isolate item names. Keep assertion helpers
960        // outside the scope containing user callback paths and default expressions.
961        {
962            fn __rydantic_assert_string<T:#rd::StringType>(){}fn __rydantic_assert_length<T:#rd::LengthType>(){}fn __rydantic_assert_number<T:#rd::NumericType>(){}fn __rydantic_assert_extra<T:#rd::ExtraFields>(){}
963            #(#assertions)*
964        }
965        let start=__rydantic_ctx.error_count();let value=(||{#decode})();#custom_parse value
966    };
967    let decode_methods = if borrowed {
968        quote! {fn decode(__rydantic_raw:#rd::Input<'__rydantic_de>,__rydantic_ctx:&mut #rd::Context,_coerce:bool)->::core::option::Option<Self>{#body}}
969    } else {
970        quote! {
971            fn decode(raw:#rd::Value,ctx:&mut #rd::Context,coerce:bool)->::core::option::Option<Self>{<Self as #rd::JsonType>::decode_input(#rd::Input::Owned(raw),ctx,coerce)}
972            fn decode_input(__rydantic_raw:#rd::Input<'_>,__rydantic_ctx:&mut #rd::Context,_coerce:bool)->::core::option::Option<Self>{#body}
973        }
974    };
975    Ok(quote! {
976        impl #impl_generics #tr for #name #ty_generics #where_clause{
977            #decode_methods
978            #prepare_default
979            fn encode(&self,__rydantic_ctx:&mut #rd::Context)->#rd::Value{#encode}
980            fn encode_input(&self,__rydantic_ctx:&mut #rd::Context)->#rd::Value{#encode_input}
981            fn validate_into(&self,__rydantic_ctx:&mut #rd::Context){let start=__rydantic_ctx.error_count();#validate #custom_validate}
982            fn schema(__rydantic_ctx:&mut #rd::SchemaContext,_coerce:bool)->#rd::Value{__rydantic_ctx.reference(::core::any::type_name::<Self>(),|__rydantic_ctx|{let mut schema=#schema;schema["title"]=#rd::__private::serde_json::json!(#title);#schema_custom #schema_async schema})}
983            fn kind()->#rd::TypeKind{#kind}
984            fn async_required(seen:&mut ::std::collections::HashSet<&'static str>)->bool{if !seen.insert(::core::any::type_name::<Self>()){return false;}#direct_async #(|| #nested_async)*}
985        }
986        #model_impl #object_impl #bridge #native_capnp
987        impl #async_impl_generics #rd::AsyncCheck for #name #ty_generics #async_where_clause{
988            fn check_async<'__rydantic_future>(&'__rydantic_future self,__rydantic_ctx:&'__rydantic_future mut #rd::Context)->#rd::BoxFuture<'__rydantic_future,()>{
989                ::std::boxed::Box::pin(async move{let start=__rydantic_ctx.error_count();#asynchronous #custom_async})
990            }
991        }
992        impl #impl_generics #rd::__private::serde::Serialize for #name #ty_generics #where_clause{
993            fn serialize<S:#rd::__private::serde::Serializer>(&self,serializer:S)->::core::result::Result<S::Ok,S::Error>{let value=#dump.map_err(#rd::__private::serde::ser::Error::custom)?;#rd::__private::serde::Serialize::serialize(&value,serializer)}
994        }
995        impl #de_impl_generics #rd::__private::serde::Deserialize<'__rydantic_de> for #name #ty_generics #de_where_clause{
996            fn deserialize<D:#rd::__private::serde::Deserializer<'__rydantic_de>>(deserializer:D)->::core::result::Result<Self,D::Error>{#deserialize}
997        }
998    })
999}