1extern crate proc_macro;
28use proc_macro::TokenStream;
29use proc_macro2::Ident;
30use syn::{parse_macro_input, DeriveInput, Data, Fields, Field, Index, Variant, Generics, GenericParam};
31use quote::{quote, ToTokens};
32use syn::punctuated::Punctuated;
33use syn::token::Comma;
34
35
36#[proc_macro_attribute]
42pub fn validate_deser(_args: TokenStream, input: TokenStream) -> TokenStream {
43 let input = parse_macro_input!(input as DeriveInput);
44
45 let name = &input.ident;
46
47 let generics = &input.generics;
48
49 let helper_name = Ident::new(&format!("__ValidDeserialize{name}"), name.span());
50
51 let HelperData { helper_def, init_from_helper } = match input.data {
52 Data::Struct(ref data) => match data.fields {
53 Fields::Named(ref fields) => build_named_struct(&name, &helper_name, &generics, &fields.named),
54 Fields::Unnamed(ref fields) => build_unnamed_struct(&name, &helper_name, &generics, &fields.unnamed),
55 Fields::Unit => build_unit_struct(&name, &helper_name),
56 }
57 Data::Enum(ref data) => build_enum(&name, &helper_name, &generics, &data.variants),
58 Data::Union(_) => {unimplemented!()}
59 };
60
61 let generic_params = generics.params.to_token_stream();
62 let extra_where_clause: Vec<_> = generics.params.iter().filter_map(|p| match p {
63 GenericParam::Type(p) => {
64 let p = &p.ident;
65 Some(quote! { #p : serde::Deserialize<'__de> })
66 }
67 _ => None,
68 }).collect();
69 let where_clause = match generics.where_clause {
70 None => quote! {
71 where #(#extra_where_clause,)*
72 },
73 Some(ref clause) => {
74 let predicates = clause.predicates.iter().map(|p| p.to_token_stream());
75 quote! {
76 where #(#predicates,)* #(#extra_where_clause,)*
77 }
78 }
79 };
80 let simple_gen_params = generics.params.iter().map(|p| match p {
81 GenericParam::Type(p) => {
82 let p = &p.ident;
83 quote! { #p }
84 }
85 GenericParam::Lifetime(p) => {
86 let p = &p.lifetime;
87 quote! { #p }
88 },
89 GenericParam::Const(p) => {
90 let p = &p.ident;
91 quote! { #p }
92 },
93 });
94
95 let tokens = quote! {
96 #input
97
98 #[derive(serde::Deserialize)]
99 #helper_def
100
101 impl <'__de, #generic_params> serde::Deserialize<'__de> for #name<#(#simple_gen_params,)*> #where_clause {
102 fn deserialize<__D>(deserializer: __D) -> Result<Self, __D::Error>
103 where
104 __D: serde::Deserializer<'__de>
105 {
106 let helper = #helper_name::deserialize(deserializer)?;
107 let instance = #init_from_helper;
108 instance.validated().map_err(serde::de::Error::custom)
109 }
110 }
111
112 };
113
114 tokens.into()
115}
116
117
118struct HelperData {
119 helper_def: proc_macro2::TokenStream,
120 init_from_helper: proc_macro2::TokenStream,
121}
122
123fn build_named_struct(name: &Ident, helper_name: &Ident, generics: &Generics, fields: &Punctuated<Field, Comma>) -> HelperData {
124 let helper_def = named_def_full(helper_name, generics, fields);
125
126 let helper_def = quote! {
127 struct #helper_def
128 };
129
130 let init_from_helper = init_from_named(name, fields);
131
132 HelperData {
133 helper_def,
134 init_from_helper
135 }
136}
137
138fn named_def_full(name: &Ident, generics: &Generics, fields: &Punctuated<Field, Comma>) -> proc_macro2::TokenStream {
139 let generic_params = generics.params.to_token_stream();
140 let where_clause = generics.where_clause.to_token_stream();
141 let fields = fields.to_token_stream();
142 quote! {
143 #name<#generic_params> #where_clause {
144 #fields
145 }
146 }
147}
148
149fn init_from_named(name: &Ident, fields: &Punctuated<Field, Comma>) -> proc_macro2::TokenStream {
150 let init_fields = fields.iter().map(|field| {
151 let name = &field.ident;
152 quote! { #name: helper.#name }
153 });
154
155 quote! {
156 #name {
157 #( #init_fields ),*
158 }
159 }
160}
161
162fn build_unnamed_struct(name: &Ident, helper_name: &Ident, generics: &Generics, fields: &Punctuated<Field, Comma>) -> HelperData {
163 let helper_def = unnamed_def_full(helper_name, generics, fields);
164
165 let helper_def = quote! {
166 struct #helper_def
167 };
168
169 let init_from_helper = init_from_unnamed(name, fields);
170
171 HelperData {
172 helper_def,
173 init_from_helper
174 }
175}
176
177fn unnamed_def_full(name: &Ident, generics: &Generics, fields: &Punctuated<Field, Comma>) -> proc_macro2::TokenStream {
178 let generic_params = generics.params.to_token_stream();
179 let where_clause = generics.where_clause.to_token_stream();
180 let fields = fields.to_token_stream();
181 quote! {
182 #name<#generic_params>(#fields) #where_clause;
183 }
184}
185
186fn init_from_unnamed(name: &Ident, fields: &Punctuated<Field, Comma>) -> proc_macro2::TokenStream {
187 let init_fields = fields.iter().enumerate().map(|(i, _)| {
188 let index = Index::from(i);
189 quote! { helper.#index }
190 });
191
192 quote! {
193 #name(#( #init_fields ),*)
194 }
195}
196
197fn build_unit_struct(name: &Ident, helper_name: &Ident) -> HelperData {
198 let helper_def = quote! {
199 struct #helper_name;
200 };
201
202 let init_from_helper = quote! {
203 #name
204 };
205
206 HelperData {
207 helper_def,
208 init_from_helper
209 }
210}
211
212fn build_enum(name: &Ident, helper_name: &Ident, generics: &Generics, variants: &Punctuated<Variant, Comma>) -> HelperData {
213 let helper_def = enum_def(helper_name, generics, variants);
214
215 let init_from_helper = init_from_enum(name, helper_name, variants);
216
217 HelperData {
218 helper_def,
219 init_from_helper
220 }
221}
222
223fn enum_def(name: &Ident, generics: &Generics, variants: &Punctuated<Variant, Comma>) -> proc_macro2::TokenStream {
224 let generic_params = generics.params.to_token_stream();
225 let where_clause = generics.where_clause.to_token_stream();
226 let variants = variants.iter().map(|variant| {
227 let name = &variant.ident;
228 match variant.fields {
229 Fields::Named(ref fields) => named_def(name, &fields.named),
230 Fields::Unnamed(ref fields) => unnamed_def(name, &fields.unnamed),
231 Fields::Unit => quote! { #name },
232 }
233 });
234 quote! {
235 enum #name<#generic_params> #where_clause {
236 #( #variants ),*
237 }
238 }
239}
240
241fn init_from_enum(name: &Ident, helper_name: &Ident, variants: &Punctuated<Variant, Comma>) -> proc_macro2::TokenStream {
242 let init_variants = variants.iter().map(|variant| {
243 let variant_name = &variant.ident;
244 match variant.fields {
245 Fields::Named(ref fields) => init_enum_from_named(name, helper_name, variant_name, &fields.named),
246 Fields::Unnamed(ref fields) => init_enum_from_unnamed(name, helper_name, variant_name, &fields.unnamed),
247 Fields::Unit => init_enum_from_unit(name, helper_name, variant_name),
248 }
249 });
250 quote! {
251 match helper {
252 #( #init_variants ),*
253 }
254 }
255}
256
257fn named_def(name: &Ident, fields: &Punctuated<Field, Comma>) -> proc_macro2::TokenStream {
258 let fields = fields.to_token_stream();
259 quote! {
260 #name {
261 #fields
262 }
263 }
264}
265
266fn unnamed_def(name: &Ident, fields: &Punctuated<Field, Comma>) -> proc_macro2::TokenStream {
267 let fields = fields.to_token_stream();
268 quote! {
269 #name(#fields)
270 }
271}
272
273fn init_enum_from_named(name: &Ident, helper_name: &Ident, variant_name: &Ident, fields: &Punctuated<Field, Comma>) -> proc_macro2::TokenStream {
274 let fields = fields.iter().map(|field| {
275 let name = &field.ident;
276 quote! { #name }
277 });
278
279 let fields_clone = fields.clone();
280
281 quote! {
282 #helper_name::#variant_name { #( #fields ),* } => #name::#variant_name { #( #fields_clone ),* }
283 }
284}
285
286fn init_enum_from_unnamed(name: &Ident, helper_name: &Ident, variant_name: &Ident, fields: &Punctuated<Field, Comma>) -> proc_macro2::TokenStream {
287 let fields = fields.iter().enumerate().map(|(i, _)| {
288 let index = Index::from(i);
289 let name = Ident::new(&format!("value_{}", i), index.span);
290 quote! { #name }
291 });
292
293 let fields_clone = fields.clone();
294
295 quote! {
296 #helper_name::#variant_name( #( #fields ),* ) => #name::#variant_name( #( #fields_clone ),* )
297 }
298}
299
300fn init_enum_from_unit(name: &Ident, helper_name: &Ident, variant_name: &Ident) -> proc_macro2::TokenStream {
301 quote! {
302 #helper_name::#variant_name => #name::#variant_name
303 }
304}