Use oneOf for enums when possible (#108)

This commit is contained in:
Adam Leventhal 2021-09-26 10:02:44 -07:00 committed by GitHub
parent dec8bcc3b7
commit 0a1200baac
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 302 additions and 276 deletions

View file

@ -2,7 +2,7 @@
"$schema": "http://json-schema.org/draft-07/schema#", "$schema": "http://json-schema.org/draft-07/schema#",
"title": "DeprecatedEnum", "title": "DeprecatedEnum",
"deprecated": true, "deprecated": true,
"anyOf": [ "oneOf": [
{ {
"type": "string", "type": "string",
"enum": [ "enum": [
@ -24,13 +24,13 @@
"foo" "foo"
], ],
"properties": { "properties": {
"foo": {
"type": "integer",
"format": "int32"
},
"deprecated_field": { "deprecated_field": {
"deprecated": true, "deprecated": true,
"type": "boolean" "type": "boolean"
},
"foo": {
"type": "integer",
"format": "int32"
} }
} }
} }

View file

@ -2,7 +2,7 @@
"$schema": "http://json-schema.org/draft-07/schema#", "$schema": "http://json-schema.org/draft-07/schema#",
"title": "This is the enum's title", "title": "This is the enum's title",
"description": "This is the enum's description.", "description": "This is the enum's description.",
"anyOf": [ "oneOf": [
{ {
"type": "string", "type": "string",
"enum": [ "enum": [

View file

@ -1,7 +1,7 @@
{ {
"$schema": "http://json-schema.org/draft-07/schema#", "$schema": "http://json-schema.org/draft-07/schema#",
"title": "Adjacent", "title": "Adjacent",
"anyOf": [ "oneOf": [
{ {
"type": "object", "type": "object",
"required": [ "required": [
@ -24,17 +24,17 @@
"t" "t"
], ],
"properties": { "properties": {
"t": {
"type": "string",
"enum": [
"StringMap"
]
},
"c": { "c": {
"type": "object", "type": "object",
"additionalProperties": { "additionalProperties": {
"type": "string" "type": "string"
} }
},
"t": {
"type": "string",
"enum": [
"StringMap"
]
} }
}, },
"additionalProperties": false "additionalProperties": false
@ -46,14 +46,14 @@
"t" "t"
], ],
"properties": { "properties": {
"c": {
"$ref": "#/definitions/UnitStruct"
},
"t": { "t": {
"type": "string", "type": "string",
"enum": [ "enum": [
"UnitStructNewType" "UnitStructNewType"
] ]
},
"c": {
"$ref": "#/definitions/UnitStruct"
} }
}, },
"additionalProperties": false "additionalProperties": false
@ -65,14 +65,14 @@
"t" "t"
], ],
"properties": { "properties": {
"c": {
"$ref": "#/definitions/Struct"
},
"t": { "t": {
"type": "string", "type": "string",
"enum": [ "enum": [
"StructNewType" "StructNewType"
] ]
},
"c": {
"$ref": "#/definitions/Struct"
} }
}, },
"additionalProperties": false "additionalProperties": false
@ -84,12 +84,6 @@
"t" "t"
], ],
"properties": { "properties": {
"t": {
"type": "string",
"enum": [
"Struct"
]
},
"c": { "c": {
"type": "object", "type": "object",
"required": [ "required": [
@ -97,15 +91,21 @@
"foo" "foo"
], ],
"properties": { "properties": {
"bar": {
"type": "boolean"
},
"foo": { "foo": {
"type": "integer", "type": "integer",
"format": "int32" "format": "int32"
},
"bar": {
"type": "boolean"
} }
}, },
"additionalProperties": false "additionalProperties": false
},
"t": {
"type": "string",
"enum": [
"Struct"
]
} }
}, },
"additionalProperties": false "additionalProperties": false
@ -117,12 +117,6 @@
"t" "t"
], ],
"properties": { "properties": {
"t": {
"type": "string",
"enum": [
"Tuple"
]
},
"c": { "c": {
"type": "array", "type": "array",
"items": [ "items": [
@ -136,6 +130,12 @@
], ],
"maxItems": 2, "maxItems": 2,
"minItems": 2 "minItems": 2
},
"t": {
"type": "string",
"enum": [
"Tuple"
]
} }
}, },
"additionalProperties": false "additionalProperties": false
@ -162,24 +162,21 @@
"t" "t"
], ],
"properties": { "properties": {
"c": {
"type": "integer",
"format": "int32"
},
"t": { "t": {
"type": "string", "type": "string",
"enum": [ "enum": [
"WithInt" "WithInt"
] ]
},
"c": {
"type": "integer",
"format": "int32"
} }
}, },
"additionalProperties": false "additionalProperties": false
} }
], ],
"definitions": { "definitions": {
"UnitStruct": {
"type": "null"
},
"Struct": { "Struct": {
"type": "object", "type": "object",
"required": [ "required": [
@ -187,14 +184,17 @@
"foo" "foo"
], ],
"properties": { "properties": {
"bar": {
"type": "boolean"
},
"foo": { "foo": {
"type": "integer", "type": "integer",
"format": "int32" "format": "int32"
},
"bar": {
"type": "boolean"
} }
} }
},
"UnitStruct": {
"type": "null"
} }
} }
} }

View file

@ -1,7 +1,7 @@
{ {
"$schema": "http://json-schema.org/draft-07/schema#", "$schema": "http://json-schema.org/draft-07/schema#",
"title": "Adjacent", "title": "Adjacent",
"anyOf": [ "oneOf": [
{ {
"type": "object", "type": "object",
"required": [ "required": [
@ -23,17 +23,17 @@
"t" "t"
], ],
"properties": { "properties": {
"t": {
"type": "string",
"enum": [
"StringMap"
]
},
"c": { "c": {
"type": "object", "type": "object",
"additionalProperties": { "additionalProperties": {
"type": "string" "type": "string"
} }
},
"t": {
"type": "string",
"enum": [
"StringMap"
]
} }
} }
}, },
@ -44,14 +44,14 @@
"t" "t"
], ],
"properties": { "properties": {
"c": {
"$ref": "#/definitions/UnitStruct"
},
"t": { "t": {
"type": "string", "type": "string",
"enum": [ "enum": [
"UnitStructNewType" "UnitStructNewType"
] ]
},
"c": {
"$ref": "#/definitions/UnitStruct"
} }
} }
}, },
@ -62,14 +62,14 @@
"t" "t"
], ],
"properties": { "properties": {
"c": {
"$ref": "#/definitions/Struct"
},
"t": { "t": {
"type": "string", "type": "string",
"enum": [ "enum": [
"StructNewType" "StructNewType"
] ]
},
"c": {
"$ref": "#/definitions/Struct"
} }
} }
}, },
@ -80,12 +80,6 @@
"t" "t"
], ],
"properties": { "properties": {
"t": {
"type": "string",
"enum": [
"Struct"
]
},
"c": { "c": {
"type": "object", "type": "object",
"required": [ "required": [
@ -93,14 +87,20 @@
"foo" "foo"
], ],
"properties": { "properties": {
"bar": {
"type": "boolean"
},
"foo": { "foo": {
"type": "integer", "type": "integer",
"format": "int32" "format": "int32"
},
"bar": {
"type": "boolean"
} }
} }
},
"t": {
"type": "string",
"enum": [
"Struct"
]
} }
} }
}, },
@ -111,12 +111,6 @@
"t" "t"
], ],
"properties": { "properties": {
"t": {
"type": "string",
"enum": [
"Tuple"
]
},
"c": { "c": {
"type": "array", "type": "array",
"items": [ "items": [
@ -130,6 +124,12 @@
], ],
"maxItems": 2, "maxItems": 2,
"minItems": 2 "minItems": 2
},
"t": {
"type": "string",
"enum": [
"Tuple"
]
} }
} }
}, },
@ -154,23 +154,20 @@
"t" "t"
], ],
"properties": { "properties": {
"c": {
"type": "integer",
"format": "int32"
},
"t": { "t": {
"type": "string", "type": "string",
"enum": [ "enum": [
"WithInt" "WithInt"
] ]
},
"c": {
"type": "integer",
"format": "int32"
} }
} }
} }
], ],
"definitions": { "definitions": {
"UnitStruct": {
"type": "null"
},
"Struct": { "Struct": {
"type": "object", "type": "object",
"required": [ "required": [
@ -178,14 +175,17 @@
"foo" "foo"
], ],
"properties": { "properties": {
"bar": {
"type": "boolean"
},
"foo": { "foo": {
"type": "integer", "type": "integer",
"format": "int32" "format": "int32"
},
"bar": {
"type": "boolean"
} }
} }
},
"UnitStruct": {
"type": "null"
} }
} }
} }

View file

@ -1,7 +1,7 @@
{ {
"$schema": "http://json-schema.org/draft-07/schema#", "$schema": "http://json-schema.org/draft-07/schema#",
"title": "External", "title": "External",
"anyOf": [ "oneOf": [
{ {
"type": "string", "type": "string",
"enum": [ "enum": [
@ -61,12 +61,12 @@
"foo" "foo"
], ],
"properties": { "properties": {
"bar": {
"type": "boolean"
},
"foo": { "foo": {
"type": "integer", "type": "integer",
"format": "int32" "format": "int32"
},
"bar": {
"type": "boolean"
} }
}, },
"additionalProperties": false "additionalProperties": false
@ -112,9 +112,6 @@
} }
], ],
"definitions": { "definitions": {
"UnitStruct": {
"type": "null"
},
"Struct": { "Struct": {
"type": "object", "type": "object",
"required": [ "required": [
@ -122,14 +119,17 @@
"foo" "foo"
], ],
"properties": { "properties": {
"bar": {
"type": "boolean"
},
"foo": { "foo": {
"type": "integer", "type": "integer",
"format": "int32" "format": "int32"
},
"bar": {
"type": "boolean"
} }
} }
},
"UnitStruct": {
"type": "null"
} }
} }
} }

View file

@ -1,7 +1,7 @@
{ {
"$schema": "http://json-schema.org/draft-07/schema#", "$schema": "http://json-schema.org/draft-07/schema#",
"title": "External", "title": "External",
"anyOf": [ "oneOf": [
{ {
"type": "string", "type": "string",
"enum": [ "enum": [
@ -61,12 +61,12 @@
"foo" "foo"
], ],
"properties": { "properties": {
"bar": {
"type": "boolean"
},
"foo": { "foo": {
"type": "integer", "type": "integer",
"format": "int32" "format": "int32"
},
"bar": {
"type": "boolean"
} }
} }
} }
@ -111,9 +111,6 @@
} }
], ],
"definitions": { "definitions": {
"UnitStruct": {
"type": "null"
},
"Struct": { "Struct": {
"type": "object", "type": "object",
"required": [ "required": [
@ -121,14 +118,17 @@
"foo" "foo"
], ],
"properties": { "properties": {
"bar": {
"type": "boolean"
},
"foo": { "foo": {
"type": "integer", "type": "integer",
"format": "int32" "format": "int32"
},
"bar": {
"type": "boolean"
} }
} }
},
"UnitStruct": {
"type": "null"
} }
} }
} }

View file

@ -1,7 +1,7 @@
{ {
"$schema": "http://json-schema.org/draft-07/schema#", "$schema": "http://json-schema.org/draft-07/schema#",
"title": "Internal", "title": "Internal",
"anyOf": [ "oneOf": [
{ {
"type": "object", "type": "object",
"required": [ "required": [
@ -55,18 +55,18 @@
"typeProperty" "typeProperty"
], ],
"properties": { "properties": {
"typeProperty": { "bar": {
"type": "string", "type": "boolean"
"enum": [
"StructNewType"
]
}, },
"foo": { "foo": {
"type": "integer", "type": "integer",
"format": "int32" "format": "int32"
}, },
"bar": { "typeProperty": {
"type": "boolean" "type": "string",
"enum": [
"StructNewType"
]
} }
} }
}, },
@ -78,18 +78,18 @@
"typeProperty" "typeProperty"
], ],
"properties": { "properties": {
"typeProperty": { "bar": {
"type": "string", "type": "boolean"
"enum": [
"Struct"
]
}, },
"foo": { "foo": {
"type": "integer", "type": "integer",
"format": "int32" "format": "int32"
}, },
"bar": { "typeProperty": {
"type": "boolean" "type": "string",
"enum": [
"Struct"
]
} }
}, },
"additionalProperties": false "additionalProperties": false

View file

@ -1,7 +1,7 @@
{ {
"$schema": "http://json-schema.org/draft-07/schema#", "$schema": "http://json-schema.org/draft-07/schema#",
"title": "Internal", "title": "Internal",
"anyOf": [ "oneOf": [
{ {
"type": "object", "type": "object",
"required": [ "required": [
@ -55,18 +55,18 @@
"typeProperty" "typeProperty"
], ],
"properties": { "properties": {
"typeProperty": { "bar": {
"type": "string", "type": "boolean"
"enum": [
"StructNewType"
]
}, },
"foo": { "foo": {
"type": "integer", "type": "integer",
"format": "int32" "format": "int32"
}, },
"bar": { "typeProperty": {
"type": "boolean" "type": "string",
"enum": [
"StructNewType"
]
} }
} }
}, },
@ -78,18 +78,18 @@
"typeProperty" "typeProperty"
], ],
"properties": { "properties": {
"typeProperty": { "bar": {
"type": "string", "type": "boolean"
"enum": [
"Struct"
]
}, },
"foo": { "foo": {
"type": "integer", "type": "integer",
"format": "int32" "format": "int32"
}, },
"bar": { "typeProperty": {
"type": "boolean" "type": "string",
"enum": [
"Struct"
]
} }
} }
}, },

View file

@ -1,7 +1,7 @@
{ {
"$schema": "http://json-schema.org/draft-07/schema#", "$schema": "http://json-schema.org/draft-07/schema#",
"title": "OuterEnum", "title": "OuterEnum",
"anyOf": [ "oneOf": [
{ {
"type": "object", "type": "object",
"required": [ "required": [

View file

@ -1,7 +1,7 @@
{ {
"$schema": "http://json-schema.org/draft-07/schema#", "$schema": "http://json-schema.org/draft-07/schema#",
"title": "Adjacent", "title": "Adjacent",
"anyOf": [ "oneOf": [
{ {
"type": "object", "type": "object",
"required": [ "required": [
@ -9,12 +9,6 @@
"t" "t"
], ],
"properties": { "properties": {
"t": {
"type": "string",
"enum": [
"Struct"
]
},
"c": { "c": {
"type": "object", "type": "object",
"required": [ "required": [
@ -25,6 +19,12 @@
"type": "boolean" "type": "boolean"
} }
} }
},
"t": {
"type": "string",
"enum": [
"Struct"
]
} }
} }
}, },
@ -35,14 +35,14 @@
"t" "t"
], ],
"properties": { "properties": {
"c": {
"type": "boolean"
},
"t": { "t": {
"type": "string", "type": "string",
"enum": [ "enum": [
"NewType" "NewType"
] ]
},
"c": {
"type": "boolean"
} }
} }
}, },
@ -53,12 +53,6 @@
"t" "t"
], ],
"properties": { "properties": {
"t": {
"type": "string",
"enum": [
"Tuple"
]
},
"c": { "c": {
"type": "array", "type": "array",
"items": [ "items": [
@ -72,6 +66,12 @@
], ],
"maxItems": 2, "maxItems": 2,
"minItems": 2 "minItems": 2
},
"t": {
"type": "string",
"enum": [
"Tuple"
]
} }
} }
}, },
@ -82,14 +82,14 @@
"t" "t"
], ],
"properties": { "properties": {
"c": {
"type": "boolean"
},
"t": { "t": {
"type": "string", "type": "string",
"enum": [ "enum": [
"Unit" "Unit"
] ]
},
"c": {
"type": "boolean"
} }
} }
} }

View file

@ -1,7 +1,7 @@
{ {
"$schema": "http://json-schema.org/draft-07/schema#", "$schema": "http://json-schema.org/draft-07/schema#",
"title": "External", "title": "External",
"anyOf": [ "oneOf": [
{ {
"type": "object", "type": "object",
"required": [ "required": [

View file

@ -1,7 +1,7 @@
{ {
"$schema": "http://json-schema.org/draft-07/schema#", "$schema": "http://json-schema.org/draft-07/schema#",
"title": "Internal", "title": "Internal",
"anyOf": [ "oneOf": [
{ {
"type": "object", "type": "object",
"required": [ "required": [
@ -9,14 +9,14 @@
"typeProperty" "typeProperty"
], ],
"properties": { "properties": {
"foo": {
"type": "boolean"
},
"typeProperty": { "typeProperty": {
"type": "string", "type": "string",
"enum": [ "enum": [
"Struct" "Struct"
] ]
},
"foo": {
"type": "boolean"
} }
} }
}, },

View file

@ -1,7 +1,7 @@
{ {
"$schema": "http://json-schema.org/draft-07/schema#", "$schema": "http://json-schema.org/draft-07/schema#",
"title": "MyEnum", "title": "MyEnum",
"anyOf": [ "oneOf": [
{ {
"type": "string", "type": "string",
"enum": [ "enum": [

View file

@ -22,7 +22,7 @@ pub fn test_schema(actual: &RootSchema, file: &str) -> TestResult {
let expected_json = match fs::read_to_string(format!("tests/expected/{}.json", file)) { let expected_json = match fs::read_to_string(format!("tests/expected/{}.json", file)) {
Ok(j) => j, Ok(j) => j,
Err(e) => { Err(e) => {
write_actual_to_file(&actual, file)?; write_actual_to_file(actual, file)?;
return Err(Box::from(e)); return Err(Box::from(e));
} }
}; };
@ -32,7 +32,7 @@ pub fn test_schema(actual: &RootSchema, file: &str) -> TestResult {
write_actual_to_file(actual, file)?; write_actual_to_file(actual, file)?;
} }
assert_eq!(actual, expected); assert_eq!(expected, actual);
Ok(()) Ok(())
} }

View file

@ -1,3 +1,5 @@
use std::collections::HashSet;
use crate::{ast::*, attr::WithAttr, metadata::SchemaMetadata}; use crate::{ast::*, attr::WithAttr, metadata::SchemaMetadata};
use proc_macro2::{Span, TokenStream}; use proc_macro2::{Span, TokenStream};
use serde_derive_internals::ast::Style; use serde_derive_internals::ast::Style;
@ -140,8 +142,14 @@ fn expr_for_external_tagged_enum<'a>(
variants: impl Iterator<Item = &'a Variant<'a>>, variants: impl Iterator<Item = &'a Variant<'a>>,
deny_unknown_fields: bool, deny_unknown_fields: bool,
) -> TokenStream { ) -> TokenStream {
let (unit_variants, complex_variants): (Vec<_>, Vec<_>) = let mut unique_names = HashSet::<String>::new();
variants.partition(|v| v.is_unit() && v.attrs.with.is_none()); let mut count = 0;
let (unit_variants, complex_variants): (Vec<_>, Vec<_>) = variants
.inspect(|v| {
unique_names.insert(v.name());
count += 1;
})
.partition(|v| v.is_unit() && v.attrs.with.is_none());
let unit_names = unit_variants.iter().map(|v| v.name()); let unit_names = unit_variants.iter().map(|v| v.name());
let unit_schema = schema_object(quote! { let unit_schema = schema_object(quote! {
@ -188,12 +196,7 @@ fn expr_for_external_tagged_enum<'a>(
schema_expr schema_expr
})); }));
schema_object(quote! { variant_subschemas(unique_names.len() == count, schemas)
subschemas: Some(Box::new(schemars::schema::SubschemaValidation {
any_of: Some(vec![#(#schemas),*]),
..Default::default()
})),
})
} }
fn expr_for_internal_tagged_enum<'a>( fn expr_for_internal_tagged_enum<'a>(
@ -201,70 +204,71 @@ fn expr_for_internal_tagged_enum<'a>(
tag_name: &str, tag_name: &str,
deny_unknown_fields: bool, deny_unknown_fields: bool,
) -> TokenStream { ) -> TokenStream {
let variant_schemas = variants.map(|variant| { let mut unique_names = HashSet::new();
let name = variant.name(); let mut count = 0;
let type_schema = schema_object(quote! { let variant_schemas = variants
instance_type: Some(schemars::schema::InstanceType::String.into()), .map(|variant| {
enum_values: Some(vec![#name.into()]), unique_names.insert(variant.name());
}); count += 1;
let mut tag_schema = schema_object(quote! { let name = variant.name();
instance_type: Some(schemars::schema::InstanceType::Object.into()), let type_schema = schema_object(quote! {
object: Some(Box::new(schemars::schema::ObjectValidation { instance_type: Some(schemars::schema::InstanceType::String.into()),
properties: { enum_values: Some(vec![#name.into()]),
let mut props = schemars::Map::new(); });
props.insert(#tag_name.to_owned(), #type_schema);
props
},
required: {
let mut required = schemars::Set::new();
required.insert(#tag_name.to_owned());
required
},
..Default::default()
})),
});
variant.attrs.as_metadata().apply_to_schema(&mut tag_schema); let mut tag_schema = schema_object(quote! {
instance_type: Some(schemars::schema::InstanceType::Object.into()),
object: Some(Box::new(schemars::schema::ObjectValidation {
properties: {
let mut props = schemars::Map::new();
props.insert(#tag_name.to_owned(), #type_schema);
props
},
required: {
let mut required = schemars::Set::new();
required.insert(#tag_name.to_owned());
required
},
..Default::default()
})),
});
if let Some(variant_schema) = variant.attrs.as_metadata().apply_to_schema(&mut tag_schema);
expr_for_untagged_enum_variant_for_flatten(&variant, deny_unknown_fields)
{
tag_schema.extend(quote!(.flatten(#variant_schema)))
}
tag_schema if let Some(variant_schema) =
}); expr_for_untagged_enum_variant_for_flatten(variant, deny_unknown_fields)
{
tag_schema.extend(quote!(.flatten(#variant_schema)))
}
schema_object(quote! { tag_schema
subschemas: Some(Box::new(schemars::schema::SubschemaValidation { })
any_of: Some(vec![#(#variant_schemas),*]), .collect();
..Default::default()
})), variant_subschemas(unique_names.len() == count, variant_schemas)
})
} }
fn expr_for_untagged_enum<'a>( fn expr_for_untagged_enum<'a>(
variants: impl Iterator<Item = &'a Variant<'a>>, variants: impl Iterator<Item = &'a Variant<'a>>,
deny_unknown_fields: bool, deny_unknown_fields: bool,
) -> TokenStream { ) -> TokenStream {
let schemas = variants.map(|variant| { let schemas = variants
let mut schema_expr = expr_for_untagged_enum_variant(variant, deny_unknown_fields); .map(|variant| {
let mut schema_expr = expr_for_untagged_enum_variant(variant, deny_unknown_fields);
variant variant
.attrs .attrs
.as_metadata() .as_metadata()
.apply_to_schema(&mut schema_expr); .apply_to_schema(&mut schema_expr);
schema_expr schema_expr
}); })
.collect();
schema_object(quote! { // Untagged enums can easily have variants whose schemas overlap; rather
subschemas: Some(Box::new(schemars::schema::SubschemaValidation { // that checking the exclusivity of each subschema we simply us `any_of`.
any_of: Some(vec![#(#schemas),*]), variant_subschemas(false, schemas)
..Default::default()
})),
})
} }
fn expr_for_adjacent_tagged_enum<'a>( fn expr_for_adjacent_tagged_enum<'a>(
@ -273,70 +277,92 @@ fn expr_for_adjacent_tagged_enum<'a>(
content_name: &str, content_name: &str,
deny_unknown_fields: bool, deny_unknown_fields: bool,
) -> TokenStream { ) -> TokenStream {
let schemas = variants.map(|variant| { let mut unique_names = HashSet::new();
let content_schema = if variant.is_unit() && variant.attrs.with.is_none() { let mut count = 0;
None let schemas = variants
} else { .map(|variant| {
Some(expr_for_untagged_enum_variant(variant, deny_unknown_fields)) unique_names.insert(variant.name());
}; count += 1;
let (add_content_to_props, add_content_to_required) = content_schema let content_schema = if variant.is_unit() && variant.attrs.with.is_none() {
.map(|content_schema| { None
( } else {
quote!(props.insert(#content_name.to_owned(), #content_schema);), Some(expr_for_untagged_enum_variant(variant, deny_unknown_fields))
quote!(required.insert(#content_name.to_owned());), };
)
})
.unwrap_or_default();
let name = variant.name(); let (add_content_to_props, add_content_to_required) = content_schema
let tag_schema = schema_object(quote! { .map(|content_schema| {
instance_type: Some(schemars::schema::InstanceType::String.into()), (
enum_values: Some(vec![#name.into()]), quote!(props.insert(#content_name.to_owned(), #content_schema);),
}); quote!(required.insert(#content_name.to_owned());),
)
})
.unwrap_or_default();
let set_additional_properties = if deny_unknown_fields { let name = variant.name();
quote! { let tag_schema = schema_object(quote! {
additional_properties: Some(Box::new(false.into())), instance_type: Some(schemars::schema::InstanceType::String.into()),
} enum_values: Some(vec![#name.into()]),
} else { });
TokenStream::new()
};
let mut outer_schema = schema_object(quote! { let set_additional_properties = if deny_unknown_fields {
instance_type: Some(schemars::schema::InstanceType::Object.into()), quote! {
object: Some(Box::new(schemars::schema::ObjectValidation { additional_properties: Some(Box::new(false.into())),
properties: { }
let mut props = schemars::Map::new(); } else {
props.insert(#tag_name.to_owned(), #tag_schema); TokenStream::new()
#add_content_to_props };
props
}, let mut outer_schema = schema_object(quote! {
required: { instance_type: Some(schemars::schema::InstanceType::Object.into()),
let mut required = schemars::Set::new(); object: Some(Box::new(schemars::schema::ObjectValidation {
required.insert(#tag_name.to_owned()); properties: {
#add_content_to_required let mut props = schemars::Map::new();
required props.insert(#tag_name.to_owned(), #tag_schema);
}, #add_content_to_props
#set_additional_properties props
},
required: {
let mut required = schemars::Set::new();
required.insert(#tag_name.to_owned());
#add_content_to_required
required
},
#set_additional_properties
..Default::default()
})),
});
variant
.attrs
.as_metadata()
.apply_to_schema(&mut outer_schema);
outer_schema
})
.collect();
variant_subschemas(unique_names.len() == count, schemas)
}
/// Callers must determine if all subschemas are mutually exclusive. This can
/// be done for most tagging regimes by checking that all tag names are unique.
fn variant_subschemas(unique: bool, schemas: Vec<TokenStream>) -> TokenStream {
if unique {
schema_object(quote! {
subschemas: Some(Box::new(schemars::schema::SubschemaValidation {
one_of: Some(vec![#(#schemas),*]),
..Default::default() ..Default::default()
})), })),
}); })
} else {
variant schema_object(quote! {
.attrs subschemas: Some(Box::new(schemars::schema::SubschemaValidation {
.as_metadata() any_of: Some(vec![#(#schemas),*]),
.apply_to_schema(&mut outer_schema); ..Default::default()
})),
outer_schema })
}); }
schema_object(quote! {
subschemas: Some(Box::new(schemars::schema::SubschemaValidation {
any_of: Some(vec![#(#schemas),*]),
..Default::default()
})),
})
} }
fn expr_for_untagged_enum_variant(variant: &Variant, deny_unknown_fields: bool) -> TokenStream { fn expr_for_untagged_enum_variant(variant: &Variant, deny_unknown_fields: bool) -> TokenStream {