schemars_derive/
lib.rs
1#![forbid(unsafe_code)]
2
3#[macro_use]
4extern crate quote;
5#[macro_use]
6extern crate syn;
7extern crate proc_macro;
8
9mod ast;
10mod attr;
11mod metadata;
12mod regex_syntax;
13mod schema_exprs;
14
15use ast::*;
16use proc_macro2::TokenStream;
17use syn::spanned::Spanned;
18
19#[proc_macro_derive(JsonSchema, attributes(schemars, serde, validate))]
20pub fn derive_json_schema_wrapper(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
21 let input = parse_macro_input!(input as syn::DeriveInput);
22 derive_json_schema(input, false)
23 .unwrap_or_else(syn::Error::into_compile_error)
24 .into()
25}
26
27#[proc_macro_derive(JsonSchema_repr, attributes(schemars, serde))]
28pub fn derive_json_schema_repr_wrapper(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
29 let input = parse_macro_input!(input as syn::DeriveInput);
30 derive_json_schema(input, true)
31 .unwrap_or_else(syn::Error::into_compile_error)
32 .into()
33}
34
35fn derive_json_schema(mut input: syn::DeriveInput, repr: bool) -> syn::Result<TokenStream> {
36 attr::process_serde_attrs(&mut input)?;
37
38 let mut cont = Container::from_ast(&input)?;
39 add_trait_bounds(&mut cont);
40
41 let crate_alias = cont.attrs.crate_name.as_ref().map(|path| {
42 quote_spanned! {path.span()=>
43 use #path as schemars;
44 }
45 });
46
47 let type_name = &cont.ident;
48 let (impl_generics, ty_generics, where_clause) = cont.generics.split_for_impl();
49
50 if let Some(transparent_field) = cont.transparent_field() {
51 let (ty, type_def) = schema_exprs::type_for_field_schema(transparent_field);
52 return Ok(quote! {
53 const _: () = {
54 #crate_alias
55 #type_def
56
57 #[automatically_derived]
58 impl #impl_generics schemars::JsonSchema for #type_name #ty_generics #where_clause {
59 fn is_referenceable() -> bool {
60 <#ty as schemars::JsonSchema>::is_referenceable()
61 }
62
63 fn schema_name() -> std::string::String {
64 <#ty as schemars::JsonSchema>::schema_name()
65 }
66
67 fn schema_id() -> std::borrow::Cow<'static, str> {
68 <#ty as schemars::JsonSchema>::schema_id()
69 }
70
71 fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
72 <#ty as schemars::JsonSchema>::json_schema(gen)
73 }
74
75 fn _schemars_private_non_optional_json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
76 <#ty as schemars::JsonSchema>::_schemars_private_non_optional_json_schema(gen)
77 }
78
79 fn _schemars_private_is_option() -> bool {
80 <#ty as schemars::JsonSchema>::_schemars_private_is_option()
81 }
82 };
83 };
84 });
85 }
86
87 let mut schema_base_name = cont.name().to_string();
88
89 if !cont.attrs.is_renamed {
90 if let Some(path) = cont.serde_attrs.remote() {
91 if let Some(segment) = path.segments.last() {
92 schema_base_name = segment.ident.to_string();
93 }
94 }
95 }
96
97 let type_params: Vec<_> = cont.generics.type_params().map(|ty| &ty.ident).collect();
99 let const_params: Vec<_> = cont.generics.const_params().map(|c| &c.ident).collect();
100 let params: Vec<_> = type_params.iter().chain(const_params.iter()).collect();
101
102 let (schema_name, schema_id) = if params.is_empty()
103 || (cont.attrs.is_renamed && !schema_base_name.contains('{'))
104 {
105 (
106 quote! {
107 #schema_base_name.to_owned()
108 },
109 quote! {
110 std::borrow::Cow::Borrowed(std::concat!(
111 std::module_path!(),
112 "::",
113 #schema_base_name
114 ))
115 },
116 )
117 } else if cont.attrs.is_renamed {
118 let mut schema_name_fmt = schema_base_name;
119 for tp in ¶ms {
120 schema_name_fmt.push_str(&format!("{{{}:.0}}", tp));
121 }
122 (
123 quote! {
124 format!(#schema_name_fmt #(,#type_params=#type_params::schema_name())* #(,#const_params=#const_params)*)
125 },
126 quote! {
127 std::borrow::Cow::Owned(
128 format!(
129 std::concat!(
130 std::module_path!(),
131 "::",
132 #schema_name_fmt
133 )
134 #(,#type_params=#type_params::schema_id())*
135 #(,#const_params=#const_params)*
136 )
137 )
138 },
139 )
140 } else {
141 let mut schema_name_fmt = schema_base_name;
142 schema_name_fmt.push_str("_for_{}");
143 schema_name_fmt.push_str(&"_and_{}".repeat(params.len() - 1));
144 (
145 quote! {
146 format!(#schema_name_fmt #(,#type_params::schema_name())* #(,#const_params)*)
147 },
148 quote! {
149 std::borrow::Cow::Owned(
150 format!(
151 std::concat!(
152 std::module_path!(),
153 "::",
154 #schema_name_fmt
155 )
156 #(,#type_params::schema_id())*
157 #(,#const_params)*
158 )
159 )
160 },
161 )
162 };
163
164 let schema_expr = if repr {
165 schema_exprs::expr_for_repr(&cont)?
166 } else {
167 schema_exprs::expr_for_container(&cont)
168 };
169
170 Ok(quote! {
171 const _: () = {
172 #crate_alias
173
174 #[automatically_derived]
175 #[allow(unused_braces)]
176 impl #impl_generics schemars::JsonSchema for #type_name #ty_generics #where_clause {
177 fn schema_name() -> std::string::String {
178 #schema_name
179 }
180
181 fn schema_id() -> std::borrow::Cow<'static, str> {
182 #schema_id
183 }
184
185 fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
186 #schema_expr
187 }
188 };
189 };
190 })
191}
192
193fn add_trait_bounds(cont: &mut Container) {
194 if let Some(bounds) = cont.serde_attrs.ser_bound() {
195 let where_clause = cont.generics.make_where_clause();
196 where_clause.predicates.extend(bounds.iter().cloned());
197 } else {
198 for param in &mut cont.generics.params {
201 if let syn::GenericParam::Type(ref mut type_param) = *param {
202 type_param.bounds.push(parse_quote!(schemars::JsonSchema));
203 }
204 }
205 }
206}