Allow schema_with attr on enum variants

This commit is contained in:
Graham Esau 2021-04-10 15:16:16 +01:00
parent 2d38db903a
commit 5dc644000c
9 changed files with 93 additions and 27 deletions

View file

@ -1,5 +1,9 @@
# Changelog # Changelog
## [0.8.4] - **In-dev**
### Added:
- `#[schemars(schema_with = "...")]` attribute can now be set on enum variants.
## [0.8.3] - 2021-04-05 ## [0.8.3] - 2021-04-05
### Added: ### Added:
- Support for `#[schemars(crate = "...")]` attribute to allow deriving JsonSchema when the schemars crate is aliased to a different name (https://github.com/GREsau/schemars/pull/55 / https://github.com/GREsau/schemars/pull/80) - Support for `#[schemars(crate = "...")]` attribute to allow deriving JsonSchema when the schemars crate is aliased to a different name (https://github.com/GREsau/schemars/pull/55 / https://github.com/GREsau/schemars/pull/80)

View file

@ -14,8 +14,8 @@ pub struct Struct {
bar: bool, bar: bool,
} }
// Outer container should always have additionalPropreties: false // Outer container should always have additionalProperties: false
// `Struct` variant should have additionalPropreties: false // `Struct` variant should have additionalProperties: false
#[derive(Debug, JsonSchema)] #[derive(Debug, JsonSchema)]
#[schemars(rename_all = "camelCase", deny_unknown_fields)] #[schemars(rename_all = "camelCase", deny_unknown_fields)]
pub enum External { pub enum External {
@ -38,7 +38,7 @@ fn enum_external_tag() -> TestResult {
test_default_generated_schema::<External>("enum-external-duf") test_default_generated_schema::<External>("enum-external-duf")
} }
// Only `Struct` variant should have additionalPropreties: false // Only `Struct` variant should have additionalProperties: false
#[derive(Debug, JsonSchema)] #[derive(Debug, JsonSchema)]
#[schemars(tag = "typeProperty", deny_unknown_fields)] #[schemars(tag = "typeProperty", deny_unknown_fields)]
pub enum Internal { pub enum Internal {
@ -60,7 +60,7 @@ fn enum_internal_tag() -> TestResult {
test_default_generated_schema::<Internal>("enum-internal-duf") test_default_generated_schema::<Internal>("enum-internal-duf")
} }
// Only `Struct` variant should have additionalPropreties: false // Only `Struct` variant should have additionalProperties: false
#[derive(Debug, JsonSchema)] #[derive(Debug, JsonSchema)]
#[schemars(untagged, deny_unknown_fields)] #[schemars(untagged, deny_unknown_fields)]
pub enum Untagged { pub enum Untagged {
@ -82,7 +82,7 @@ fn enum_untagged() -> TestResult {
test_default_generated_schema::<Untagged>("enum-untagged-duf") test_default_generated_schema::<Untagged>("enum-untagged-duf")
} }
// Outer container and `Struct` variant should have additionalPropreties: false // Outer container and `Struct` variant should have additionalProperties: false
#[derive(Debug, JsonSchema)] #[derive(Debug, JsonSchema)]
#[schemars(tag = "t", content = "c", deny_unknown_fields)] #[schemars(tag = "t", content = "c", deny_unknown_fields)]
pub enum Adjacent { pub enum Adjacent {

View file

@ -74,6 +74,24 @@
"minItems": 2 "minItems": 2
} }
} }
},
{
"type": "object",
"required": [
"c",
"t"
],
"properties": {
"t": {
"type": "string",
"enum": [
"Unit"
]
},
"c": {
"type": "boolean"
}
}
} }
] ]
} }

View file

@ -56,6 +56,18 @@
} }
}, },
"additionalProperties": false "additionalProperties": false
},
{
"type": "object",
"required": [
"unit"
],
"properties": {
"unit": {
"type": "boolean"
}
},
"additionalProperties": false
} }
] ]
} }

View file

@ -36,6 +36,23 @@
] ]
} }
} }
},
{
"type": [
"boolean",
"object"
],
"required": [
"typeProperty"
],
"properties": {
"typeProperty": {
"type": "string",
"enum": [
"Unit"
]
}
}
} }
] ]
} }

View file

@ -29,6 +29,9 @@
], ],
"maxItems": 2, "maxItems": 2,
"minItems": 2 "minItems": 2
},
{
"type": "boolean"
} }
] ]
} }

View file

@ -2,8 +2,6 @@ mod util;
use schemars::JsonSchema; use schemars::JsonSchema;
use util::*; use util::*;
// FIXME determine whether schema_with should be allowed on unit variants
fn schema_fn(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { fn schema_fn(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
<bool>::json_schema(gen) <bool>::json_schema(gen)
} }
@ -23,8 +21,8 @@ pub enum External {
#[schemars(schema_with = "schema_fn")] DoesntImplementJsonSchema, #[schemars(schema_with = "schema_fn")] DoesntImplementJsonSchema,
i32, i32,
), ),
// #[schemars(schema_with = "schema_fn")] #[schemars(schema_with = "schema_fn")]
// Unit, Unit,
} }
#[test] #[test]
@ -40,8 +38,8 @@ pub enum Internal {
foo: DoesntImplementJsonSchema, foo: DoesntImplementJsonSchema,
}, },
NewType(#[schemars(schema_with = "schema_fn")] DoesntImplementJsonSchema), NewType(#[schemars(schema_with = "schema_fn")] DoesntImplementJsonSchema),
// #[schemars(schema_with = "schema_fn")] #[schemars(schema_with = "schema_fn")]
// Unit, Unit,
} }
#[test] #[test]
@ -61,8 +59,8 @@ pub enum Untagged {
#[schemars(schema_with = "schema_fn")] DoesntImplementJsonSchema, #[schemars(schema_with = "schema_fn")] DoesntImplementJsonSchema,
i32, i32,
), ),
// #[schemars(schema_with = "schema_fn")] #[schemars(schema_with = "schema_fn")]
// Unit, Unit,
} }
#[test] #[test]
@ -82,8 +80,8 @@ pub enum Adjacent {
#[schemars(schema_with = "schema_fn")] DoesntImplementJsonSchema, #[schemars(schema_with = "schema_fn")] DoesntImplementJsonSchema,
i32, i32,
), ),
// #[schemars(schema_with = "schema_fn")] #[schemars(schema_with = "schema_fn")]
// Unit, Unit,
} }
#[test] #[test]

View file

@ -50,7 +50,7 @@ fn derive_json_schema(
let (impl_generics, ty_generics, where_clause) = cont.generics.split_for_impl(); let (impl_generics, ty_generics, where_clause) = cont.generics.split_for_impl();
if let Some(transparent_field) = cont.transparent_field() { if let Some(transparent_field) = cont.transparent_field() {
let (ty, type_def) = schema_exprs::type_for_schema(transparent_field, 0); let (ty, type_def) = schema_exprs::type_for_field_schema(transparent_field, 0);
return Ok(quote! { return Ok(quote! {
const _: () = { const _: () = {
#crate_alias #crate_alias

View file

@ -57,7 +57,7 @@ pub fn expr_for_repr(cont: &Container) -> Result<TokenStream, syn::Error> {
} }
fn expr_for_field(field: &Field, allow_ref: bool) -> TokenStream { fn expr_for_field(field: &Field, allow_ref: bool) -> TokenStream {
let (ty, type_def) = type_for_schema(field, 0); let (ty, type_def) = type_for_field_schema(field, 0);
let span = field.original.span(); let span = field.original.span();
let gen = quote!(gen); let gen = quote!(gen);
@ -78,11 +78,17 @@ fn expr_for_field(field: &Field, allow_ref: bool) -> TokenStream {
} }
} }
pub fn type_for_schema(field: &Field, local_id: usize) -> (syn::Type, Option<TokenStream>) { pub fn type_for_field_schema(field: &Field, local_id: usize) -> (syn::Type, Option<TokenStream>) {
match &field.attrs.with { match &field.attrs.with {
None => (field.ty.to_owned(), None), None => (field.ty.to_owned(), None),
Some(WithAttr::Type(ty)) => (ty.to_owned(), None), Some(with_attr) => type_for_schema(with_attr, local_id),
Some(WithAttr::Function(fun)) => { }
}
fn type_for_schema(with_attr: &WithAttr, local_id: usize) -> (syn::Type, Option<TokenStream>) {
match with_attr {
WithAttr::Type(ty) => (ty.to_owned(), None),
WithAttr::Function(fun) => {
let ty_name = format_ident!("_SchemarsSchemaWithFunction{}", local_id); let ty_name = format_ident!("_SchemarsSchemaWithFunction{}", local_id);
let fn_name = fun.segments.last().unwrap().ident.to_string(); let fn_name = fun.segments.last().unwrap().ident.to_string();
@ -316,10 +322,14 @@ fn expr_for_adjacent_tagged_enum<'a>(
} }
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 {
if let Some(WithAttr::Type(with)) = &variant.attrs.with { if let Some(with_attr) = &variant.attrs.with {
let (ty, type_def) = type_for_schema(with_attr, 0);
let gen = quote!(gen); let gen = quote!(gen);
return quote_spanned! {variant.original.span()=> return quote_spanned! {variant.original.span()=>
#gen.subschema_for::<#with>() {
#type_def
#gen.subschema_for::<#ty>()
}
}; };
} }
@ -335,10 +345,14 @@ fn expr_for_untagged_enum_variant_for_flatten(
variant: &Variant, variant: &Variant,
deny_unknown_fields: bool, deny_unknown_fields: bool,
) -> Option<TokenStream> { ) -> Option<TokenStream> {
if let Some(WithAttr::Type(with)) = &variant.attrs.with { if let Some(with_attr) = &variant.attrs.with {
let (ty, type_def) = type_for_schema(with_attr, 0);
let gen = quote!(gen); let gen = quote!(gen);
return Some(quote_spanned! {variant.original.span()=> return Some(quote_spanned! {variant.original.span()=>
<#with as schemars::JsonSchema>::json_schema(#gen) {
#type_def
<#ty as schemars::JsonSchema>::json_schema(#gen)
}
}); });
} }
@ -365,7 +379,7 @@ fn expr_for_tuple_struct(fields: &[Field]) -> TokenStream {
.iter() .iter()
.filter(|f| !f.serde_attrs.skip_deserializing()) .filter(|f| !f.serde_attrs.skip_deserializing())
.enumerate() .enumerate()
.map(|(i, f)| type_for_schema(f, i)) .map(|(i, f)| type_for_field_schema(f, i))
.unzip(); .unzip();
quote! { quote! {
{ {
@ -411,7 +425,7 @@ fn expr_for_struct(
..SchemaMetadata::from_attrs(&field.attrs) ..SchemaMetadata::from_attrs(&field.attrs)
}; };
let (ty, type_def) = type_for_schema(field, type_defs.len()); let (ty, type_def) = type_for_field_schema(field, type_defs.len());
if let Some(type_def) = type_def { if let Some(type_def) = type_def {
type_defs.push(type_def); type_defs.push(type_def);
} }
@ -427,7 +441,7 @@ fn expr_for_struct(
let flattens: Vec<_> = flattened_fields let flattens: Vec<_> = flattened_fields
.into_iter() .into_iter()
.map(|field| { .map(|field| {
let (ty, type_def) = type_for_schema(field, type_defs.len()); let (ty, type_def) = type_for_field_schema(field, type_defs.len());
if let Some(type_def) = type_def { if let Some(type_def) = type_def {
type_defs.push(type_def); type_defs.push(type_def);
} }