Refactor out add_schema_as_property
This commit is contained in:
parent
1a2dafc1a5
commit
60a9869448
6 changed files with 89 additions and 101 deletions
|
@ -24,40 +24,12 @@ pub fn json_schema_for_flatten<T: ?Sized + JsonSchema>(
|
||||||
schema
|
schema
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper for generating schemas for `Option` fields.
|
pub fn apply_metadata(schema: Schema, metadata: Metadata) -> Schema {
|
||||||
pub fn add_schema_as_property<T: ?Sized + JsonSchema>(
|
if metadata == Metadata::default() {
|
||||||
gen: &mut SchemaGenerator,
|
schema
|
||||||
parent: &mut SchemaObject,
|
|
||||||
name: String,
|
|
||||||
metadata: Option<Metadata>,
|
|
||||||
required: Option<bool>,
|
|
||||||
) {
|
|
||||||
let is_type_option = T::_schemars_private_is_option();
|
|
||||||
let required = required.unwrap_or(!is_type_option);
|
|
||||||
|
|
||||||
let mut schema = if required && is_type_option {
|
|
||||||
T::_schemars_private_non_optional_json_schema(gen)
|
|
||||||
} else {
|
} else {
|
||||||
gen.subschema_for::<T>()
|
|
||||||
};
|
|
||||||
|
|
||||||
schema = apply_metadata(schema, metadata);
|
|
||||||
|
|
||||||
let object = parent.object();
|
|
||||||
if required {
|
|
||||||
object.required.insert(name.clone());
|
|
||||||
}
|
|
||||||
object.properties.insert(name, schema);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn apply_metadata(schema: Schema, metadata: Option<Metadata>) -> Schema {
|
|
||||||
match metadata {
|
|
||||||
None => schema,
|
|
||||||
Some(ref metadata) if *metadata == Metadata::default() => schema,
|
|
||||||
Some(metadata) => {
|
|
||||||
let mut schema_obj = schema.into_object();
|
let mut schema_obj = schema.into_object();
|
||||||
schema_obj.metadata = Some(Box::new(metadata)).merge(schema_obj.metadata);
|
schema_obj.metadata = Some(Box::new(metadata)).merge(schema_obj.metadata);
|
||||||
Schema::Object(schema_obj)
|
Schema::Object(schema_obj)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,16 @@
|
||||||
],
|
],
|
||||||
"definitions": {
|
"definitions": {
|
||||||
"InnerStruct": {
|
"InnerStruct": {
|
||||||
"type": "object"
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"x"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"x": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int32"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -56,7 +56,9 @@ build_enum!(
|
||||||
#[derive(Debug, JsonSchema)]
|
#[derive(Debug, JsonSchema)]
|
||||||
OuterEnum {
|
OuterEnum {
|
||||||
#[derive(Debug, JsonSchema)]
|
#[derive(Debug, JsonSchema)]
|
||||||
InnerStruct {}
|
InnerStruct {
|
||||||
|
x: i32
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
|
@ -120,9 +120,7 @@ impl ValidationAttrs {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn validation_statements(&self, field_name: &str) -> Option<TokenStream> {
|
pub fn apply_to_schema(&self, schema_expr: TokenStream) -> TokenStream {
|
||||||
// Assume that the result will be interpolated in a context with the local variable
|
|
||||||
// `schema_object` - the SchemaObject for the struct that contains this field.
|
|
||||||
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();
|
||||||
|
@ -187,7 +185,7 @@ impl ValidationAttrs {
|
||||||
|
|
||||||
let format = self.format.as_ref().map(|f| {
|
let format = self.format.as_ref().map(|f| {
|
||||||
quote! {
|
quote! {
|
||||||
prop_schema_object.format = Some(#f.to_string());
|
schema_object.format = Some(#f.to_string());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -202,11 +200,10 @@ impl ValidationAttrs {
|
||||||
|| string_validation.is_some()
|
|| string_validation.is_some()
|
||||||
|| format.is_some()
|
|| format.is_some()
|
||||||
{
|
{
|
||||||
Some(quote! {
|
quote! {
|
||||||
if let Some(schemars::schema::Schema::Object(prop_schema_object)) = schema_object
|
{
|
||||||
.object
|
let mut schema = #schema_expr;
|
||||||
.as_mut()
|
if let schemars::schema::Schema::Object(schema_object) = &mut schema
|
||||||
.and_then(|o| o.properties.get_mut(#field_name))
|
|
||||||
{
|
{
|
||||||
#array_validation
|
#array_validation
|
||||||
#number_validation
|
#number_validation
|
||||||
|
@ -214,9 +211,11 @@ impl ValidationAttrs {
|
||||||
#string_validation
|
#string_validation
|
||||||
#format
|
#format
|
||||||
}
|
}
|
||||||
})
|
schema
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
None
|
schema_expr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -226,8 +225,8 @@ fn wrap_array_validation(v: Vec<TokenStream>) -> Option<TokenStream> {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(quote! {
|
Some(quote! {
|
||||||
if prop_schema_object.has_type(schemars::schema::InstanceType::Array) {
|
if schema_object.has_type(schemars::schema::InstanceType::Array) {
|
||||||
let validation = prop_schema_object.array();
|
let validation = schema_object.array();
|
||||||
#(#v)*
|
#(#v)*
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -239,9 +238,9 @@ fn wrap_number_validation(v: Vec<TokenStream>) -> Option<TokenStream> {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(quote! {
|
Some(quote! {
|
||||||
if prop_schema_object.has_type(schemars::schema::InstanceType::Integer)
|
if schema_object.has_type(schemars::schema::InstanceType::Integer)
|
||||||
|| prop_schema_object.has_type(schemars::schema::InstanceType::Number) {
|
|| schema_object.has_type(schemars::schema::InstanceType::Number) {
|
||||||
let validation = prop_schema_object.number();
|
let validation = schema_object.number();
|
||||||
#(#v)*
|
#(#v)*
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -253,8 +252,8 @@ fn wrap_object_validation(v: Vec<TokenStream>) -> Option<TokenStream> {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(quote! {
|
Some(quote! {
|
||||||
if prop_schema_object.has_type(schemars::schema::InstanceType::Object) {
|
if schema_object.has_type(schemars::schema::InstanceType::Object) {
|
||||||
let validation = prop_schema_object.object();
|
let validation = schema_object.object();
|
||||||
#(#v)*
|
#(#v)*
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -266,8 +265,8 @@ fn wrap_string_validation(v: Vec<TokenStream>) -> Option<TokenStream> {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(quote! {
|
Some(quote! {
|
||||||
if prop_schema_object.has_type(schemars::schema::InstanceType::String) {
|
if schema_object.has_type(schemars::schema::InstanceType::String) {
|
||||||
let validation = prop_schema_object.string();
|
let validation = schema_object.string();
|
||||||
#(#v)*
|
#(#v)*
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use crate::attr;
|
use crate::attr;
|
||||||
use attr::Attrs;
|
use attr::Attrs;
|
||||||
use proc_macro2::{Ident, Span, TokenStream};
|
use proc_macro2::TokenStream;
|
||||||
use quote::{ToTokens, TokenStreamExt};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct SchemaMetadata<'a> {
|
pub struct SchemaMetadata<'a> {
|
||||||
|
@ -14,24 +13,6 @@ pub struct SchemaMetadata<'a> {
|
||||||
pub default: Option<TokenStream>,
|
pub default: Option<TokenStream>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToTokens for SchemaMetadata<'_> {
|
|
||||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
||||||
let setters = self.make_setters();
|
|
||||||
if setters.is_empty() {
|
|
||||||
tokens.append(Ident::new("None", Span::call_site()))
|
|
||||||
} else {
|
|
||||||
tokens.extend(quote! {
|
|
||||||
Some({
|
|
||||||
schemars::schema::Metadata {
|
|
||||||
#(#setters)*
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> SchemaMetadata<'a> {
|
impl<'a> SchemaMetadata<'a> {
|
||||||
pub fn from_attrs(attrs: &'a Attrs) -> Self {
|
pub fn from_attrs(attrs: &'a Attrs) -> Self {
|
||||||
SchemaMetadata {
|
SchemaMetadata {
|
||||||
|
@ -46,10 +27,15 @@ impl<'a> SchemaMetadata<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn apply_to_schema(&self, schema_expr: TokenStream) -> TokenStream {
|
pub fn apply_to_schema(&self, schema_expr: TokenStream) -> TokenStream {
|
||||||
|
let setters = self.make_setters();
|
||||||
|
if setters.is_empty() {
|
||||||
|
schema_expr
|
||||||
|
} else {
|
||||||
quote! {
|
quote! {
|
||||||
{
|
schemars::_private::apply_metadata(#schema_expr, schemars::schema::Metadata {
|
||||||
let schema = #schema_expr;
|
#(#setters)*
|
||||||
schemars::_private::apply_metadata(schema, #self)
|
..Default::default()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -413,30 +413,49 @@ fn expr_for_struct(
|
||||||
let name = field.name();
|
let name = field.name();
|
||||||
let default = field_default_expr(field, set_container_default.is_some());
|
let default = field_default_expr(field, set_container_default.is_some());
|
||||||
|
|
||||||
let required = match (&default, field.validation_attrs.required) {
|
let (ty, type_def) = type_for_field_schema(field, type_defs.len());
|
||||||
(Some(_), _) => quote!(Some(false)),
|
if let Some(type_def) = type_def {
|
||||||
(None, false) => quote!(None),
|
type_defs.push(type_def);
|
||||||
(None, true) => quote!(Some(true)),
|
}
|
||||||
|
|
||||||
|
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 metadata = &SchemaMetadata {
|
let maybe_insert_required = match (&default, field.validation_attrs.required) {
|
||||||
|
(Some(_), _) => TokenStream::new(),
|
||||||
|
(None, false) => {
|
||||||
|
quote! {
|
||||||
|
if !<#ty as schemars::JsonSchema>::_schemars_private_is_option() {
|
||||||
|
object_validation.required.insert(#name.to_owned());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(None, true) => quote! {
|
||||||
|
object_validation.required.insert(#name.to_owned());
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let metadata = SchemaMetadata {
|
||||||
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)
|
..SchemaMetadata::from_attrs(&field.attrs)
|
||||||
};
|
};
|
||||||
|
|
||||||
let (ty, type_def) = type_for_field_schema(field, type_defs.len());
|
let schema_expr = metadata.apply_to_schema(schema_expr);
|
||||||
if let Some(type_def) = type_def {
|
let schema_expr = field.validation_attrs.apply_to_schema(schema_expr);
|
||||||
type_defs.push(type_def);
|
|
||||||
}
|
|
||||||
|
|
||||||
let args = quote!(gen, &mut schema_object, #name.to_owned(), #metadata, #required);
|
quote! {
|
||||||
let validation = field.validation_attrs.validation_statements(&name);
|
object_validation.properties.insert(#name.to_owned(), #schema_expr);
|
||||||
|
#maybe_insert_required
|
||||||
quote_spanned! {ty.span()=>
|
|
||||||
schemars::_private::add_schema_as_property::<#ty>(#args);
|
|
||||||
#validation
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
@ -464,7 +483,7 @@ fn expr_for_struct(
|
||||||
|
|
||||||
let set_additional_properties = if deny_unknown_fields {
|
let set_additional_properties = if deny_unknown_fields {
|
||||||
quote! {
|
quote! {
|
||||||
schema_object.object().additional_properties = Some(Box::new(false.into()));
|
object_validation.additional_properties = Some(Box::new(false.into()));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
TokenStream::new()
|
TokenStream::new()
|
||||||
|
@ -477,6 +496,7 @@ fn expr_for_struct(
|
||||||
instance_type: Some(schemars::schema::InstanceType::Object.into()),
|
instance_type: Some(schemars::schema::InstanceType::Object.into()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
let object_validation = schema_object.object();
|
||||||
#set_additional_properties
|
#set_additional_properties
|
||||||
#(#properties)*
|
#(#properties)*
|
||||||
schemars::schema::Schema::Object(schema_object)
|
schemars::schema::Schema::Object(schema_object)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue