diff --git a/schemars/tests/enum.rs b/schemars/tests/enum.rs index cb3073c..f52b8bc 100644 --- a/schemars/tests/enum.rs +++ b/schemars/tests/enum.rs @@ -1,12 +1,12 @@ mod util; -use schemars::MakeSchema; +use schemars::{MakeSchema, Map}; use util::*; #[derive(Debug, MakeSchema)] #[schemars(rename_all = "camelCase")] pub enum External { UnitOne, - String(String), + StringMap(Map), Struct{ foo: i32, bar: bool }, UnitTwo, } @@ -15,12 +15,12 @@ pub enum External { fn enum_external_tag() -> TestResult { test_default_generated_schema::("enum-external") } -/* + #[derive(Debug, MakeSchema)] #[schemars(tag = "typeProperty")] pub enum Internal { UnitOne, - String(String), + StringMap(Map), Struct{ foo: i32, bar: bool }, UnitTwo, } @@ -29,12 +29,12 @@ pub enum Internal { fn enum_internal_tag() -> TestResult { test_default_generated_schema::("enum-internal") } -*/ + #[derive(Debug, MakeSchema)] #[schemars(untagged)] pub enum Untagged { UnitOne, - String(String), + StringMap(Map), Struct{ foo: i32, bar: bool } } diff --git a/schemars/tests/expected/enum-external.json b/schemars/tests/expected/enum-external.json index 021fcba..4e31de2 100644 --- a/schemars/tests/expected/enum-external.json +++ b/schemars/tests/expected/enum-external.json @@ -11,12 +11,15 @@ { "type": "object", "properties": { - "string": { - "type": "string" + "stringMap": { + "type": "object", + "additionalProperties": { + "type": "string" + } } }, "required": [ - "string" + "stringMap" ] }, { diff --git a/schemars/tests/expected/enum-internal.json b/schemars/tests/expected/enum-internal.json index f50e5de..1f3bf6f 100644 --- a/schemars/tests/expected/enum-internal.json +++ b/schemars/tests/expected/enum-internal.json @@ -1,222 +1,73 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Schema", + "title": "Internal", "anyOf": [ { - "type": "boolean" - }, - { - "$ref": "#/definitions/Ref" - }, - { - "$ref": "#/definitions/SchemaObject" - } - ], - "definitions": { - "InstanceType": { - "enum": [ - "null", - "boolean", - "object", - "array", - "number", - "string", - "integer" - ] - }, - "Schema": { - "anyOf": [ - { - "type": "boolean" - }, - { - "$ref": "#/definitions/Ref" - }, - { - "$ref": "#/definitions/SchemaObject" - } - ] - }, - "SchemaObject": { "type": "object", "properties": { - "$id": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } + "typeProperty": { + "type": "string", + "enum": [ + "UnitOne" ] - }, - "$schema": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ] - }, - "allOf": { - "anyOf": [ - { - "type": "array", - "items": { - "$ref": "#/definitions/Schema" - } - }, - { - "type": "null" - } - ] - }, - "anyOf": { - "anyOf": [ - { - "type": "array", - "items": { - "$ref": "#/definitions/Schema" - } - }, - { - "type": "null" - } - ] - }, - "definitions": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/Schema" - } - }, - "description": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ] - }, - "enum": { - "anyOf": [ - { - "type": "array", - "items": true - }, - { - "type": "null" - } - ] - }, - "items": { - "anyOf": [ - { - "$ref": "#/definitions/SingleOrVec_For_Schema" - }, - { - "type": "null" - } - ] - }, - "not": { - "anyOf": [ - { - "$ref": "#/definitions/Schema" - }, - { - "type": "null" - } - ] - }, - "oneOf": { - "anyOf": [ - { - "type": "array", - "items": { - "$ref": "#/definitions/Schema" - } - }, - { - "type": "null" - } - ] - }, - "properties": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/Schema" - } - }, - "required": { - "type": "array", - "items": { - "type": "string" - } - }, - "title": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ] - }, - "type": { - "anyOf": [ - { - "$ref": "#/definitions/SingleOrVec_For_InstanceType" - }, - { - "type": "null" - } - ] - } - }, - "additionalProperties": true - }, - "Ref": { - "type": "object", - "properties": { - "$ref": { - "type": "string" } }, "required": [ - "$ref" + "typeProperty" ] }, - "SingleOrVec_For_InstanceType": { - "anyOf": [ - { - "$ref": "#/definitions/InstanceType" - }, - { - "type": "array", - "items": { - "$ref": "#/definitions/InstanceType" - } + { + "type": "object", + "properties": { + "typeProperty": { + "type": "string", + "enum": [ + "StringMap" + ] } + }, + "required": [ + "typeProperty" + ], + "additionalProperties": { + "type": "string" + } + }, + { + "type": "object", + "properties": { + "typeProperty": { + "type": "string", + "enum": [ + "Struct" + ] + }, + "bar": { + "type": "boolean" + }, + "foo": { + "type": "integer" + } + }, + "required": [ + "bar", + "foo", + "typeProperty" ] }, - "SingleOrVec_For_Schema": { - "anyOf": [ - { - "$ref": "#/definitions/Schema" - }, - { - "type": "array", - "items": { - "$ref": "#/definitions/Schema" - } + { + "type": "object", + "properties": { + "typeProperty": { + "type": "string", + "enum": [ + "UnitTwo" + ] } + }, + "required": [ + "typeProperty" ] } - } + ] } \ No newline at end of file diff --git a/schemars/tests/expected/enum-untagged.json b/schemars/tests/expected/enum-untagged.json index 441329b..e874634 100644 --- a/schemars/tests/expected/enum-untagged.json +++ b/schemars/tests/expected/enum-untagged.json @@ -6,7 +6,10 @@ "type": "null" }, { - "type": "string" + "type": "object", + "additionalProperties": { + "type": "string" + } }, { "type": "object", diff --git a/schemars_derive/src/lib.rs b/schemars_derive/src/lib.rs index 63c1cc1..a643950 100644 --- a/schemars_derive/src/lib.rs +++ b/schemars_derive/src/lib.rs @@ -104,6 +104,7 @@ fn schema_for_enum(variants: &[Variant], cattrs: &attr::Container) -> TokenStrea match cattrs.tag() { EnumTag::External => schema_for_external_tagged_enum(variants, cattrs), EnumTag::None => schema_for_untagged_enum(variants, cattrs), + EnumTag::Internal { tag } => schema_for_internal_tagged_enum(variants, cattrs, tag), _ => unimplemented!("Adjacent/internal tagged enums not yet supported."), } } @@ -152,6 +153,45 @@ fn schema_for_external_tagged_enum(variants: &[Variant], cattrs: &attr::Containe }) } +fn schema_for_internal_tagged_enum( + variants: &[Variant], + cattrs: &attr::Container, + tag_name: &str, +) -> TokenStream { + let schemas = variants.into_iter().map(|variant| { + let name = variant.attrs.name().deserialize_name(); + let type_schema = wrap_schema_fields(quote! { + instance_type: Some(schemars::schema::InstanceType::String.into()), + enum_values: Some(vec![#name.into()]), + }); + let schema = wrap_schema_fields(quote! { + instance_type: Some(schemars::schema::InstanceType::Object.into()), + 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 + }, + }); + if is_unit_variant(&variant) { + schema + } else { + let sub_schema = schema_for_untagged_enum_variant(variant, cattrs); + quote! { + #schema.flatten(#sub_schema)? + } + } + }); + + wrap_schema_fields(quote! { + any_of: Some(vec![#(#schemas),*]), + }) +} + fn schema_for_untagged_enum(variants: &[Variant], cattrs: &attr::Container) -> TokenStream { let schemas = variants .into_iter()