Skip to content

Commit

Permalink
Include Option fields in "required" for serialize schemas
Browse files Browse the repository at this point in the history
  • Loading branch information
GREsau committed Sep 2, 2024
1 parent 5695623 commit 72d6e9b
Show file tree
Hide file tree
Showing 5 changed files with 40 additions and 36 deletions.
45 changes: 17 additions & 28 deletions schemars/src/_private/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,41 +134,30 @@ pub fn apply_internal_enum_variant_tag(
}
}

pub fn insert_object_property<T: ?Sized + JsonSchema>(
pub fn insert_object_property(
schema: &mut Schema,
key: &str,
may_be_omitted: bool,
required_attr: bool,
is_optional: bool,
sub_schema: Schema,
) {
fn insert_object_property_impl(
schema: &mut Schema,
key: &str,
required: bool,
sub_schema: Schema,
) {
let obj = schema.ensure_object();
if let Some(properties) = obj
.entry("properties")
.or_insert(Value::Object(Map::new()))
.as_object_mut()
{
properties.insert(key.to_owned(), sub_schema.into());
}
let obj = schema.ensure_object();
if let Some(properties) = obj
.entry("properties")
.or_insert(Value::Object(Map::new()))
.as_object_mut()
{
properties.insert(key.to_owned(), sub_schema.into());
}

if required {
if let Some(req) = obj
.entry("required")
.or_insert(Value::Array(Vec::new()))
.as_array_mut()
{
req.push(key.into());
}
if !is_optional {
if let Some(req) = obj
.entry("required")
.or_insert(Value::Array(Vec::new()))
.as_array_mut()
{
req.push(key.into());
}
}

let required = !may_be_omitted && (required_attr || !T::_schemars_private_is_option());
insert_object_property_impl(schema, key, required, sub_schema);
}

pub fn insert_metadata_property(schema: &mut Schema, key: &str, value: impl Into<Value>) {
Expand Down
1 change: 1 addition & 0 deletions schemars/tests/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ struct MyStruct {
skip_serializing_if: bool,
#[schemars(rename(serialize = "ser_renamed", deserialize = "de_renamed"))]
renamed: bool,
option: Option<bool>,
}

#[test]
Expand Down
6 changes: 6 additions & 0 deletions schemars/tests/expected/contract_deserialize.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@
},
"de_renamed": {
"type": "boolean"
},
"option": {
"type": [
"boolean",
"null"
]
}
},
"required": [
Expand Down
9 changes: 8 additions & 1 deletion schemars/tests/expected/contract_serialize.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,18 @@
},
"ser_renamed": {
"type": "boolean"
},
"OPTION": {
"type": [
"boolean",
"null"
]
}
},
"required": [
"READ-ONLY",
"DEFAULT",
"ser_renamed"
"ser_renamed",
"OPTION"
]
}
15 changes: 8 additions & 7 deletions schemars_derive/src/schema_exprs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -509,16 +509,17 @@ fn expr_for_struct(

let has_default = set_container_default.is_some() || !field.serde_attrs.default().is_none();
let has_skip_serialize_if = field.serde_attrs.skip_serializing_if().is_some();
let may_be_omitted = if has_default == has_skip_serialize_if {
has_default.into_token_stream()
let required_attr = field.attrs.validation.required;

let is_optional = if has_skip_serialize_if && has_default {
quote!(true)
} else {
quote!(if #GENERATOR.contract().is_serialize() {
#has_skip_serialize_if
} else {
#has_default
#has_default || (!#required_attr && <#ty as schemars::JsonSchema>::_schemars_private_is_option())
})
};
let required_attr = field.attrs.validation.required;

let mut schema_expr = SchemaExpr::from(if field.attrs.validation.required {
quote_spanned! {ty.span()=>
Expand All @@ -538,11 +539,11 @@ fn expr_for_struct(
})
}

// embed `#type_def` outside of `#schema_expr`, because it's used as the type param
// (i.e. `#type_def` is the definition of `#ty`)
// embed `#type_def` outside of `#schema_expr`, because it's used as a type param
// in `#is_optional` (`#type_def` is the definition of `#ty`)
field.with_contract_check(quote!({
#type_def
schemars::_private::insert_object_property::<#ty>(&mut #SCHEMA, #name, #may_be_omitted, #required_attr, #schema_expr);
schemars::_private::insert_object_property(&mut #SCHEMA, #name, #is_optional, #schema_expr);
}))
}
})
Expand Down

0 comments on commit 72d6e9b

Please sign in to comment.