Refactoring

This commit is contained in:
Graham Esau 2021-04-16 16:53:15 +01:00
parent 4be21bd811
commit 5f841f2e5c
4 changed files with 117 additions and 101 deletions

View file

@ -5,6 +5,7 @@ mod validation;
pub use schemars_to_serde::process_serde_attrs; pub use schemars_to_serde::process_serde_attrs;
pub use validation::ValidationAttrs; pub use validation::ValidationAttrs;
use crate::metadata::SchemaMetadata;
use proc_macro2::{Group, Span, TokenStream, TokenTree}; use proc_macro2::{Group, Span, TokenStream, TokenTree};
use quote::ToTokens; use quote::ToTokens;
use serde_derive_internals::Ctxt; use serde_derive_internals::Ctxt;
@ -53,6 +54,27 @@ impl Attrs {
result result
} }
pub fn as_metadata(&self) -> SchemaMetadata<'_> {
#[allow(clippy::ptr_arg)]
fn none_if_empty(s: &String) -> Option<&str> {
if s.is_empty() {
None
} else {
Some(s)
}
}
SchemaMetadata {
title: self.title.as_ref().and_then(none_if_empty),
description: self.description.as_ref().and_then(none_if_empty),
deprecated: self.deprecated,
examples: &self.examples,
read_only: false,
write_only: false,
default: None,
}
}
fn populate( fn populate(
mut self, mut self,
attrs: &[syn::Attribute], attrs: &[syn::Attribute],

View file

@ -120,7 +120,7 @@ impl ValidationAttrs {
self self
} }
pub fn apply_to_schema(&self, schema_expr: TokenStream) -> TokenStream { pub fn apply_to_schema(&self, schema_expr: &mut TokenStream) {
let mut array_validation = Vec::new(); let mut array_validation = Vec::new();
let mut number_validation = Vec::new(); let mut number_validation = Vec::new();
let mut object_validation = Vec::new(); let mut object_validation = Vec::new();
@ -200,7 +200,7 @@ impl ValidationAttrs {
|| string_validation.is_some() || string_validation.is_some()
|| format.is_some() || format.is_some()
{ {
quote! { *schema_expr = quote! {
{ {
let mut schema = #schema_expr; let mut schema = #schema_expr;
if let schemars::schema::Schema::Object(schema_object) = &mut schema if let schemars::schema::Schema::Object(schema_object) = &mut schema
@ -214,8 +214,6 @@ impl ValidationAttrs {
schema schema
} }
} }
} else {
schema_expr
} }
} }
} }

View file

@ -1,5 +1,3 @@
use crate::attr;
use attr::Attrs;
use proc_macro2::TokenStream; use proc_macro2::TokenStream;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -14,24 +12,10 @@ pub struct SchemaMetadata<'a> {
} }
impl<'a> SchemaMetadata<'a> { impl<'a> SchemaMetadata<'a> {
pub fn from_attrs(attrs: &'a Attrs) -> Self { pub fn apply_to_schema(&self, schema_expr: &mut TokenStream) {
SchemaMetadata {
title: attrs.title.as_ref().and_then(none_if_empty),
description: attrs.description.as_ref().and_then(none_if_empty),
deprecated: attrs.deprecated,
examples: &attrs.examples,
read_only: false,
write_only: false,
default: None,
}
}
pub fn apply_to_schema(&self, schema_expr: TokenStream) -> TokenStream {
let setters = self.make_setters(); let setters = self.make_setters();
if setters.is_empty() { if !setters.is_empty() {
schema_expr *schema_expr = quote! {
} else {
quote! {
schemars::_private::apply_metadata(#schema_expr, schemars::schema::Metadata { schemars::_private::apply_metadata(#schema_expr, schemars::schema::Metadata {
#(#setters)* #(#setters)*
..Default::default() ..Default::default()
@ -91,12 +75,3 @@ impl<'a> SchemaMetadata<'a> {
setters setters
} }
} }
#[allow(clippy::ptr_arg)]
fn none_if_empty(s: &String) -> Option<&str> {
if s.is_empty() {
None
} else {
Some(s)
}
}

View file

@ -5,7 +5,7 @@ use serde_derive_internals::attr::{self as serde_attr, Default as SerdeDefault,
use syn::spanned::Spanned; use syn::spanned::Spanned;
pub fn expr_for_container(cont: &Container) -> TokenStream { pub fn expr_for_container(cont: &Container) -> TokenStream {
let schema_expr = match &cont.data { let mut schema_expr = match &cont.data {
Data::Struct(Style::Unit, _) => expr_for_unit_struct(), Data::Struct(Style::Unit, _) => expr_for_unit_struct(),
Data::Struct(Style::Newtype, fields) => expr_for_newtype_struct(&fields[0]), Data::Struct(Style::Newtype, fields) => expr_for_newtype_struct(&fields[0]),
Data::Struct(Style::Tuple, fields) => expr_for_tuple_struct(fields), Data::Struct(Style::Tuple, fields) => expr_for_tuple_struct(fields),
@ -17,8 +17,8 @@ pub fn expr_for_container(cont: &Container) -> TokenStream {
Data::Enum(variants) => expr_for_enum(variants, &cont.serde_attrs), Data::Enum(variants) => expr_for_enum(variants, &cont.serde_attrs),
}; };
let doc_metadata = SchemaMetadata::from_attrs(&cont.attrs); cont.attrs.as_metadata().apply_to_schema(&mut schema_expr);
doc_metadata.apply_to_schema(schema_expr) schema_expr
} }
pub fn expr_for_repr(cont: &Container) -> Result<TokenStream, syn::Error> { pub fn expr_for_repr(cont: &Container) -> Result<TokenStream, syn::Error> {
@ -47,13 +47,13 @@ pub fn expr_for_repr(cont: &Container) -> Result<TokenStream, syn::Error> {
let enum_ident = &cont.ident; let enum_ident = &cont.ident;
let variant_idents = variants.iter().map(|v| &v.ident); let variant_idents = variants.iter().map(|v| &v.ident);
let schema_expr = schema_object(quote! { let mut schema_expr = schema_object(quote! {
instance_type: Some(schemars::schema::InstanceType::Integer.into()), instance_type: Some(schemars::schema::InstanceType::Integer.into()),
enum_values: Some(vec![#((#enum_ident::#variant_idents as #repr_type).into()),*]), enum_values: Some(vec![#((#enum_ident::#variant_idents as #repr_type).into()),*]),
}); });
let doc_metadata = SchemaMetadata::from_attrs(&cont.attrs); cont.attrs.as_metadata().apply_to_schema(&mut schema_expr);
Ok(doc_metadata.apply_to_schema(schema_expr)) Ok(schema_expr)
} }
fn expr_for_field(field: &Field, allow_ref: bool) -> TokenStream { fn expr_for_field(field: &Field, allow_ref: bool) -> TokenStream {
@ -61,29 +61,24 @@ fn expr_for_field(field: &Field, allow_ref: bool) -> TokenStream {
let span = field.original.span(); let span = field.original.span();
let gen = quote!(gen); let gen = quote!(gen);
let schema_expr = if field.validation_attrs.required { let mut schema_expr = if field.validation_attrs.required {
quote_spanned! {span=> quote_spanned! {span=>
{ <#ty as schemars::JsonSchema>::_schemars_private_non_optional_json_schema(#gen)
#type_def
<#ty as schemars::JsonSchema>::_schemars_private_non_optional_json_schema(#gen)
}
} }
} else if allow_ref { } else if allow_ref {
quote_spanned! {span=> quote_spanned! {span=>
{ #gen.subschema_for::<#ty>()
#type_def
#gen.subschema_for::<#ty>()
}
} }
} else { } else {
quote_spanned! {span=> quote_spanned! {span=>
{ <#ty as schemars::JsonSchema>::json_schema(#gen)
#type_def
<#ty as schemars::JsonSchema>::json_schema(#gen)
}
} }
}; };
field.validation_attrs.apply_to_schema(schema_expr)
prepend_type_def(type_def, &mut schema_expr);
field.validation_attrs.apply_to_schema(&mut schema_expr);
schema_expr
} }
pub fn type_for_field_schema(field: &Field) -> (syn::Type, Option<TokenStream>) { pub fn type_for_field_schema(field: &Field) -> (syn::Type, Option<TokenStream>) {
@ -167,7 +162,7 @@ fn expr_for_external_tagged_enum<'a>(
let name = variant.name(); let name = variant.name();
let sub_schema = expr_for_untagged_enum_variant(variant, deny_unknown_fields); let sub_schema = expr_for_untagged_enum_variant(variant, deny_unknown_fields);
let schema_expr = schema_object(quote! { let mut schema_expr = schema_object(quote! {
instance_type: Some(schemars::schema::InstanceType::Object.into()), instance_type: Some(schemars::schema::InstanceType::Object.into()),
object: Some(Box::new(schemars::schema::ObjectValidation { object: Some(Box::new(schemars::schema::ObjectValidation {
properties: { properties: {
@ -184,8 +179,13 @@ fn expr_for_external_tagged_enum<'a>(
..Default::default() ..Default::default()
})), })),
}); });
let doc_metadata = SchemaMetadata::from_attrs(&variant.attrs);
doc_metadata.apply_to_schema(schema_expr) variant
.attrs
.as_metadata()
.apply_to_schema(&mut schema_expr);
schema_expr
})); }));
schema_object(quote! { schema_object(quote! {
@ -208,7 +208,7 @@ fn expr_for_internal_tagged_enum<'a>(
enum_values: Some(vec![#name.into()]), enum_values: Some(vec![#name.into()]),
}); });
let tag_schema = schema_object(quote! { let mut tag_schema = schema_object(quote! {
instance_type: Some(schemars::schema::InstanceType::Object.into()), instance_type: Some(schemars::schema::InstanceType::Object.into()),
object: Some(Box::new(schemars::schema::ObjectValidation { object: Some(Box::new(schemars::schema::ObjectValidation {
properties: { properties: {
@ -224,15 +224,16 @@ fn expr_for_internal_tagged_enum<'a>(
..Default::default() ..Default::default()
})), })),
}); });
let doc_metadata = SchemaMetadata::from_attrs(&variant.attrs);
let tag_schema = doc_metadata.apply_to_schema(tag_schema);
match expr_for_untagged_enum_variant_for_flatten(&variant, deny_unknown_fields) { variant.attrs.as_metadata().apply_to_schema(&mut tag_schema);
Some(variant_schema) => quote! {
#tag_schema.flatten(#variant_schema) if let Some(variant_schema) =
}, expr_for_untagged_enum_variant_for_flatten(&variant, deny_unknown_fields)
None => tag_schema, {
tag_schema.extend(quote!(.flatten(#variant_schema)))
} }
tag_schema
}); });
schema_object(quote! { schema_object(quote! {
@ -248,9 +249,14 @@ fn expr_for_untagged_enum<'a>(
deny_unknown_fields: bool, deny_unknown_fields: bool,
) -> TokenStream { ) -> TokenStream {
let schemas = variants.map(|variant| { let schemas = variants.map(|variant| {
let schema_expr = expr_for_untagged_enum_variant(variant, deny_unknown_fields); let mut schema_expr = expr_for_untagged_enum_variant(variant, deny_unknown_fields);
let doc_metadata = SchemaMetadata::from_attrs(&variant.attrs);
doc_metadata.apply_to_schema(schema_expr) variant
.attrs
.as_metadata()
.apply_to_schema(&mut schema_expr);
schema_expr
}); });
schema_object(quote! { schema_object(quote! {
@ -297,7 +303,7 @@ fn expr_for_adjacent_tagged_enum<'a>(
TokenStream::new() TokenStream::new()
}; };
let outer_schema = schema_object(quote! { let mut outer_schema = schema_object(quote! {
instance_type: Some(schemars::schema::InstanceType::Object.into()), instance_type: Some(schemars::schema::InstanceType::Object.into()),
object: Some(Box::new(schemars::schema::ObjectValidation { object: Some(Box::new(schemars::schema::ObjectValidation {
properties: { properties: {
@ -317,8 +323,12 @@ fn expr_for_adjacent_tagged_enum<'a>(
})), })),
}); });
let doc_metadata = SchemaMetadata::from_attrs(&variant.attrs); variant
doc_metadata.apply_to_schema(outer_schema) .attrs
.as_metadata()
.apply_to_schema(&mut outer_schema);
outer_schema
}); });
schema_object(quote! { schema_object(quote! {
@ -333,12 +343,12 @@ fn expr_for_untagged_enum_variant(variant: &Variant, deny_unknown_fields: bool)
if let Some(with_attr) = &variant.attrs.with { if let Some(with_attr) = &variant.attrs.with {
let (ty, type_def) = type_for_schema(with_attr); let (ty, type_def) = type_for_schema(with_attr);
let gen = quote!(gen); let gen = quote!(gen);
return quote_spanned! {variant.original.span()=> let mut schema_expr = quote_spanned! {variant.original.span()=>
{ #gen.subschema_for::<#ty>()
#type_def
#gen.subschema_for::<#ty>()
}
}; };
prepend_type_def(type_def, &mut schema_expr);
return schema_expr;
} }
match variant.style { match variant.style {
@ -356,12 +366,12 @@ fn expr_for_untagged_enum_variant_for_flatten(
if let Some(with_attr) = &variant.attrs.with { if let Some(with_attr) = &variant.attrs.with {
let (ty, type_def) = type_for_schema(with_attr); let (ty, type_def) = type_for_schema(with_attr);
let gen = quote!(gen); let gen = quote!(gen);
return Some(quote_spanned! {variant.original.span()=> let mut schema_expr = quote_spanned! {variant.original.span()=>
{ <#ty as schemars::JsonSchema>::json_schema(#gen)
#type_def };
<#ty as schemars::JsonSchema>::json_schema(#gen)
} prepend_type_def(type_def, &mut schema_expr);
}); return Some(schema_expr);
} }
Some(match variant.style { Some(match variant.style {
@ -429,17 +439,6 @@ fn expr_for_struct(
let (ty, type_def) = type_for_field_schema(field); let (ty, type_def) = type_for_field_schema(field);
let gen = quote!(gen);
let schema_expr = if field.validation_attrs.required {
quote_spanned! {ty.span()=>
<#ty as schemars::JsonSchema>::_schemars_private_non_optional_json_schema(#gen)
}
} else {
quote_spanned! {ty.span()=>
#gen.subschema_for::<#ty>()
}
};
let maybe_insert_required = match (&default, field.validation_attrs.required) { let maybe_insert_required = match (&default, field.validation_attrs.required) {
(Some(_), _) => TokenStream::new(), (Some(_), _) => TokenStream::new(),
(None, false) => { (None, false) => {
@ -458,11 +457,22 @@ fn expr_for_struct(
read_only: field.serde_attrs.skip_deserializing(), read_only: field.serde_attrs.skip_deserializing(),
write_only: field.serde_attrs.skip_serializing(), write_only: field.serde_attrs.skip_serializing(),
default, default,
..SchemaMetadata::from_attrs(&field.attrs) ..field.attrs.as_metadata()
}; };
let schema_expr = metadata.apply_to_schema(schema_expr); let gen = quote!(gen);
let schema_expr = field.validation_attrs.apply_to_schema(schema_expr); let mut schema_expr = if field.validation_attrs.required {
quote_spanned! {ty.span()=>
<#ty as schemars::JsonSchema>::_schemars_private_non_optional_json_schema(#gen)
}
} else {
quote_spanned! {ty.span()=>
#gen.subschema_for::<#ty>()
}
};
metadata.apply_to_schema(&mut schema_expr);
field.validation_attrs.apply_to_schema(&mut schema_expr);
quote! { quote! {
{ {
@ -486,12 +496,12 @@ fn expr_for_struct(
}; };
let args = quote!(gen, #required); let args = quote!(gen, #required);
quote_spanned! {ty.span()=> let mut schema_expr = quote_spanned! {ty.span()=>
.flatten({ schemars::_private::json_schema_for_flatten::<#ty>(#args)
#type_def };
schemars::_private::json_schema_for_flatten::<#ty>(#args)
}) prepend_type_def(type_def, &mut schema_expr);
} schema_expr
}) })
.collect(); .collect();
@ -513,7 +523,7 @@ fn expr_for_struct(
#set_additional_properties #set_additional_properties
#(#properties)* #(#properties)*
schemars::schema::Schema::Object(schema_object) schemars::schema::Schema::Object(schema_object)
#(#flattens)* #(.flatten(#flattens))*
} }
} }
} }
@ -582,3 +592,14 @@ fn schema_object(properties: TokenStream) -> TokenStream {
}) })
} }
} }
fn prepend_type_def(type_def: Option<TokenStream>, schema_expr: &mut TokenStream) {
if let Some(type_def) = type_def {
*schema_expr = quote! {
{
#type_def
#schema_expr
}
}
}
}