Support untagged enums

This commit is contained in:
Graham Esau 2019-08-05 20:40:06 +01:00
parent c552e6d208
commit 4a9fdd3334
3 changed files with 59 additions and 76 deletions

View file

@ -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<T>, RefCell<T>, Mutex<T>, RwLock<T>, Result<R,E>?, Duration, SystemTime,
// IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV6, SocketAddrV6,

View file

@ -3,26 +3,14 @@
"title": "schemars__schema__Schema",
"anyOf": [
{
"properties": {
"Bool": {
"type": "boolean"
}
}
},
{
"properties": {
"Ref": {
"$ref": "#/definitions/schemars__schema__SchemaRef"
}
}
},
{
"properties": {
"Object": {
"$ref": "#/definitions/schemars__schema__SchemaObject"
}
}
}
],
"definitions": {
"schemars__schema__InstanceType": {
@ -39,26 +27,14 @@
"schemars__schema__Schema": {
"anyOf": [
{
"properties": {
"Bool": {
"type": "boolean"
}
}
},
{
"properties": {
"Ref": {
"$ref": "#/definitions/schemars__schema__SchemaRef"
}
}
},
{
"properties": {
"Object": {
"$ref": "#/definitions/schemars__schema__SchemaObject"
}
}
}
]
},
"schemars__schema__SchemaObject": {
@ -224,43 +200,27 @@
"schemars__schema__SingleOrVec_schemars__schema__InstanceType_": {
"anyOf": [
{
"properties": {
"Single": {
"$ref": "#/definitions/schemars__schema__InstanceType"
}
}
},
{
"properties": {
"Vec": {
"type": "array",
"items": {
"$ref": "#/definitions/schemars__schema__InstanceType"
}
}
}
}
]
},
"schemars__schema__SingleOrVec_schemars__schema__Schema_": {
"anyOf": [
{
"properties": {
"Single": {
"$ref": "#/definitions/schemars__schema__Schema"
}
}
},
{
"properties": {
"Vec": {
"type": "array",
"items": {
"$ref": "#/definitions/schemars__schema__Schema"
}
}
}
}
]
}
}

View file

@ -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,7 +105,34 @@ 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 {
let sub_schema = schema_for_untagged_enum_variant(variant);
wrap_schema_fields(quote! {
properties: {
let mut props = std::collections::BTreeMap::new();
props.insert(#name.to_owned(), #sub_schema);
props
},
})
}));
wrap_schema_fields(quote! {
any_of: Some(vec![#(#schemas),*]),
})
}
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;
@ -112,21 +147,7 @@ fn schema_for_enum(variants: &[Variant]) -> TokenStream {
}
}
Style::Struct => schema_for_struct(&variant.fields),
Style::Unit => unreachable!("Unit variants already filtered out"),
};
wrap_schema_fields(quote! {
properties: {
let mut props = std::collections::BTreeMap::new();
props.insert(#name.to_owned(), #sub_schema);
props
},
})
}));
wrap_schema_fields(quote! {
any_of: Some(vec![#(#schemas),*]),
})
}
}
fn schema_for_struct(fields: &[Field]) -> TokenStream {