Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve handling of internally tagged enum with newtypes #347

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,14 @@
"const": "StringMap"
}
},
"additionalProperties": {
"type": "string"
},
"allOf": [
{
"type": "object",
"additionalProperties": {
"type": "string"
}
}
],
"required": [
"tag"
]
Expand All @@ -44,22 +49,18 @@
{
"type": "object",
"properties": {
"foo": {
"type": "integer",
"format": "int32"
},
"bar": {
"type": "boolean"
},
"tag": {
"type": "string",
"const": "StructNewType"
}
},
"allOf": [
{
"$ref": "#/$defs/Struct"
}
],
"required": [
"tag",
"foo",
"bar"
"tag"
]
},
{
Expand Down Expand Up @@ -95,5 +96,23 @@
"tag"
]
}
]
],
"$defs": {
"Struct": {
"type": "object",
"properties": {
"foo": {
"type": "integer",
"format": "int32"
},
"bar": {
"type": "boolean"
}
},
"required": [
"foo",
"bar"
]
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,31 @@
"const": "StringMap"
}
},
"additionalProperties": {
"type": "string"
"allOf": [
{
"type": "object",
"additionalProperties": {
"type": "string"
}
}
],
"required": [
"tag"
]
},
{
"type": "object",
"properties": {
"tag": {
"type": "string",
"const": "StructNewType"
}
},
"allOf": [
{
"$ref": "#/$defs/Struct"
}
],
"required": [
"tag"
]
Expand All @@ -42,16 +64,19 @@
},
"tag": {
"type": "string",
"const": "StructNewType"
"const": "Struct"
}
},
"additionalProperties": false,
"required": [
"tag",
"foo",
"bar"
]
},
{
}
],
"$defs": {
"Struct": {
"type": "object",
"properties": {
"foo": {
Expand All @@ -60,18 +85,12 @@
},
"bar": {
"type": "boolean"
},
"tag": {
"type": "string",
"const": "Struct"
}
},
"additionalProperties": false,
"required": [
"tag",
"foo",
"bar"
]
}
]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
"const": "NewType"
}
},
"allOf": [
true
],
"required": [
"t"
],
Expand Down
46 changes: 45 additions & 1 deletion schemars_derive/src/schema_exprs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,50 @@ fn expr_for_field(field: &Field, allow_ref: bool) -> SchemaExpr {
schema_expr
}

fn expr_for_internally_tagged_newtype_field(field: &Field) -> SchemaExpr {
let (ty, type_def) = type_for_field_schema(field);
let span = field.original.span();

let mut schema_expr = SchemaExpr::from(if field.attrs.validation.required {
quote_spanned! {span=>
<#ty as schemars::JsonSchema>::_schemars_private_non_optional_json_schema(#GENERATOR)
}
} else {
let keyword = "allOf";
quote_spanned!(span =>
{
if (#GENERATOR.settings().inline_subschemas) {
<#ty as schemars::JsonSchema>::json_schema(#GENERATOR)
} else {
let subschema = <#ty as schemars::JsonSchema>::json_schema(#GENERATOR);
let subschema_type = subschema.get("type");
let is_null_type = subschema_type.is_some() && subschema_type.unwrap().as_str().unwrap() == "null";
if !is_null_type {
let mut map = schemars::_private::serde_json::Map::new();
map.insert(
#keyword.into(),
schemars::_private::serde_json::Value::Array({
let mut enum_values = schemars::_private::alloc::vec::Vec::new();
enum_values.push(#GENERATOR.subschema_for::<#ty>().to_value());
enum_values
}),

);
schemars::Schema::from(map)
} else {
<#ty as schemars::JsonSchema>::json_schema(#GENERATOR)
}
}
}
)
});

schema_expr.definitions.extend(type_def);
field.add_mutators(&mut schema_expr.mutators);

schema_expr
}

pub fn type_for_field_schema(field: &Field) -> (syn::Type, Option<TokenStream>) {
match &field.attrs.with {
None => (field.ty.to_owned(), None),
Expand Down Expand Up @@ -430,7 +474,7 @@ fn expr_for_internal_tagged_enum_variant(

match variant.style {
Style::Unit => expr_for_unit_struct(),
Style::Newtype => expr_for_field(&variant.fields[0], false),
Style::Newtype => expr_for_internally_tagged_newtype_field(&variant.fields[0]),
Style::Tuple => expr_for_tuple_struct(&variant.fields),
Style::Struct => expr_for_struct(&variant.fields, &SerdeDefault::None, deny_unknown_fields),
}
Expand Down