From b54849598734b4d356c8ce9517f15f13bddad5bf Mon Sep 17 00:00:00 2001 From: Wyatt Herkamp Date: Mon, 12 Aug 2024 08:26:09 -0400 Subject: [PATCH 1/2] Adjacent Tagged Enums should use if and else statements. --- schemars/tests/enum.rs | 30 +- .../enum-adjacent-tagged-no-extras.json | 187 +++++++++++ .../tests/expected/enum-adjacent-tagged.json | 290 +++++++++--------- schemars_derive/src/schema_exprs.rs | 150 +++++---- 4 files changed, 454 insertions(+), 203 deletions(-) create mode 100644 schemars/tests/expected/enum-adjacent-tagged-no-extras.json diff --git a/schemars/tests/enum.rs b/schemars/tests/enum.rs index 4c57f34a..7795cd29 100644 --- a/schemars/tests/enum.rs +++ b/schemars/tests/enum.rs @@ -1,7 +1,7 @@ mod util; use std::collections::BTreeMap; -use schemars::JsonSchema; +use schemars::{schema_for, JsonSchema}; use util::*; // Ensure that schemars_derive uses the full path to std::string::String @@ -88,7 +88,9 @@ fn enum_untagged() -> TestResult { #[derive(JsonSchema)] #[schemars(tag = "t", content = "c")] enum Adjacent { + /// Has a description UnitOne, + #[schemars(title = "String Map")] StringMap(BTreeMap<&'static str, &'static str>), UnitStructNewType(UnitStruct), StructNewType(Struct), @@ -98,15 +100,39 @@ enum Adjacent { }, Tuple(i32, bool), UnitTwo, - #[schemars(with = "i32")] WithInt, } #[test] fn enum_adjacent_tagged() -> TestResult { + println!( + "{}", + serde_json::to_string_pretty(&schema_for!(Adjacent)).unwrap() + ); test_default_generated_schema::("enum-adjacent-tagged") } +#[allow(dead_code)] +#[derive(JsonSchema)] +#[schemars(tag = "t", content = "c")] +enum AdjacentNoExtras { + UnitOne, + StringMap(BTreeMap<&'static str, &'static str>), + UnitStructNewType(UnitStruct), + StructNewType(Struct), + Struct { + foo: i32, + bar: bool, + }, + Tuple(i32, bool), + UnitTwo, + #[schemars(with = "i32")] + WithInt, +} +#[test] +fn enum_adjacent_tagged_no_extras() -> TestResult { + test_default_generated_schema::("enum-adjacent-tagged-no-extras") +} #[allow(dead_code)] #[derive(JsonSchema)] #[schemars(tag = "typeProperty")] diff --git a/schemars/tests/expected/enum-adjacent-tagged-no-extras.json b/schemars/tests/expected/enum-adjacent-tagged-no-extras.json new file mode 100644 index 00000000..4aad1eec --- /dev/null +++ b/schemars/tests/expected/enum-adjacent-tagged-no-extras.json @@ -0,0 +1,187 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "AdjacentNoExtras", + "properties": { + "t": { + "type": "string", + "enum": [ + "UnitOne", + "StringMap", + "UnitStructNewType", + "StructNewType", + "Struct", + "Tuple", + "UnitTwo", + "WithInt" + ] + } + }, + "allOf": [ + { + "if": { + "properties": { + "t": { + "const": "StringMap" + } + } + }, + "then": { + "properties": { + "c": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "required": [ + "c" + ] + } + }, + { + "if": { + "properties": { + "t": { + "const": "UnitStructNewType" + } + } + }, + "then": { + "properties": { + "c": { + "$ref": "#/$defs/UnitStruct" + } + }, + "required": [ + "c" + ] + } + }, + { + "if": { + "properties": { + "t": { + "const": "StructNewType" + } + } + }, + "then": { + "properties": { + "c": { + "$ref": "#/$defs/Struct" + } + }, + "required": [ + "c" + ] + } + }, + { + "if": { + "properties": { + "t": { + "const": "Struct" + } + } + }, + "then": { + "properties": { + "c": { + "type": "object", + "properties": { + "bar": { + "type": "boolean" + }, + "foo": { + "type": "integer", + "format": "int32" + } + }, + "required": [ + "foo", + "bar" + ] + } + }, + "required": [ + "c" + ] + } + }, + { + "if": { + "properties": { + "t": { + "const": "Tuple" + } + } + }, + "then": { + "properties": { + "c": { + "type": "array", + "maxItems": 2, + "minItems": 2, + "prefixItems": [ + { + "type": "integer", + "format": "int32" + }, + { + "type": "boolean" + } + ] + } + }, + "required": [ + "c" + ] + } + }, + { + "if": { + "properties": { + "t": { + "const": "WithInt" + } + } + }, + "then": { + "properties": { + "c": { + "type": "integer", + "format": "int32" + } + }, + "required": [ + "c" + ] + } + } + ], + "required": [ + "t" + ], + "$defs": { + "Struct": { + "type": "object", + "properties": { + "bar": { + "type": "boolean" + }, + "foo": { + "type": "integer", + "format": "int32" + } + }, + "required": [ + "foo", + "bar" + ] + }, + "UnitStruct": { + "type": "null" + } + } +} \ No newline at end of file diff --git a/schemars/tests/expected/enum-adjacent-tagged.json b/schemars/tests/expected/enum-adjacent-tagged.json index c631ae54..5d9952a5 100644 --- a/schemars/tests/expected/enum-adjacent-tagged.json +++ b/schemars/tests/expected/enum-adjacent-tagged.json @@ -1,191 +1,193 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "Adjacent", - "oneOf": [ - { - "type": "object", - "properties": { - "t": { + "properties": { + "t": { + "oneOf": [ + { + "description": "Has a description", "type": "string", - "enum": [ - "UnitOne" - ] - } - }, - "required": [ - "t" - ] - }, - { - "type": "object", - "properties": { - "t": { + "const": "UnitOne" + }, + { + "title": "String Map", "type": "string", - "enum": [ - "StringMap" - ] + "const": "StringMap" }, - "c": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "required": [ - "t", - "c" - ] - }, - { - "type": "object", - "properties": { - "t": { + { + "type": "string", + "const": "UnitStructNewType" + }, + { + "type": "string", + "const": "StructNewType" + }, + { "type": "string", - "enum": [ - "UnitStructNewType" - ] + "const": "Struct" }, - "c": { - "$ref": "#/$defs/UnitStruct" + { + "type": "string", + "const": "Tuple" + }, + { + "type": "string", + "const": "UnitTwo" + }, + { + "type": "string", + "const": "WithInt" } - }, - "required": [ - "t", - "c" ] - }, + } + }, + "allOf": [ { - "type": "object", - "properties": { - "t": { - "type": "string", - "enum": [ - "StructNewType" - ] - }, - "c": { - "$ref": "#/$defs/Struct" + "title": "String Map", + "if": { + "properties": { + "t": { + "const": "StringMap" + } } }, - "required": [ - "t", - "c" - ] + "then": { + "properties": { + "c": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "required": [ + "c" + ] + } }, { - "type": "object", - "properties": { - "t": { - "type": "string", - "enum": [ - "Struct" - ] - }, - "c": { - "type": "object", - "properties": { - "foo": { - "type": "integer", - "format": "int32" - }, - "bar": { - "type": "boolean" - } - }, - "required": [ - "foo", - "bar" - ] + "if": { + "properties": { + "t": { + "const": "UnitStructNewType" + } } }, - "required": [ - "t", - "c" - ] + "then": { + "properties": { + "c": { + "$ref": "#/$defs/UnitStruct" + } + }, + "required": [ + "c" + ] + } }, { - "type": "object", - "properties": { - "t": { - "type": "string", - "enum": [ - "Tuple" - ] - }, - "c": { - "type": "array", - "prefixItems": [ - { - "type": "integer", - "format": "int32" - }, - { - "type": "boolean" - } - ], - "minItems": 2, - "maxItems": 2 + "if": { + "properties": { + "t": { + "const": "StructNewType" + } } }, - "required": [ - "t", - "c" - ] + "then": { + "properties": { + "c": { + "$ref": "#/$defs/Struct" + } + }, + "required": [ + "c" + ] + } }, { - "type": "object", - "properties": { - "t": { - "type": "string", - "enum": [ - "UnitTwo" - ] + "if": { + "properties": { + "t": { + "const": "Struct" + } } }, - "required": [ - "t" - ] + "then": { + "properties": { + "c": { + "type": "object", + "properties": { + "bar": { + "type": "boolean" + }, + "foo": { + "type": "integer", + "format": "int32" + } + }, + "required": [ + "foo", + "bar" + ] + } + }, + "required": [ + "c" + ] + } }, { - "type": "object", - "properties": { - "t": { - "type": "string", - "enum": [ - "WithInt" - ] - }, - "c": { - "type": "integer", - "format": "int32" + "if": { + "properties": { + "t": { + "const": "Tuple" + } } }, - "required": [ - "t", - "c" - ] + "then": { + "properties": { + "c": { + "type": "array", + "maxItems": 2, + "minItems": 2, + "prefixItems": [ + { + "type": "integer", + "format": "int32" + }, + { + "type": "boolean" + } + ] + } + }, + "required": [ + "c" + ] + } } ], + "required": [ + "t" + ], "$defs": { - "UnitStruct": { - "type": "null" - }, "Struct": { "type": "object", "properties": { + "bar": { + "type": "boolean" + }, "foo": { "type": "integer", "format": "int32" - }, - "bar": { - "type": "boolean" } }, "required": [ "foo", "bar" ] + }, + "UnitStruct": { + "type": "null" } } } \ No newline at end of file diff --git a/schemars_derive/src/schema_exprs.rs b/schemars_derive/src/schema_exprs.rs index 1f5df99c..273077a7 100644 --- a/schemars_derive/src/schema_exprs.rs +++ b/schemars_derive/src/schema_exprs.rs @@ -266,78 +266,114 @@ fn expr_for_untagged_enum<'a>( // that checking the exclusivity of each subschema we simply us `any_of`. variant_subschemas(false, schemas) } - fn expr_for_adjacent_tagged_enum<'a>( variants: impl Iterator>, tag_name: &str, content_name: &str, deny_unknown_fields: bool, ) -> TokenStream { - let mut unique_names = HashSet::new(); - let mut count = 0; - let schemas = variants - .map(|variant| { - unique_names.insert(variant.name()); - count += 1; - - let content_schema = if variant.is_unit() && variant.attrs.with.is_none() { - None - } else { - Some(expr_for_untagged_enum_variant(variant, deny_unknown_fields)) - }; - - let (add_content_to_props, add_content_to_required) = content_schema - .map(|content_schema| { - ( - quote!(#content_name: (#content_schema),), - quote!(#content_name,), - ) - }) - .unwrap_or_default(); - - let name = variant.name(); - let tag_schema = quote! { - schemars::json_schema!({ - "type": "string", - "enum": [#name], - }) - }; + let mut has_tag_name_extras = false; + let mut enum_values = Vec::with_capacity(variants.size_hint().0); + let mut if_tags = Vec::with_capacity(variants.size_hint().0); + for variant in variants { + if variant.attrs.title.is_some() || variant.attrs.description.is_some() { + has_tag_name_extras = true; + } - let set_additional_properties = if deny_unknown_fields { - quote! { - "additionalProperties": false, - } - } else { - TokenStream::new() - }; + let content_schema = if variant.is_unit() && variant.attrs.with.is_none() { + None + } else { + Some(expr_for_untagged_enum_variant(variant, deny_unknown_fields)) + }; + enum_values.push((variant.name(), &variant.attrs)); + if matches!(variant.style, Style::Unit) && content_schema.is_none() { + continue; + } + let (add_content_to_props, add_content_to_required) = content_schema + .map(|content_schema| { + ( + quote!(#content_name: (#content_schema),), + quote!(#content_name,), + ) + }) + .unwrap_or_default(); - let mut outer_schema = quote! { - schemars::json_schema!({ - "type": "object", + let name = variant.name(); + let mut if_tag = quote! { + schemars::json_schema!({ + "if": { + "properties": { + #tag_name: { + "const": #name + } + }, + }, + "then": { "properties": { - #tag_name: (#tag_schema), #add_content_to_props }, "required": [ - #tag_name, #add_content_to_required ], - // As we're creating a "wrapper" object, we can honor the - // disposition of deny_unknown_fields. - #set_additional_properties - }) - }; - - variant - .attrs - .as_metadata() - .apply_to_schema(&mut outer_schema); - - outer_schema - }) - .collect(); + }, + }) + }; - variant_subschemas(unique_names.len() == count, schemas) + variant.attrs.as_metadata().apply_to_schema(&mut if_tag); + if_tags.push(if_tag); + } + let set_additional_properties = if deny_unknown_fields { + quote! { + map.insert("additionalProperties", false.into()); + } + } else { + TokenStream::new() + }; + let tag_property = if has_tag_name_extras { + let mut one_of = Vec::new(); + for (name, attrs) in enum_values { + let mut schema = quote! { + schemars::_private::new_unit_enum_variant(#name) + }; + attrs.as_metadata().apply_to_schema(&mut schema); + one_of.push(schema); + } + quote! { + let mut tag_properties = schemars::_serde_json::Map::new(); + tag_properties.insert("oneOf".to_owned(), schemars::_serde_json::Value::Array({ + let mut enum_values = Vec::new(); + #(enum_values.push(#one_of.to_value());)* + enum_values + })); + } + } else { + let names = enum_values.iter().map(|(name, _)| quote!(#name)); + quote! { + let mut tag_properties = schemars::_serde_json::Map::new(); + tag_properties.insert("type".to_owned(), "string".into()); + tag_properties.insert("enum".to_owned(), schemars::_serde_json::Value::Array({ + let mut enum_values = Vec::new(); + #(enum_values.push(#names.into());)* + enum_values + })); + } + }; + quote! { + let mut map = schemars::_serde_json::Map::new(); + #tag_property + map.insert("properties".to_owned(), schemars::_serde_json::json!({ + #tag_name: tag_properties + })); + map.insert("required".to_owned(), schemars::_serde_json::json!([#tag_name])); + #set_additional_properties + + map.insert("allOf".to_owned(), schemars::_serde_json::Value::Array({ + let mut enum_values = Vec::new(); + #(enum_values.push(#if_tags.to_value());)* + enum_values + })); + schemars::Schema::from(map) + } } /// Callers must determine if all subschemas are mutually exclusive. This can From cf35ab292678049ba6d9e042573e929a36d7a934 Mon Sep 17 00:00:00 2001 From: Wyatt Herkamp Date: Mon, 12 Aug 2024 08:50:33 -0400 Subject: [PATCH 2/2] Test fixes --- .../expected/enum-adjacent-tagged-duf.json | 295 +++++++++--------- .../tests/expected/extend_enum_adjacent.json | 168 +++++----- .../schema_with-enum-adjacent-tagged.json | 170 +++++----- schemars_derive/src/schema_exprs.rs | 10 +- 4 files changed, 327 insertions(+), 316 deletions(-) diff --git a/schemars/tests/expected/enum-adjacent-tagged-duf.json b/schemars/tests/expected/enum-adjacent-tagged-duf.json index dfb3bb8b..36e6c59c 100644 --- a/schemars/tests/expected/enum-adjacent-tagged-duf.json +++ b/schemars/tests/expected/enum-adjacent-tagged-duf.json @@ -1,200 +1,189 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "Adjacent", - "oneOf": [ + "properties": { + "t": { + "type": "string", + "enum": [ + "UnitOne", + "StringMap", + "UnitStructNewType", + "StructNewType", + "Struct", + "Tuple", + "UnitTwo", + "WithInt" + ] + } + }, + "additionalProperties": false, + "allOf": [ { - "type": "object", - "properties": { - "t": { - "type": "string", - "enum": [ - "UnitOne" - ] + "if": { + "properties": { + "t": { + "const": "StringMap" + } } }, - "required": [ - "t" - ], - "additionalProperties": false + "then": { + "properties": { + "c": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + }, + "required": [ + "c" + ] + } }, { - "type": "object", - "properties": { - "t": { - "type": "string", - "enum": [ - "StringMap" - ] - }, - "c": { - "type": "object", - "additionalProperties": { - "type": "string" + "if": { + "properties": { + "t": { + "const": "UnitStructNewType" } } }, - "required": [ - "t", - "c" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "t": { - "type": "string", - "enum": [ - "UnitStructNewType" - ] + "then": { + "properties": { + "c": { + "$ref": "#/$defs/UnitStruct" + } }, - "c": { - "$ref": "#/$defs/UnitStruct" - } - }, - "required": [ - "t", - "c" - ], - "additionalProperties": false + "required": [ + "c" + ] + } }, { - "type": "object", - "properties": { - "t": { - "type": "string", - "enum": [ - "StructNewType" - ] - }, - "c": { - "$ref": "#/$defs/Struct" + "if": { + "properties": { + "t": { + "const": "StructNewType" + } } }, - "required": [ - "t", - "c" - ], - "additionalProperties": false - }, - { - "type": "object", - "properties": { - "t": { - "type": "string", - "enum": [ - "Struct" - ] + "then": { + "properties": { + "c": { + "$ref": "#/$defs/Struct" + } }, - "c": { - "type": "object", - "properties": { - "foo": { - "type": "integer", - "format": "int32" - }, - "bar": { - "type": "boolean" - } - }, - "additionalProperties": false, - "required": [ - "foo", - "bar" - ] - } - }, - "required": [ - "t", - "c" - ], - "additionalProperties": false + "required": [ + "c" + ] + } }, { - "type": "object", - "properties": { - "t": { - "type": "string", - "enum": [ - "Tuple" - ] - }, - "c": { - "type": "array", - "prefixItems": [ - { - "type": "integer", - "format": "int32" - }, - { - "type": "boolean" - } - ], - "minItems": 2, - "maxItems": 2 + "if": { + "properties": { + "t": { + "const": "Struct" + } } }, - "required": [ - "t", - "c" - ], - "additionalProperties": false + "then": { + "properties": { + "c": { + "type": "object", + "properties": { + "bar": { + "type": "boolean" + }, + "foo": { + "type": "integer", + "format": "int32" + } + }, + "additionalProperties": false, + "required": [ + "foo", + "bar" + ] + } + }, + "required": [ + "c" + ] + } }, { - "type": "object", - "properties": { - "t": { - "type": "string", - "enum": [ - "UnitTwo" - ] + "if": { + "properties": { + "t": { + "const": "Tuple" + } } }, - "required": [ - "t" - ], - "additionalProperties": false + "then": { + "properties": { + "c": { + "type": "array", + "maxItems": 2, + "minItems": 2, + "prefixItems": [ + { + "type": "integer", + "format": "int32" + }, + { + "type": "boolean" + } + ] + } + }, + "required": [ + "c" + ] + } }, { - "type": "object", - "properties": { - "t": { - "type": "string", - "enum": [ - "WithInt" - ] - }, - "c": { - "type": "integer", - "format": "int32" + "if": { + "properties": { + "t": { + "const": "WithInt" + } } }, - "required": [ - "t", - "c" - ], - "additionalProperties": false + "then": { + "properties": { + "c": { + "type": "integer", + "format": "int32" + } + }, + "required": [ + "c" + ] + } } ], + "required": [ + "t" + ], "$defs": { - "UnitStruct": { - "type": "null" - }, "Struct": { "type": "object", "properties": { + "bar": { + "type": "boolean" + }, "foo": { "type": "integer", "format": "int32" - }, - "bar": { - "type": "boolean" } }, "required": [ "foo", "bar" ] + }, + "UnitStruct": { + "type": "null" } } } \ No newline at end of file diff --git a/schemars/tests/expected/extend_enum_adjacent.json b/schemars/tests/expected/extend_enum_adjacent.json index 6241e079..487cdc72 100644 --- a/schemars/tests/expected/extend_enum_adjacent.json +++ b/schemars/tests/expected/extend_enum_adjacent.json @@ -1,101 +1,103 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "Adjacent", - "oneOf": [ + "properties": { + "t": { + "type": "string", + "enum": [ + "Unit", + "NewType", + "Tuple", + "Struct" + ] + } + }, + "allOf": [ { - "type": "object", - "properties": { - "t": { - "type": "string", - "enum": [ - "Unit" - ] + "foo": "bar", + "if": { + "properties": { + "t": { + "const": "NewType" + } } }, - "required": [ - "t" - ], - "foo": "bar" - }, - { - "type": "object", - "properties": { - "t": { - "type": "string", - "enum": [ - "NewType" - ] + "then": { + "properties": { + "c": true }, - "c": true - }, - "required": [ - "t", - "c" - ], - "foo": "bar" + "required": [ + "c" + ] + } }, { - "type": "object", - "properties": { - "t": { - "type": "string", - "enum": [ - "Tuple" - ] - }, - "c": { - "type": "array", - "prefixItems": [ - { - "type": "integer", - "format": "int32" - }, - { - "type": "boolean" - } - ], - "minItems": 2, - "maxItems": 2 + "foo": "bar", + "if": { + "properties": { + "t": { + "const": "Tuple" + } } }, - "required": [ - "t", - "c" - ], - "foo": "bar" + "then": { + "properties": { + "c": { + "type": "array", + "maxItems": 2, + "minItems": 2, + "prefixItems": [ + { + "type": "integer", + "format": "int32" + }, + { + "type": "boolean" + } + ] + } + }, + "required": [ + "c" + ] + } }, { - "type": "object", - "properties": { - "t": { - "type": "string", - "enum": [ - "Struct" - ] - }, - "c": { - "type": "object", - "properties": { - "i": { - "type": "integer", - "format": "int32" - }, - "b": { - "type": "boolean" - } - }, - "required": [ - "i", - "b" - ] + "foo": "bar", + "if": { + "properties": { + "t": { + "const": "Struct" + } } }, - "required": [ - "t", - "c" - ], - "foo": "bar" + "then": { + "properties": { + "c": { + "type": "object", + "properties": { + "b": { + "type": "boolean" + }, + "i": { + "type": "integer", + "format": "int32" + } + }, + "required": [ + "i", + "b" + ] + } + }, + "required": [ + "c" + ] + } } ], - "foo": "bar" + "foo": "bar", + "required": [ + "t" + ] } \ No newline at end of file diff --git a/schemars/tests/expected/schema_with-enum-adjacent-tagged.json b/schemars/tests/expected/schema_with-enum-adjacent-tagged.json index 3e7173d5..89761c6e 100644 --- a/schemars/tests/expected/schema_with-enum-adjacent-tagged.json +++ b/schemars/tests/expected/schema_with-enum-adjacent-tagged.json @@ -1,97 +1,115 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "Adjacent", - "oneOf": [ + "properties": { + "t": { + "type": "string", + "enum": [ + "Struct", + "NewType", + "Tuple", + "Unit" + ] + } + }, + "allOf": [ { - "type": "object", - "properties": { - "t": { - "type": "string", - "enum": [ - "Struct" - ] - }, - "c": { - "type": "object", - "properties": { - "foo": { - "type": "boolean" - } - }, - "required": [ - "foo" - ] + "if": { + "properties": { + "t": { + "const": "Struct" + } } }, - "required": [ - "t", - "c" - ] + "then": { + "properties": { + "c": { + "type": "object", + "properties": { + "foo": { + "type": "boolean" + } + }, + "required": [ + "foo" + ] + } + }, + "required": [ + "c" + ] + } }, { - "type": "object", - "properties": { - "t": { - "type": "string", - "enum": [ - "NewType" - ] - }, - "c": { - "type": "boolean" + "if": { + "properties": { + "t": { + "const": "NewType" + } } }, - "required": [ - "t", - "c" - ] + "then": { + "properties": { + "c": { + "type": "boolean" + } + }, + "required": [ + "c" + ] + } }, { - "type": "object", - "properties": { - "t": { - "type": "string", - "enum": [ - "Tuple" - ] - }, - "c": { - "type": "array", - "prefixItems": [ - { - "type": "boolean" - }, - { - "type": "integer", - "format": "int32" - } - ], - "minItems": 2, - "maxItems": 2 + "if": { + "properties": { + "t": { + "const": "Tuple" + } } }, - "required": [ - "t", - "c" - ] + "then": { + "properties": { + "c": { + "type": "array", + "maxItems": 2, + "minItems": 2, + "prefixItems": [ + { + "type": "boolean" + }, + { + "type": "integer", + "format": "int32" + } + ] + } + }, + "required": [ + "c" + ] + } }, { - "type": "object", - "properties": { - "t": { - "type": "string", - "enum": [ - "Unit" - ] - }, - "c": { - "type": "boolean" + "if": { + "properties": { + "t": { + "const": "Unit" + } } }, - "required": [ - "t", - "c" - ] + "then": { + "properties": { + "c": { + "type": "boolean" + } + }, + "required": [ + "c" + ] + } } + ], + "required": [ + "t" ] } \ No newline at end of file diff --git a/schemars_derive/src/schema_exprs.rs b/schemars_derive/src/schema_exprs.rs index 273077a7..5997f0e6 100644 --- a/schemars_derive/src/schema_exprs.rs +++ b/schemars_derive/src/schema_exprs.rs @@ -324,7 +324,7 @@ fn expr_for_adjacent_tagged_enum<'a>( } let set_additional_properties = if deny_unknown_fields { quote! { - map.insert("additionalProperties", false.into()); + map.insert("additionalProperties".to_owned(), false.into()); } } else { TokenStream::new() @@ -358,8 +358,9 @@ fn expr_for_adjacent_tagged_enum<'a>( })); } }; - quote! { - let mut map = schemars::_serde_json::Map::new(); + quote! ( + { + let mut map = schemars::_serde_json::Map::new(); #tag_property map.insert("properties".to_owned(), schemars::_serde_json::json!({ #tag_name: tag_properties @@ -373,7 +374,8 @@ fn expr_for_adjacent_tagged_enum<'a>( enum_values })); schemars::Schema::from(map) - } + } + ) } /// Callers must determine if all subschemas are mutually exclusive. This can