Merge branch 'master' into validate

This commit is contained in:
Graham Esau 2021-04-15 16:03:25 +01:00
commit b68132f17d
31 changed files with 440 additions and 143 deletions

View file

@ -13,6 +13,10 @@ use syn::Meta::{List, NameValue};
use syn::MetaNameValue;
use syn::NestedMeta::{Lit, Meta};
// FIXME using the same struct for containers+variants+fields means that
// with/schema_with are accepted (but ignored) on containers, and
// repr/crate_name are accepted (but ignored) on variants and fields etc.
#[derive(Debug, Default)]
pub struct Attrs {
pub with: Option<WithAttr>,
@ -21,6 +25,7 @@ pub struct Attrs {
pub deprecated: bool,
pub examples: Vec<syn::Path>,
pub repr: Option<syn::Type>,
pub crate_name: Option<syn::Path>,
}
#[derive(Debug)]
@ -125,6 +130,16 @@ impl Attrs {
}
}
Meta(NameValue(m)) if m.path.is_ident("crate") && attr_type == "schemars" => {
if let Ok(p) = parse_lit_into_path(errors, attr_type, "crate", &m.lit) {
if self.crate_name.is_some() {
duplicate_error(m)
} else {
self.crate_name = Some(p)
}
}
}
_ if ignore_errors => {}
Meta(meta_item) => {

View file

@ -125,11 +125,11 @@ impl ValidationAttrs {
// `schema_object` - the SchemaObject for the struct that contains this field.
let mut statements = Vec::new();
if self.required {
statements.push(quote! {
schema_object.object().required.insert(#field_name.to_owned());
});
}
// if self.required {
// statements.push(quote! {
// schema_object.object().required.insert(#field_name.to_owned());
// });
// }
let mut array_validation = Vec::new();
let mut number_validation = Vec::new();

View file

@ -14,6 +14,7 @@ mod schema_exprs;
use ast::*;
use proc_macro2::TokenStream;
use syn::spanned::Spanned;
#[proc_macro_derive(JsonSchema, attributes(schemars, serde, validate))]
pub fn derive_json_schema_wrapper(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
@ -40,14 +41,20 @@ fn derive_json_schema(
attr::process_serde_attrs(&mut input)?;
let cont = Container::from_ast(&input)?;
let crate_alias = cont.attrs.crate_name.as_ref().map(|path| {
quote_spanned! {path.span()=>
use #path as schemars;
}
});
let type_name = &cont.ident;
let (impl_generics, ty_generics, where_clause) = cont.generics.split_for_impl();
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! {
const _: () = {
#crate_alias
#type_def
#[automatically_derived]
@ -64,18 +71,12 @@ fn derive_json_schema(
<#ty as schemars::JsonSchema>::json_schema(gen)
}
fn json_schema_for_flatten(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
<#ty as schemars::JsonSchema>::json_schema_for_flatten(gen)
fn _schemars_private_non_optional_json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
<#ty as schemars::JsonSchema>::_schemars_private_non_optional_json_schema(gen)
}
fn add_schema_as_property(
gen: &mut schemars::gen::SchemaGenerator,
parent: &mut schemars::schema::SchemaObject,
name: String,
metadata: Option<schemars::schema::Metadata>,
required: Option<bool>,
) {
<#ty as schemars::JsonSchema>::add_schema_as_property(gen, parent, name, metadata, required)
fn _schemars_private_is_option() -> bool {
<#ty as schemars::JsonSchema>::_schemars_private_is_option()
}
};
};
@ -122,16 +123,20 @@ fn derive_json_schema(
};
Ok(quote! {
#[automatically_derived]
#[allow(unused_braces)]
impl #impl_generics schemars::JsonSchema for #type_name #ty_generics #where_clause {
fn schema_name() -> std::string::String {
#schema_name
}
const _: () = {
#crate_alias
fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
#schema_expr
}
#[automatically_derived]
#[allow(unused_braces)]
impl #impl_generics schemars::JsonSchema for #type_name #ty_generics #where_clause {
fn schema_name() -> std::string::String {
#schema_name
}
fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
#schema_expr
}
};
};
})
}

View file

@ -49,7 +49,7 @@ impl<'a> SchemaMetadata<'a> {
quote! {
{
let schema = #schema_expr;
gen.apply_metadata(schema, #self)
schemars::_private::apply_metadata(schema, #self)
}
}
}

View file

@ -57,31 +57,38 @@ pub fn expr_for_repr(cont: &Container) -> Result<TokenStream, syn::Error> {
}
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 gen = quote!(gen);
if allow_ref {
quote_spanned! {span=>
{
#type_def
gen.subschema_for::<#ty>()
#gen.subschema_for::<#ty>()
}
}
} else {
quote_spanned! {span=>
{
#type_def
<#ty as schemars::JsonSchema>::json_schema(gen)
<#ty as schemars::JsonSchema>::json_schema(#gen)
}
}
}
}
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 {
None => (field.ty.to_owned(), None),
Some(WithAttr::Type(ty)) => (ty.to_owned(), None),
Some(WithAttr::Function(fun)) => {
Some(with_attr) => type_for_schema(with_attr, local_id),
}
}
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 fn_name = fun.segments.last().unwrap().ident.to_string();
@ -315,9 +322,14 @@ fn expr_for_adjacent_tagged_enum<'a>(
}
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);
return quote_spanned! {variant.original.span()=>
gen.subschema_for::<#with>()
{
#type_def
#gen.subschema_for::<#ty>()
}
};
}
@ -333,9 +345,14 @@ fn expr_for_untagged_enum_variant_for_flatten(
variant: &Variant,
deny_unknown_fields: bool,
) -> 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);
return Some(quote_spanned! {variant.original.span()=>
<#with as schemars::JsonSchema>::json_schema(gen)
{
#type_def
<#ty as schemars::JsonSchema>::json_schema(#gen)
}
});
}
@ -362,7 +379,7 @@ fn expr_for_tuple_struct(fields: &[Field]) -> TokenStream {
.iter()
.filter(|f| !f.serde_attrs.skip_deserializing())
.enumerate()
.map(|(i, f)| type_for_schema(f, i))
.map(|(i, f)| type_for_field_schema(f, i))
.unzip();
quote! {
{
@ -409,20 +426,16 @@ fn expr_for_struct(
..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 {
type_defs.push(type_def);
}
let args = quote!(gen, &mut schema_object, #name.to_owned(), #metadata, #required);
let validation = field.validation_attrs.validation_statements(&name);
quote_spanned! {ty.span()=>
<#ty as schemars::JsonSchema>::add_schema_as_property(
gen,
&mut schema_object,
#name.to_owned(),
#metadata,
#required);
schemars::_private::add_schema_as_property::<#ty>(#args);
#validation
}
})
@ -431,13 +444,14 @@ fn expr_for_struct(
let flattens: Vec<_> = flattened_fields
.into_iter()
.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 {
type_defs.push(type_def);
}
let gen = quote!(gen);
quote_spanned! {ty.span()=>
.flatten(<#ty as schemars::JsonSchema>::json_schema_for_flatten(gen))
.flatten(schemars::_private::json_schema_for_flatten::<#ty>(#gen))
}
})
.collect();