schemars_derive/
schema_exprs.rs

1use crate::{ast::*, attr::WithAttr, idents::*};
2use proc_macro2::{Span, TokenStream};
3use quote::ToTokens;
4use serde_derive_internals::ast::Style;
5use serde_derive_internals::attr::{self as serde_attr, Default as SerdeDefault, TagType};
6use syn::spanned::Spanned;
7
8pub struct SchemaExpr {
9    /// Definitions for types or functions that may be used within the creator or mutators
10    definitions: Vec<TokenStream>,
11    /// An expression that produces a `Schema`
12    creator: TokenStream,
13    /// Statements (including terminating semicolon) that mutate a var `schema` of type `Schema`
14    mutators: Vec<TokenStream>,
15}
16
17impl From<TokenStream> for SchemaExpr {
18    fn from(creator: TokenStream) -> Self {
19        Self {
20            definitions: Vec::new(),
21            creator,
22            mutators: Vec::new(),
23        }
24    }
25}
26
27impl ToTokens for SchemaExpr {
28    fn to_tokens(&self, tokens: &mut TokenStream) {
29        let Self {
30            definitions,
31            creator,
32            mutators,
33        } = self;
34
35        tokens.extend(if mutators.is_empty() {
36            quote!({
37                #(#definitions)*
38                #creator
39            })
40        } else {
41            quote!({
42                #(#definitions)*
43                let mut #SCHEMA = #creator;
44                #(#mutators)*
45                #SCHEMA
46            })
47        });
48    }
49}
50
51pub fn expr_for_container(cont: &Container) -> SchemaExpr {
52    let type_from = cont
53        .serde_attrs
54        .type_from()
55        .or(cont.serde_attrs.type_try_from());
56    let type_into = cont.serde_attrs.type_into();
57
58    let mut schema_expr = if let Some(with) = &cont.attrs.with {
59        expr_for_container_with(cont, with)
60    } else if let Some(transparent_field) = cont.transparent_field() {
61        expr_for_newtype_struct(cont, transparent_field)
62    } else if let (Some(from), Some(into)) = (type_from, type_into) {
63        quote! {
64            if #GENERATOR.contract().is_deserialize() {
65                <#from as schemars::JsonSchema>::json_schema(#GENERATOR)
66            } else {
67                <#into as schemars::JsonSchema>::json_schema(#GENERATOR)
68            }
69        }
70        .into()
71    } else {
72        let schema_expr = match &cont.data {
73            Data::Struct(Style::Unit, _) => expr_for_unit_struct(),
74            Data::Struct(Style::Newtype, fields) => expr_for_newtype_struct(cont, &fields[0]),
75            Data::Struct(Style::Tuple, fields) => expr_for_tuple_struct(cont, fields),
76            Data::Struct(Style::Struct, fields) => expr_for_struct(
77                cont,
78                fields,
79                cont.serde_attrs.default(),
80                cont.serde_attrs.deny_unknown_fields(),
81            ),
82            Data::Enum(variants) => expr_for_enum(cont, variants, &cont.serde_attrs),
83        };
84
85        if let Some(from) = type_from {
86            quote! {
87                if #GENERATOR.contract().is_deserialize() {
88                    <#from as schemars::JsonSchema>::json_schema(#GENERATOR)
89                } else {
90                    #schema_expr
91                }
92            }
93            .into()
94        } else if let Some(into) = type_into {
95            quote! {
96                if #GENERATOR.contract().is_serialize() {
97                    <#into as schemars::JsonSchema>::json_schema(#GENERATOR)
98                } else {
99                    #schema_expr
100                }
101            }
102            .into()
103        } else {
104            schema_expr
105        }
106    };
107
108    cont.add_mutators(&mut schema_expr.mutators);
109
110    schema_expr
111}
112
113pub fn expr_for_repr(cont: &Container) -> Result<SchemaExpr, syn::Error> {
114    let repr_type = cont.attrs.repr.as_ref().ok_or_else(|| {
115        syn::Error::new(
116            Span::call_site(),
117            "JsonSchema_repr: missing #[repr(...)] attribute",
118        )
119    })?;
120
121    let Data::Enum(variants) = &cont.data else {
122        return Err(syn::Error::new(
123            Span::call_site(),
124            "JsonSchema_repr can only be used on enums",
125        ));
126    };
127
128    if let Some(non_unit_error) = variants.iter().find_map(|v| match v.style {
129        Style::Unit => None,
130        _ => Some(syn::Error::new_spanned(
131            v.original,
132            "JsonSchema_repr: must be a unit variant",
133        )),
134    }) {
135        return Err(non_unit_error);
136    }
137
138    let enum_ident = &cont.ident;
139    let variant_idents = variants.iter().map(|v| &v.ident);
140
141    let mut schema_expr = SchemaExpr::from(quote!({
142        let mut map = schemars::_private::serde_json::Map::new();
143        map.insert("type".into(), "integer".into());
144        map.insert(
145            "enum".into(),
146            schemars::_private::serde_json::Value::Array({
147                let mut enum_values = schemars::_private::alloc::vec::Vec::new();
148                #(enum_values.push((#enum_ident::#variant_idents as #repr_type).into());)*
149                enum_values
150            }),
151        );
152        schemars::Schema::from(map)
153    }));
154
155    cont.add_mutators(&mut schema_expr.mutators);
156
157    Ok(schema_expr)
158}
159
160fn expr_for_container_with(cont: &Container, with_attr: &WithAttr) -> SchemaExpr {
161    let (ty, type_def) = type_for_schema(cont, with_attr);
162
163    let mut schema_expr = SchemaExpr::from(quote! {
164        <#ty as schemars::JsonSchema>::json_schema(#GENERATOR)
165    });
166
167    schema_expr.definitions.extend(type_def);
168
169    schema_expr
170}
171
172fn expr_for_field(
173    cont: &Container,
174    field: &Field,
175    is_internal_tagged_enum_newtype: bool,
176) -> SchemaExpr {
177    let (ty, type_def) = type_for_field_schema(cont, field);
178    let span = field.original.span();
179
180    let schema_expr = if field.attrs.validation.required {
181        quote_spanned! {span=>
182            <#ty as schemars::JsonSchema>::_schemars_private_non_optional_json_schema(#GENERATOR)
183        }
184    } else if is_internal_tagged_enum_newtype {
185        quote_spanned! {span=>
186            schemars::_private::json_schema_for_internally_tagged_enum_newtype_variant::<#ty>(#GENERATOR)
187        }
188    } else {
189        quote_spanned! {span=>
190            #GENERATOR.subschema_for::<#ty>()
191        }
192    };
193    let mut schema_expr = SchemaExpr::from(schema_expr);
194
195    schema_expr.definitions.extend(type_def);
196    field.add_mutators(&mut schema_expr.mutators);
197
198    schema_expr
199}
200
201fn type_for_field_schema(cont: &Container, field: &Field) -> (syn::Type, Option<TokenStream>) {
202    match &field.attrs.with {
203        None => (field.ty.clone(), None),
204        Some(with_attr) => type_for_schema(cont, with_attr),
205    }
206}
207
208fn type_for_schema(cont: &Container, with_attr: &WithAttr) -> (syn::Type, Option<TokenStream>) {
209    match with_attr {
210        WithAttr::Type(ty) => (ty.clone(), None),
211        WithAttr::Function(fun) => {
212            let cont_name = &cont.ident;
213            let fn_name = fun.segments.last().unwrap().ident.to_string();
214            let (impl_generics, ty_generics, where_clause) = cont.generics.split_for_impl();
215
216            let type_def = quote_spanned! {fun.span()=>
217                struct _SchemarsSchemaWithFunction<T: ?::core::marker::Sized>(::core::marker::PhantomData<T>);
218
219                impl #impl_generics schemars::JsonSchema for _SchemarsSchemaWithFunction<#cont_name #ty_generics> #where_clause {
220                    fn inline_schema() -> bool {
221                        true
222                    }
223
224                    fn schema_name() -> schemars::_private::alloc::borrow::Cow<'static, str> {
225                        schemars::_private::alloc::borrow::Cow::Borrowed(#fn_name)
226                    }
227
228                    fn schema_id() -> schemars::_private::alloc::borrow::Cow<'static, str> {
229                        schemars::_private::alloc::borrow::Cow::Borrowed(::core::concat!(
230                            "_SchemarsSchemaWithFunction/",
231                            ::core::module_path!(),
232                            "/",
233                            ::core::stringify!(#fun)
234                        ))
235                    }
236
237                    fn json_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
238                        #fun(generator)
239                    }
240                }
241            };
242
243            (
244                parse_quote!(_SchemarsSchemaWithFunction::<#cont_name #ty_generics>),
245                Some(type_def),
246            )
247        }
248    }
249}
250
251fn expr_for_enum(
252    cont: &Container,
253    variants: &[Variant],
254    cattrs: &serde_attr::Container,
255) -> SchemaExpr {
256    if variants.is_empty() {
257        return quote!(schemars::Schema::from(false)).into();
258    }
259    let deny_unknown_fields = cattrs.deny_unknown_fields();
260    let variants = variants.iter();
261
262    match cattrs.tag() {
263        TagType::External => expr_for_external_tagged_enum(cont, variants, deny_unknown_fields),
264        TagType::None => expr_for_untagged_enum(cont, variants, deny_unknown_fields),
265        TagType::Internal { tag } => {
266            expr_for_internal_tagged_enum(cont, variants, tag, deny_unknown_fields)
267        }
268        TagType::Adjacent { tag, content } => {
269            expr_for_adjacent_tagged_enum(cont, variants, tag, content, deny_unknown_fields)
270        }
271    }
272}
273
274fn expr_for_external_tagged_enum<'a>(
275    cont: &Container,
276    variants: impl Iterator<Item = &'a Variant<'a>>,
277    deny_unknown_fields: bool,
278) -> SchemaExpr {
279    let (unit_variants, complex_variants): (Vec<_>, Vec<_>) = variants.partition(|v| {
280        v.is_unit() && v.attrs.is_default() && !v.serde_attrs.untagged() && !cont.attrs.ref_variants
281    });
282    let add_unit_names = unit_variants.iter().map(|v| {
283        let name = v.name();
284        v.with_contract_check(quote! {
285            enum_values.push((#name).into());
286        })
287    });
288    let unit_schema = SchemaExpr::from(quote!({
289        let mut map = schemars::_private::serde_json::Map::new();
290        map.insert("type".into(), "string".into());
291        map.insert(
292            "enum".into(),
293            schemars::_private::serde_json::Value::Array({
294                let mut enum_values = schemars::_private::alloc::vec::Vec::new();
295                #(#add_unit_names)*
296                enum_values
297            }),
298        );
299        schemars::Schema::from(map)
300    }));
301
302    if complex_variants.is_empty() {
303        return unit_schema;
304    }
305
306    let mut schemas = Vec::new();
307    if !unit_variants.is_empty() {
308        schemas.push((None, unit_schema));
309    }
310
311    schemas.extend(complex_variants.into_iter().map(|variant| {
312        if variant.serde_attrs.untagged() {
313            return (
314                Some(variant),
315                expr_for_untagged_enum_variant(cont, variant, deny_unknown_fields, true),
316            );
317        }
318
319        let name = variant.name();
320
321        let mut schema_expr =
322            SchemaExpr::from(if variant.is_unit() && variant.attrs.with.is_none() {
323                quote! {
324                    schemars::_private::new_unit_enum_variant(#name)
325                }
326            } else {
327                let sub_schema =
328                    expr_for_untagged_enum_variant(cont, variant, deny_unknown_fields, false);
329                quote! {
330                    schemars::_private::new_externally_tagged_enum_variant(#name, #sub_schema)
331                }
332            });
333
334        variant.add_mutators(&mut schema_expr.mutators);
335
336        (Some(variant), schema_expr)
337    }));
338
339    variant_subschemas(cont, true, schemas)
340}
341
342fn expr_for_internal_tagged_enum<'a>(
343    cont: &Container,
344    variants: impl Iterator<Item = &'a Variant<'a>>,
345    tag_name: &str,
346    deny_unknown_fields: bool,
347) -> SchemaExpr {
348    let variant_schemas = variants
349        .map(|variant| {
350            if variant.serde_attrs.untagged() {
351                return (Some(variant), expr_for_untagged_enum_variant(cont, variant, deny_unknown_fields, true))
352            }
353
354            let mut schema_expr = expr_for_internal_tagged_enum_variant(cont, variant, deny_unknown_fields);
355
356            let name = variant.name();
357            schema_expr.mutators.push(quote!(
358                schemars::_private::apply_internal_enum_variant_tag(&mut #SCHEMA, #tag_name, #name, #deny_unknown_fields);
359            ));
360
361            variant.add_mutators(&mut schema_expr.mutators);
362
363            (Some(variant), schema_expr)
364        })
365        .collect();
366
367    variant_subschemas(cont, true, variant_schemas)
368}
369
370fn expr_for_untagged_enum<'a>(
371    cont: &Container,
372    variants: impl Iterator<Item = &'a Variant<'a>>,
373    deny_unknown_fields: bool,
374) -> SchemaExpr {
375    let schemas = variants
376        .map(|variant| {
377            let schema_expr =
378                expr_for_untagged_enum_variant(cont, variant, deny_unknown_fields, true);
379
380            (Some(variant), schema_expr)
381        })
382        .collect();
383
384    // Untagged enums can easily have variants whose schemas overlap; rather
385    // that checking the exclusivity of each subschema we simply us `any_of`.
386    variant_subschemas(cont, false, schemas)
387}
388
389fn expr_for_adjacent_tagged_enum<'a>(
390    cont: &Container,
391    variants: impl Iterator<Item = &'a Variant<'a>>,
392    tag_name: &str,
393    content_name: &str,
394    deny_unknown_fields: bool,
395) -> SchemaExpr {
396    let schemas = variants
397        .map(|variant| {
398            if variant.serde_attrs.untagged() {
399                return (
400                    Some(variant),
401                    expr_for_untagged_enum_variant(cont, variant, deny_unknown_fields, true),
402                );
403            }
404
405            let content_schema = if variant.is_unit() && variant.attrs.with.is_none() {
406                None
407            } else {
408                Some(expr_for_untagged_enum_variant(
409                    cont,
410                    variant,
411                    deny_unknown_fields,
412                    false,
413                ))
414            };
415
416            let (add_content_to_props, add_content_to_required) = content_schema
417                .map(|content_schema| {
418                    (
419                        quote!(#content_name: (#content_schema),),
420                        quote!(#content_name,),
421                    )
422                })
423                .unwrap_or_default();
424
425            let name = variant.name();
426            let tag_schema = quote! {
427                schemars::json_schema!({
428                    "type": "string",
429                    "const": #name,
430                })
431            };
432
433            let set_additional_properties = if deny_unknown_fields {
434                quote! {
435                    "additionalProperties": false,
436                }
437            } else {
438                TokenStream::new()
439            };
440
441            let mut outer_schema = SchemaExpr::from(quote!(schemars::json_schema!({
442                "type": "object",
443                "properties": {
444                    #tag_name: (#tag_schema),
445                    #add_content_to_props
446                },
447                "required": [
448                    #tag_name,
449                    #add_content_to_required
450                ],
451                // As we're creating a "wrapper" object, we can honor the
452                // disposition of deny_unknown_fields.
453                #set_additional_properties
454            })));
455
456            variant.add_mutators(&mut outer_schema.mutators);
457
458            (Some(variant), outer_schema)
459        })
460        .collect();
461
462    variant_subschemas(cont, true, schemas)
463}
464
465/// Callers must determine if all subschemas are mutually exclusive. The current behaviour is to
466/// assume that variants are mutually exclusive except for untagged enums.
467fn variant_subschemas(
468    cont: &Container,
469    mut unique: bool,
470    schemas: Vec<(Option<&Variant>, SchemaExpr)>,
471) -> SchemaExpr {
472    if schemas
473        .iter()
474        .any(|(v, _)| v.is_some_and(|v| v.serde_attrs.untagged()))
475    {
476        unique = false;
477    }
478
479    let keyword = if unique { "oneOf" } else { "anyOf" };
480    let add_schemas = schemas.into_iter().map(|(variant, mut schema)| {
481        if cont.attrs.ref_variants {
482            schema = enum_ref_variants(cont, variant, schema);
483        }
484
485        let add = quote! {
486            enum_values.push(#schema.to_value());
487        };
488        match variant {
489            Some(v) => v.with_contract_check(add),
490            None => add,
491        }
492    });
493    quote!({
494        let mut map = schemars::_private::serde_json::Map::new();
495        map.insert(
496            #keyword.into(),
497            schemars::_private::serde_json::Value::Array({
498                let mut enum_values = schemars::_private::alloc::vec::Vec::new();
499                #(#add_schemas)*
500                enum_values
501            }),
502        );
503        schemars::Schema::from(map)
504    })
505    .into()
506}
507
508fn enum_ref_variants(cont: &Container, variant: Option<&Variant>, expr: SchemaExpr) -> SchemaExpr {
509    let Some(variant) = variant else {
510        return expr;
511    };
512
513    let cont_name = &cont.ident;
514    // FIXME can this use serialize name where appropriate?
515    let variant_name = variant.serde_attrs.name().deserialize_name();
516    let (impl_generics, ty_generics, where_clause) = cont.generics.split_for_impl();
517
518    let type_def = quote! {
519        struct _SchemarsRefVariant<T: ?::core::marker::Sized>(::core::marker::PhantomData<T>);
520
521        impl #impl_generics schemars::JsonSchema for _SchemarsRefVariant<#cont_name #ty_generics> #where_clause {
522            fn inline_schema() -> bool {
523                false
524            }
525
526            fn schema_name() -> schemars::_private::alloc::borrow::Cow<'static, str> {
527                schemars::_private::alloc::borrow::Cow::Borrowed(#variant_name)
528            }
529
530            fn schema_id() -> schemars::_private::alloc::borrow::Cow<'static, str> {
531                schemars::_private::alloc::borrow::Cow::Owned(
532                    schemars::_private::alloc::format!(
533                        "_SchemarsRefVariant/{}::{}",
534                        <#cont_name #ty_generics as schemars::JsonSchema>::schema_id(),
535                        #variant_name,
536                ))
537            }
538
539            fn json_schema(#GENERATOR: &mut schemars::SchemaGenerator) -> schemars::Schema {
540                #expr
541            }
542        }
543    };
544
545    let mut expr = SchemaExpr::from(
546        quote!(#GENERATOR.subschema_for::<_SchemarsRefVariant::<#cont_name #ty_generics>>()),
547    );
548
549    expr.definitions.push(type_def);
550
551    expr
552}
553
554// This function is also used for tagged variants, in which case the resulting SchemaExpr will be
555// embedded or mutated to include the tag. `is_actually_untagged` will be true if the enum or the
556// variant has the `untagged` attribute.
557fn expr_for_untagged_enum_variant(
558    cont: &Container,
559    variant: &Variant,
560    deny_unknown_fields: bool,
561    is_actually_untagged: bool,
562) -> SchemaExpr {
563    let mut schema_expr = if let Some(with_attr) = &variant.attrs.with {
564        let (ty, type_def) = type_for_schema(cont, with_attr);
565        let mut schema_expr = SchemaExpr::from(quote_spanned! {variant.original.span()=>
566            #GENERATOR.subschema_for::<#ty>()
567        });
568
569        schema_expr.definitions.extend(type_def);
570        schema_expr
571    } else {
572        match variant.style {
573            Style::Unit => expr_for_unit_struct(),
574            Style::Newtype => expr_for_field(cont, &variant.fields[0], false),
575            Style::Tuple => expr_for_tuple_struct(cont, &variant.fields),
576            Style::Struct => expr_for_struct(
577                cont,
578                &variant.fields,
579                &SerdeDefault::None,
580                deny_unknown_fields,
581            ),
582        }
583    };
584
585    if is_actually_untagged {
586        if variant.attrs.common.title.is_none() {
587            let title = variant.name();
588            schema_expr.mutators.push(quote! {
589                if #GENERATOR.settings().untagged_enum_variant_titles {
590                    #SCHEMA.insert("title".into(), #title.into());
591                }
592            });
593        }
594
595        variant.add_mutators(&mut schema_expr.mutators);
596    }
597
598    schema_expr
599}
600
601fn expr_for_internal_tagged_enum_variant(
602    cont: &Container,
603    variant: &Variant,
604    deny_unknown_fields: bool,
605) -> SchemaExpr {
606    if let Some(with_attr) = &variant.attrs.with {
607        let (ty, type_def) = type_for_schema(cont, with_attr);
608        let mut schema_expr = SchemaExpr::from(quote_spanned! {variant.original.span()=>
609            <#ty as schemars::JsonSchema>::json_schema(#GENERATOR)
610        });
611
612        schema_expr.definitions.extend(type_def);
613
614        return schema_expr;
615    }
616
617    match variant.style {
618        Style::Unit => expr_for_unit_struct(),
619        Style::Newtype => expr_for_field(cont, &variant.fields[0], true),
620        Style::Tuple => expr_for_tuple_struct(cont, &variant.fields),
621        Style::Struct => expr_for_struct(
622            cont,
623            &variant.fields,
624            &SerdeDefault::None,
625            deny_unknown_fields,
626        ),
627    }
628}
629
630fn expr_for_unit_struct() -> SchemaExpr {
631    quote! {
632        #GENERATOR.subschema_for::<()>()
633    }
634    .into()
635}
636
637fn expr_for_newtype_struct(cont: &Container, field: &Field) -> SchemaExpr {
638    expr_for_field(cont, field, false)
639}
640
641fn expr_for_tuple_struct(cont: &Container, fields: &[Field]) -> SchemaExpr {
642    let fields: Vec<_> = fields
643        .iter()
644        .map(|f| {
645            let field_expr = expr_for_field(cont, f, false);
646            f.with_contract_check(quote! {
647                prefix_items.push((#field_expr).to_value());
648            })
649        })
650        .collect();
651
652    let max_len = fields.len();
653
654    // The length check must be done at runtime because it can vary based on the contract if any
655    // items have `skip_serializing` or `skip_deserializing` attributes.
656    quote!({
657        let mut prefix_items = schemars::_private::alloc::vec::Vec::<schemars::_private::serde_json::Value>::with_capacity(#max_len);
658        #(#fields)*
659        let len = prefix_items.len();
660
661        let mut map = schemars::_private::serde_json::Map::new();
662        map.insert("type".into(), "array".into());
663        if !prefix_items.is_empty() {
664            map.insert("prefixItems".into(), prefix_items.into());
665            map.insert("minItems".into(), len.into());
666        }
667        map.insert("maxItems".into(), len.into());
668
669        schemars::Schema::from(map)
670    })
671    .into()
672}
673
674fn expr_for_struct(
675    cont: &Container,
676    fields: &[Field],
677    default: &SerdeDefault,
678    deny_unknown_fields: bool,
679) -> SchemaExpr {
680    let set_container_default = match default {
681        SerdeDefault::None => None,
682        SerdeDefault::Default => Some(quote!(let #STRUCT_DEFAULT = Self::default();)),
683        SerdeDefault::Path(path) => Some(quote!(let #STRUCT_DEFAULT = #path();)),
684    };
685
686    // a vec of mutators
687    let properties: Vec<TokenStream> = fields
688        .iter()
689        .filter(|f| !f.serde_attrs.skip_deserializing() || !f.serde_attrs.skip_serializing())
690        .map(|field| {
691            if field.serde_attrs.flatten() {
692                let (ty, type_def) = type_for_field_schema(cont, field);
693
694                let required = field.attrs.validation.required;
695                let mut schema_expr = SchemaExpr::from(quote_spanned! {ty.span()=>
696                    schemars::_private::json_schema_for_flatten::<#ty>(#GENERATOR, #required)
697                });
698
699                schema_expr.definitions.extend(type_def);
700
701                field.with_contract_check(quote! {
702                    schemars::_private::flatten(&mut #SCHEMA, #schema_expr);
703                })
704            } else {
705                let mut schema_expr = expr_for_field(cont, field, false);
706
707                if let Some(default) = field_default_expr(field, set_container_default.is_some()) {
708                    schema_expr.mutators.push(quote! {
709                        #default.and_then(|d| schemars::_schemars_maybe_to_value!(d))
710                            .map(|d| #SCHEMA.insert("default".into(), d));
711                    });
712                }
713
714                let name = field.name();
715                let (ty, type_def) = type_for_field_schema(cont, field);
716
717                if type_def.is_some() {
718                    assert!(!schema_expr.definitions.is_empty());
719                }
720
721                let has_default = set_container_default.is_some() || !field.serde_attrs.default().is_none();
722                let has_skip_serialize_if = field.serde_attrs.skip_serializing_if().is_some();
723                let required_attr = field.attrs.validation.required;
724
725                let is_optional = if has_skip_serialize_if && has_default {
726                    quote!(true)
727                } else if !has_skip_serialize_if && !has_default && !required_attr {
728                    quote!(#GENERATOR.contract().is_deserialize() && <#ty as schemars::JsonSchema>::_schemars_private_is_option())
729                } else {
730                    quote!(if #GENERATOR.contract().is_deserialize() {
731                        #has_default || (!#required_attr && <#ty as schemars::JsonSchema>::_schemars_private_is_option())
732                    } else {
733                        #has_skip_serialize_if
734                    })
735                };
736
737                // Embed definitions outside of `#schema_expr`, because they may contain the
738                // definition of the `#ty` type which is used in `#is_optional``
739                let definitions = core::mem::take(&mut schema_expr.definitions);
740                field.with_contract_check(quote!({
741                    #(#definitions)*
742                    schemars::_private::insert_object_property(&mut #SCHEMA, #name, #is_optional, #schema_expr);
743                }))
744            }
745        })
746        .collect();
747
748    let set_additional_properties = if deny_unknown_fields {
749        quote! {
750            "additionalProperties": false,
751        }
752    } else {
753        TokenStream::new()
754    };
755
756    SchemaExpr {
757        definitions: set_container_default.into_iter().collect(),
758        creator: quote!(schemars::json_schema!({
759            "type": "object",
760            #set_additional_properties
761        })),
762        mutators: properties,
763    }
764}
765
766fn field_default_expr(field: &Field, container_has_default: bool) -> Option<TokenStream> {
767    let field_default = field.serde_attrs.default();
768    if field.serde_attrs.skip_serializing() || (field_default.is_none() && !container_has_default) {
769        return None;
770    }
771
772    let ty = field.ty;
773    let default_expr = match field_default {
774        SerdeDefault::None => {
775            let member = &field.member;
776            quote!(#STRUCT_DEFAULT.#member)
777        }
778        SerdeDefault::Default => quote!(<#ty>::default()),
779        SerdeDefault::Path(path) => quote!(#path()),
780    };
781
782    let default_expr = if let Some(skip_if) = field.serde_attrs.skip_serializing_if() {
783        quote! {
784            {
785                let default = #default_expr;
786                if #skip_if(&default) {
787                    None
788                } else {
789                    Some(default)
790                }
791            }
792        }
793    } else {
794        quote!(Some(#default_expr))
795    };
796
797    Some(if let Some(ser_with) = field.serde_attrs.serialize_with() {
798        quote! {
799            {
800                struct _SchemarsDefaultSerialize<T>(T);
801
802                impl serde::Serialize for _SchemarsDefaultSerialize<#ty>
803                {
804                    fn serialize<S>(&self, serializer: S) -> ::core::result::Result<S::Ok, S::Error>
805                    where
806                        S: serde::Serializer
807                    {
808                        #ser_with(&self.0, serializer)
809                    }
810                }
811
812                #default_expr.map(|d| _SchemarsDefaultSerialize(d))
813            }
814        }
815    } else {
816        default_expr
817    })
818}