Add #[schemars(extend("key" = value))]
attribute (#297)
This commit is contained in:
parent
97b70aa82c
commit
840315b2dd
18 changed files with 527 additions and 26 deletions
|
@ -10,7 +10,7 @@ use proc_macro2::{Group, Span, TokenStream, TokenTree};
|
|||
use quote::ToTokens;
|
||||
use serde_derive_internals::Ctxt;
|
||||
use syn::parse::{self, Parse};
|
||||
use syn::{Meta, MetaNameValue};
|
||||
use syn::{LitStr, Meta, MetaNameValue};
|
||||
|
||||
// FIXME using the same struct for containers+variants+fields means that
|
||||
// with/schema_with are accepted (but ignored) on containers, and
|
||||
|
@ -26,6 +26,7 @@ pub struct Attrs {
|
|||
pub repr: Option<syn::Type>,
|
||||
pub crate_name: Option<syn::Path>,
|
||||
pub is_renamed: bool,
|
||||
pub extensions: Vec<(String, TokenStream)>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -68,6 +69,7 @@ impl Attrs {
|
|||
description: self.description.as_ref().and_then(none_if_empty),
|
||||
deprecated: self.deprecated,
|
||||
examples: &self.examples,
|
||||
extensions: &self.extensions,
|
||||
read_only: false,
|
||||
write_only: false,
|
||||
default: None,
|
||||
|
@ -162,6 +164,29 @@ impl Attrs {
|
|||
}
|
||||
}
|
||||
|
||||
Meta::List(m) if m.path.is_ident("extend") && attr_type == "schemars" => {
|
||||
let parser =
|
||||
syn::punctuated::Punctuated::<Extension, Token![,]>::parse_terminated;
|
||||
match m.parse_args_with(parser) {
|
||||
Ok(extensions) => {
|
||||
for extension in extensions {
|
||||
let key = extension.key.value();
|
||||
// This is O(n^2) but should be fine with the typically small number of extensions.
|
||||
// If this does become a problem, it can be changed to use IndexMap, or a separate Map with cloned keys.
|
||||
if self.extensions.iter().any(|e| e.0 == key) {
|
||||
errors.error_spanned_by(
|
||||
extension.key,
|
||||
format!("Duplicate extension key '{}'", key),
|
||||
);
|
||||
} else {
|
||||
self.extensions.push((key, extension.value));
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(err) => errors.syn_error(err),
|
||||
}
|
||||
}
|
||||
|
||||
_ if ignore_errors => {}
|
||||
|
||||
Meta::List(m) if m.path.is_ident("inner") && attr_type == "schemars" => {
|
||||
|
@ -198,7 +223,8 @@ impl Attrs {
|
|||
repr: None,
|
||||
crate_name: None,
|
||||
is_renamed: _,
|
||||
} if examples.is_empty())
|
||||
extensions,
|
||||
} if examples.is_empty() && extensions.is_empty())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -322,3 +348,27 @@ fn respan_token_tree(mut token: TokenTree, span: Span) -> TokenTree {
|
|||
token.set_span(span);
|
||||
token
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Extension {
|
||||
key: LitStr,
|
||||
value: TokenStream,
|
||||
}
|
||||
|
||||
impl Parse for Extension {
|
||||
fn parse(input: parse::ParseStream) -> syn::Result<Self> {
|
||||
let key = input.parse::<LitStr>()?;
|
||||
input.parse::<Token![=]>()?;
|
||||
let mut value = TokenStream::new();
|
||||
|
||||
while !input.is_empty() && !input.peek(Token![,]) {
|
||||
value.extend([input.parse::<TokenTree>()?]);
|
||||
}
|
||||
|
||||
if value.is_empty() {
|
||||
return Err(syn::Error::new(input.span(), "Expected extension value"));
|
||||
}
|
||||
|
||||
Ok(Extension { key, value })
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ pub struct SchemaMetadata<'a> {
|
|||
pub write_only: bool,
|
||||
pub examples: &'a [syn::Path],
|
||||
pub default: Option<TokenStream>,
|
||||
pub extensions: &'a [(String, TokenStream)],
|
||||
}
|
||||
|
||||
impl<'a> SchemaMetadata<'a> {
|
||||
|
@ -74,6 +75,12 @@ impl<'a> SchemaMetadata<'a> {
|
|||
});
|
||||
}
|
||||
|
||||
for (k, v) in self.extensions {
|
||||
setters.push(quote! {
|
||||
obj.insert(#k.to_owned(), schemars::_serde_json::json!(#v));
|
||||
});
|
||||
}
|
||||
|
||||
setters
|
||||
}
|
||||
}
|
||||
|
|
|
@ -232,13 +232,13 @@ fn expr_for_internal_tagged_enum<'a>(
|
|||
let name = variant.name();
|
||||
|
||||
let mut schema_expr = expr_for_internal_tagged_enum_variant(variant, deny_unknown_fields);
|
||||
variant.attrs.as_metadata().apply_to_schema(&mut schema_expr);
|
||||
|
||||
quote!({
|
||||
schema_expr = quote!({
|
||||
let mut schema = #schema_expr;
|
||||
schemars::_private::apply_internal_enum_variant_tag(&mut schema, #tag_name, #name, #deny_unknown_fields);
|
||||
schema
|
||||
})
|
||||
});
|
||||
variant.attrs.as_metadata().apply_to_schema(&mut schema_expr);
|
||||
schema_expr
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue