diff --git a/schemars/src/make_schema.rs b/schemars/src/make_schema.rs index ab8a6ee..1e2f741 100644 --- a/schemars/src/make_schema.rs +++ b/schemars/src/make_schema.rs @@ -35,7 +35,9 @@ macro_rules! no_ref_schema { }; } -// TODO any other serde types other than serde_json value? +// TODO any other serde/json types other than serde_json value? +// TODO serde yaml value/map under feature flag +// TODO add some inline attributes // https://github.com/serde-rs/serde/blob/ce75418e40a593fc5c0902cbf4a45305a4178dd7/serde/src/ser/impls.rs // Cell, RefCell, Mutex, RwLock, Result?, Duration, SystemTime, // IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV6, SocketAddrV6, diff --git a/schemars/tests/schema.json b/schemars/tests/schema.json index b39d9a5..2da5bfa 100644 --- a/schemars/tests/schema.json +++ b/schemars/tests/schema.json @@ -3,25 +3,13 @@ "title": "schemars__schema__Schema", "anyOf": [ { - "properties": { - "Bool": { - "type": "boolean" - } - } + "type": "boolean" }, { - "properties": { - "Ref": { - "$ref": "#/definitions/schemars__schema__SchemaRef" - } - } + "$ref": "#/definitions/schemars__schema__SchemaRef" }, { - "properties": { - "Object": { - "$ref": "#/definitions/schemars__schema__SchemaObject" - } - } + "$ref": "#/definitions/schemars__schema__SchemaObject" } ], "definitions": { @@ -39,25 +27,13 @@ "schemars__schema__Schema": { "anyOf": [ { - "properties": { - "Bool": { - "type": "boolean" - } - } + "type": "boolean" }, { - "properties": { - "Ref": { - "$ref": "#/definitions/schemars__schema__SchemaRef" - } - } + "$ref": "#/definitions/schemars__schema__SchemaRef" }, { - "properties": { - "Object": { - "$ref": "#/definitions/schemars__schema__SchemaObject" - } - } + "$ref": "#/definitions/schemars__schema__SchemaObject" } ] }, @@ -224,20 +200,12 @@ "schemars__schema__SingleOrVec_schemars__schema__InstanceType_": { "anyOf": [ { - "properties": { - "Single": { - "$ref": "#/definitions/schemars__schema__InstanceType" - } - } + "$ref": "#/definitions/schemars__schema__InstanceType" }, { - "properties": { - "Vec": { - "type": "array", - "items": { - "$ref": "#/definitions/schemars__schema__InstanceType" - } - } + "type": "array", + "items": { + "$ref": "#/definitions/schemars__schema__InstanceType" } } ] @@ -245,20 +213,12 @@ "schemars__schema__SingleOrVec_schemars__schema__Schema_": { "anyOf": [ { - "properties": { - "Single": { - "$ref": "#/definitions/schemars__schema__Schema" - } - } + "$ref": "#/definitions/schemars__schema__Schema" }, { - "properties": { - "Vec": { - "type": "array", - "items": { - "$ref": "#/definitions/schemars__schema__Schema" - } - } + "type": "array", + "items": { + "$ref": "#/definitions/schemars__schema__Schema" } } ] diff --git a/schemars_derive/src/lib.rs b/schemars_derive/src/lib.rs index 4c07590..753e199 100644 --- a/schemars_derive/src/lib.rs +++ b/schemars_derive/src/lib.rs @@ -7,6 +7,7 @@ extern crate proc_macro; use proc_macro2::{Span, TokenStream}; use serde_derive_internals::ast::{Container, Data, Field, Style, Variant}; +use serde_derive_internals::attr::{self, EnumTag}; use serde_derive_internals::{Ctxt, Derive}; use syn::spanned::Spanned; use syn::{DeriveInput, GenericParam, Generics}; @@ -27,7 +28,7 @@ pub fn derive_make_schema(input: proc_macro::TokenStream) -> proc_macro::TokenSt let schema = match cont.data { Data::Struct(Style::Struct, ref fields) => schema_for_struct(fields), - Data::Enum(ref variants) => schema_for_enum(variants), + Data::Enum(ref variants) => schema_for_enum(variants, &cont.attrs), _ => unimplemented!("work in progress!"), }; @@ -73,8 +74,15 @@ fn is_unit_variant(v: &&Variant) -> bool { } } -fn schema_for_enum(variants: &[Variant]) -> TokenStream { - // TODO handle untagged or adjacently tagged enums +fn schema_for_enum(variants: &[Variant], cattrs: &attr::Container) -> TokenStream { + match cattrs.tag() { + EnumTag::External => schema_for_external_tagged_enum(variants), + EnumTag::None => schema_for_untagged_enum(variants), + _ => unimplemented!("Adjacent/internal tagged enums not yet supported."), + } +} + +fn schema_for_external_tagged_enum(variants: &[Variant]) -> TokenStream { let (unit_variants, complex_variants): (Vec<_>, Vec<_>) = variants.into_iter().partition(is_unit_variant); let unit_count = unit_variants.len(); @@ -97,24 +105,7 @@ fn schema_for_enum(variants: &[Variant]) -> TokenStream { schemas.extend(complex_variants.into_iter().map(|variant| { let name = variant.attrs.name().deserialize_name(); - let sub_schema = match variant.style { - Style::Newtype => { - let f = &variant.fields[0]; - let ty = f.ty; - quote_spanned! {f.original.span()=> - gen.subschema_for::<#ty>() - } - } - Style::Tuple => { - let types = variant.fields.iter().map(|f| f.ty); - quote! { - gen.subschema_for::<(#(#types),*)>() - } - } - Style::Struct => schema_for_struct(&variant.fields), - Style::Unit => unreachable!("Unit variants already filtered out"), - }; - + let sub_schema = schema_for_untagged_enum_variant(variant); wrap_schema_fields(quote! { properties: { let mut props = std::collections::BTreeMap::new(); @@ -129,6 +120,36 @@ fn schema_for_enum(variants: &[Variant]) -> TokenStream { }) } +fn schema_for_untagged_enum(variants: &[Variant]) -> TokenStream { + let schemas = variants.into_iter().map(schema_for_untagged_enum_variant); + + wrap_schema_fields(quote! { + any_of: Some(vec![#(#schemas),*]), + }) +} + +fn schema_for_untagged_enum_variant(variant: &Variant) -> TokenStream { + match variant.style { + Style::Unit => quote! { + gen.subschema_for::<()>() + }, + Style::Newtype => { + let f = &variant.fields[0]; + let ty = f.ty; + quote_spanned! {f.original.span()=> + gen.subschema_for::<#ty>() + } + } + Style::Tuple => { + let types = variant.fields.iter().map(|f| f.ty); + quote! { + gen.subschema_for::<(#(#types),*)>() + } + } + Style::Struct => schema_for_struct(&variant.fields), + } +} + fn schema_for_struct(fields: &[Field]) -> TokenStream { let recurse = fields.into_iter().map(|f| { let name = f.attrs.name().deserialize_name();