Make Option<T> fields optional in generated schemas (#16)

This commit is contained in:
Graham Esau 2020-02-29 19:37:20 +00:00 committed by GitHub
parent 60284fdf93
commit 4ad5000232
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 269 additions and 148 deletions

View file

@ -1,5 +1,6 @@
use crate::attr;
use proc_macro2::TokenStream;
use proc_macro2::{Ident, Span, TokenStream};
use quote::{ToTokens, TokenStreamExt};
use syn::{Attribute, ExprPath};
#[derive(Debug, Clone, Default)]
@ -12,74 +13,91 @@ pub struct SchemaMetadata {
pub skip_default_if: Option<ExprPath>,
}
pub fn set_metadata_on_schema_from_docs(
schema_expr: TokenStream,
attrs: &[Attribute],
) -> TokenStream {
let metadata = get_metadata_from_docs(attrs);
set_metadata_on_schema(schema_expr, &metadata)
}
pub fn get_metadata_from_docs(attrs: &[Attribute]) -> SchemaMetadata {
let (title, description) = attr::get_title_and_desc_from_doc(attrs);
SchemaMetadata {
title,
description,
..Default::default()
}
}
pub fn set_metadata_on_schema(schema_expr: TokenStream, metadata: &SchemaMetadata) -> TokenStream {
let mut setters = Vec::<TokenStream>::new();
if let Some(title) = &metadata.title {
setters.push(quote! {
metadata.title = Some(#title.to_owned());
});
}
if let Some(description) = &metadata.description {
setters.push(quote! {
metadata.description = Some(#description.to_owned());
});
}
if metadata.read_only {
setters.push(quote! {
metadata.read_only = true;
});
}
if metadata.write_only {
setters.push(quote! {
metadata.write_only = true;
});
}
match (&metadata.default, &metadata.skip_default_if) {
(Some(default), Some(skip_if)) => setters.push(quote! {
{
let default = #default;
if !#skip_if(&default) {
metadata.default = schemars::_serde_json::value::to_value(default).ok();
}
}
}),
(Some(default), None) => setters.push(quote! {
metadata.default = schemars::_serde_json::value::to_value(#default).ok();
}),
_ => {}
}
if setters.is_empty() {
return schema_expr;
}
quote! {
{
let schema = #schema_expr.into();
let mut schema_obj = gen.make_extensible(schema);
let mut metadata = schema_obj.metadata();
#(#setters)*
schemars::schema::Schema::Object(schema_obj)
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({
let mut metadata = schemars::schema::Metadata::default();
#(#setters)*
metadata
})
})
}
}
}
impl SchemaMetadata {
pub fn from_doc_attrs(attrs: &[Attribute]) -> SchemaMetadata {
let (title, description) = attr::get_title_and_desc_from_doc(attrs);
SchemaMetadata {
title,
description,
..Default::default()
}
}
pub fn apply_to_schema(&self, schema_expr: TokenStream) -> TokenStream {
let setters = self.make_setters();
if setters.is_empty() {
return schema_expr;
}
quote! {
{
let mut schema = #schema_expr.into();
gen.make_extensible(&mut schema);
let mut metadata = schema.metadata();
#(#setters)*
schemars::schema::Schema::Object(schema)
}
}
}
fn make_setters(self: &SchemaMetadata) -> Vec<TokenStream> {
let mut setters = Vec::<TokenStream>::new();
if let Some(title) = &self.title {
setters.push(quote! {
metadata.title = Some(#title.to_owned());
});
}
if let Some(description) = &self.description {
setters.push(quote! {
metadata.description = Some(#description.to_owned());
});
}
if self.read_only {
setters.push(quote! {
metadata.read_only = true;
});
}
if self.write_only {
setters.push(quote! {
metadata.write_only = true;
});
}
match (&self.default, &self.skip_default_if) {
(Some(default), Some(skip_if)) => setters.push(quote! {
{
let default = #default;
if !#skip_if(&default) {
metadata.default = schemars::_serde_json::value::to_value(default).ok();
}
}
}),
(Some(default), None) => setters.push(quote! {
metadata.default = schemars::_serde_json::value::to_value(#default).ok();
}),
_ => {}
}
setters
}
}