diff --git a/schemars/tests/integration/snapshots/schemars/tests/integration/enums.rs~internally_tagged_enum.json b/schemars/tests/integration/snapshots/schemars/tests/integration/enums.rs~internally_tagged_enum.json index e91dcb6a..639802f3 100644 --- a/schemars/tests/integration/snapshots/schemars/tests/integration/enums.rs~internally_tagged_enum.json +++ b/schemars/tests/integration/snapshots/schemars/tests/integration/enums.rs~internally_tagged_enum.json @@ -22,9 +22,14 @@ "const": "StringMap" } }, - "additionalProperties": { - "type": "string" - }, + "allOf": [ + { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + ], "required": [ "tag" ] @@ -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" ] }, { @@ -95,5 +96,23 @@ "tag" ] } - ] + ], + "$defs": { + "Struct": { + "type": "object", + "properties": { + "foo": { + "type": "integer", + "format": "int32" + }, + "bar": { + "type": "boolean" + } + }, + "required": [ + "foo", + "bar" + ] + } + } } \ No newline at end of file diff --git a/schemars/tests/integration/snapshots/schemars/tests/integration/enums_deny_unknown_fields.rs~internally_tagged_enum.json b/schemars/tests/integration/snapshots/schemars/tests/integration/enums_deny_unknown_fields.rs~internally_tagged_enum.json index 2e5b8c88..cc1620a1 100644 --- a/schemars/tests/integration/snapshots/schemars/tests/integration/enums_deny_unknown_fields.rs~internally_tagged_enum.json +++ b/schemars/tests/integration/snapshots/schemars/tests/integration/enums_deny_unknown_fields.rs~internally_tagged_enum.json @@ -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" ] @@ -42,16 +64,19 @@ }, "tag": { "type": "string", - "const": "StructNewType" + "const": "Struct" } }, + "additionalProperties": false, "required": [ "tag", "foo", "bar" ] - }, - { + } + ], + "$defs": { + "Struct": { "type": "object", "properties": { "foo": { @@ -60,18 +85,12 @@ }, "bar": { "type": "boolean" - }, - "tag": { - "type": "string", - "const": "Struct" } }, - "additionalProperties": false, "required": [ - "tag", "foo", "bar" ] } - ] + } } \ No newline at end of file diff --git a/schemars/tests/integration/snapshots/schemars/tests/integration/extend.rs~extend_internally_tagged_enum.json b/schemars/tests/integration/snapshots/schemars/tests/integration/extend.rs~extend_internally_tagged_enum.json index c0f6354e..a6a17a06 100644 --- a/schemars/tests/integration/snapshots/schemars/tests/integration/extend.rs~extend_internally_tagged_enum.json +++ b/schemars/tests/integration/snapshots/schemars/tests/integration/extend.rs~extend_internally_tagged_enum.json @@ -23,6 +23,9 @@ "const": "NewType" } }, + "allOf": [ + true + ], "required": [ "t" ], diff --git a/schemars_derive/src/schema_exprs.rs b/schemars_derive/src/schema_exprs.rs index 0b30e2e6..254076be 100644 --- a/schemars_derive/src/schema_exprs.rs +++ b/schemars_derive/src/schema_exprs.rs @@ -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) { match &field.attrs.with { None => (field.ty.to_owned(), None), @@ -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), }