Add transform = ... attribute (#312)

This allows running arbitrary transforms on generated schemas when deriving `JsonSchema`
This commit is contained in:
Graham Esau 2024-08-10 09:56:52 +01:00 committed by GitHub
parent 29067a0331
commit 14b06e71ba
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 173 additions and 10 deletions

View file

@ -27,6 +27,7 @@ pub struct Attrs {
pub crate_name: Option<syn::Path>,
pub is_renamed: bool,
pub extensions: Vec<(String, TokenStream)>,
pub transforms: Vec<syn::Expr>,
}
#[derive(Debug)]
@ -70,6 +71,7 @@ impl Attrs {
deprecated: self.deprecated,
examples: &self.examples,
extensions: &self.extensions,
transforms: &self.transforms,
read_only: false,
write_only: false,
default: None,
@ -164,6 +166,25 @@ impl Attrs {
}
}
Meta::NameValue(m) if m.path.is_ident("transform") && attr_type == "schemars" => {
if let syn::Expr::Lit(syn::ExprLit {
lit: syn::Lit::Str(lit_str),
..
}) = &m.value
{
if parse_lit_str::<syn::Expr>(lit_str).is_ok() {
errors.error_spanned_by(
&m.value,
format!(
"Expected a `fn(&mut Schema)` or other value implementing `schemars::transform::Transform`, found `&str`.\nDid you mean `[schemars(transform = {})]`?",
lit_str.value()
),
)
}
}
self.transforms.push(m.value.clone());
}
Meta::List(m) if m.path.is_ident("extend") && attr_type == "schemars" => {
let parser =
syn::punctuated::Punctuated::<Extension, Token![,]>::parse_terminated;
@ -224,7 +245,8 @@ impl Attrs {
crate_name: None,
is_renamed: _,
extensions,
} if examples.is_empty() && extensions.is_empty())
transforms
} if examples.is_empty() && extensions.is_empty() && transforms.is_empty())
}
}

View file

@ -1,4 +1,5 @@
use proc_macro2::TokenStream;
use syn::spanned::Spanned;
#[derive(Debug, Clone)]
pub struct SchemaMetadata<'a> {
@ -10,6 +11,7 @@ pub struct SchemaMetadata<'a> {
pub examples: &'a [syn::Path],
pub default: Option<TokenStream>,
pub extensions: &'a [(String, TokenStream)],
pub transforms: &'a [syn::Expr],
}
impl<'a> SchemaMetadata<'a> {
@ -23,6 +25,18 @@ impl<'a> SchemaMetadata<'a> {
schema
}}
}
if !self.transforms.is_empty() {
let apply_transforms = self.transforms.iter().map(|t| {
quote_spanned! {t.span()=>
schemars::transform::Transform::transform(&mut #t, &mut schema);
}
});
*schema_expr = quote! {{
let mut schema = #schema_expr;
#(#apply_transforms)*
schema
}};
}
}
fn make_setters(&self) -> Vec<TokenStream> {