1use proc_macro::TokenStream;
7use quote::quote;
8use syn::{
9 Data, DataEnum, DataStruct, DeriveInput, Expr, Field, Fields, Variant, parse_macro_input,
10};
11
12#[proc_macro_derive(TypeShift, attributes(validate, serde, schemars))]
13pub fn derive_typeshift(input: TokenStream) -> TokenStream {
28 let input = parse_macro_input!(input as DeriveInput);
29
30 let ident = input.ident;
31 let generics = input.generics;
32 let (_, ty_generics, _) = generics.split_for_impl();
33
34 let ser_generics =
35 generics_with_type_bound(&generics, syn::parse_quote!(::typeshift::serde::Serialize));
36 let (ser_impl_generics, _, ser_where_clause) = ser_generics.split_for_impl();
37
38 let mut de_generics = generics_with_type_bound(
39 &generics,
40 syn::parse_quote!(::typeshift::serde::Deserialize<'__typeshift_de>),
41 );
42 de_generics
43 .params
44 .insert(0, syn::parse_quote!('__typeshift_de));
45 let (de_impl_generics, _, de_where_clause) = de_generics.split_for_impl();
46
47 let schema_generics = generics_with_type_bound(
48 &generics,
49 syn::parse_quote!(::typeshift::schemars::JsonSchema),
50 );
51 let (schema_impl_generics, _, schema_where_clause) = schema_generics.split_for_impl();
52
53 let validate_generics = generics_with_type_bound(
54 &generics,
55 syn::parse_quote!(::typeshift::validator::Validate),
56 );
57 let (validate_impl_generics, _, validate_where_clause) = validate_generics.split_for_impl();
58
59 let serde_container_attrs = filter_attrs(&input.attrs, &["serde"]);
60 let validate_container_attrs = filter_attrs(&input.attrs, &["validate"]);
61 let schema_container_attrs = filter_attrs(&input.attrs, &["serde", "schemars"]);
62
63 let (ser_helper_def, ser_helper_init) = match &input.data {
64 Data::Struct(data) => serialize_struct_helpers(data, &generics),
65 Data::Enum(data) => serialize_enum_helpers(data, &generics),
66 Data::Union(_) => {
67 return syn::Error::new_spanned(ident, "TypeShift derive does not support union")
68 .to_compile_error()
69 .into();
70 }
71 };
72
73 let (de_helper_def, de_construct) = match &input.data {
74 Data::Struct(data) => deserialize_struct_helpers(data, &generics),
75 Data::Enum(data) => deserialize_enum_helpers(data, &generics),
76 Data::Union(_) => {
77 return syn::Error::new_spanned(ident, "TypeShift derive does not support union")
78 .to_compile_error()
79 .into();
80 }
81 };
82
83 let schema_helper_def = match &input.data {
84 Data::Struct(data) => schema_struct_helper(data, &generics),
85 Data::Enum(data) => schema_enum_helper(data, &generics),
86 Data::Union(_) => {
87 return syn::Error::new_spanned(ident, "TypeShift derive does not support union")
88 .to_compile_error()
89 .into();
90 }
91 };
92
93 let ser_ref_lt = match &input.data {
94 Data::Struct(data) => (!matches!(data.fields, Fields::Unit)).then_some("'__typeshift_ser"),
95 Data::Enum(data) => enum_has_payload(data).then_some("'__typeshift_ser"),
96 Data::Union(_) => None,
97 };
98 let ser_helper_use_generics = helper_use_generics(&generics, ser_ref_lt.map(|_| "'_"));
99 let de_helper_use_generics = helper_use_generics(&generics, None);
100 let schema_helper_use_generics = helper_use_generics(&generics, None);
101
102 let validate_impl = match &input.data {
103 Data::Struct(data) => {
104 let (val_helper_def, val_helper_init) = validate_struct_helpers(data, &generics);
105 let val_ref_lt = (!matches!(data.fields, Fields::Unit)).then_some("'__typeshift_val");
106 let val_helper_use_generics = helper_use_generics(&generics, val_ref_lt.map(|_| "'_"));
107 quote! {
108 impl #validate_impl_generics ::typeshift::validator::Validate for #ident #ty_generics #validate_where_clause {
109 fn validate(&self) -> ::core::result::Result<(), ::typeshift::validator::ValidationErrors> {
110 #[allow(dead_code)]
111 #[derive(::typeshift::validator::Validate)]
112 #[validate(crate = "typeshift::validator")]
113 #(#validate_container_attrs)*
114 #val_helper_def
115
116 let helper: __TypeShiftValidateHelper #val_helper_use_generics = #val_helper_init;
117 ::typeshift::validator::Validate::validate(&helper)
118 }
119 }
120 }
121 }
122 Data::Enum(_) => {
123 quote! {
124 impl #validate_impl_generics ::typeshift::validator::Validate for #ident #ty_generics #validate_where_clause {
125 fn validate(&self) -> ::core::result::Result<(), ::typeshift::validator::ValidationErrors> {
126 ::core::result::Result::Ok(())
127 }
128 }
129 }
130 }
131 Data::Union(_) => {
132 return syn::Error::new_spanned(ident, "TypeShift derive does not support union")
133 .to_compile_error()
134 .into();
135 }
136 };
137
138 let expanded = quote! {
139 impl #ser_impl_generics ::typeshift::serde::Serialize for #ident #ty_generics #ser_where_clause {
140 fn serialize<S>(&self, serializer: S) -> ::core::result::Result<S::Ok, S::Error>
141 where
142 S: ::typeshift::serde::Serializer,
143 {
144 #[allow(dead_code)]
145 #[derive(::typeshift::serde::Serialize)]
146 #[serde(crate = "typeshift::serde")]
147 #(#serde_container_attrs)*
148 #ser_helper_def
149
150 let helper: __TypeShiftSerializeHelper #ser_helper_use_generics = #ser_helper_init;
151 ::typeshift::serde::Serialize::serialize(&helper, serializer)
152 }
153 }
154
155 impl #de_impl_generics ::typeshift::serde::Deserialize<'__typeshift_de> for #ident #ty_generics #de_where_clause {
156 fn deserialize<D>(deserializer: D) -> ::core::result::Result<Self, D::Error>
157 where
158 D: ::typeshift::serde::Deserializer<'__typeshift_de>,
159 {
160 #[allow(dead_code)]
161 #[derive(::typeshift::serde::Deserialize)]
162 #[serde(crate = "typeshift::serde")]
163 #(#serde_container_attrs)*
164 #de_helper_def
165
166 let helper = <__TypeShiftDeserializeHelper #de_helper_use_generics as ::typeshift::serde::Deserialize>::deserialize(deserializer)?;
167 ::core::result::Result::Ok(#de_construct)
168 }
169 }
170
171 #validate_impl
172
173 impl #schema_impl_generics ::typeshift::schemars::JsonSchema for #ident #ty_generics #schema_where_clause {
174 fn schema_name() -> ::std::string::String {
175 #[allow(dead_code)]
176 #[derive(::typeshift::schemars::JsonSchema)]
177 #[schemars(crate = "typeshift::schemars")]
178 #(#schema_container_attrs)*
179 #schema_helper_def
180
181 <__TypeShiftSchemaHelper #schema_helper_use_generics as ::typeshift::schemars::JsonSchema>::schema_name()
182 }
183
184 fn json_schema(
185 schema_generator: &mut ::typeshift::schemars::r#gen::SchemaGenerator,
186 ) -> ::typeshift::schemars::schema::Schema {
187 #[allow(dead_code)]
188 #[derive(::typeshift::schemars::JsonSchema)]
189 #[schemars(crate = "typeshift::schemars")]
190 #(#schema_container_attrs)*
191 #schema_helper_def
192
193 <__TypeShiftSchemaHelper #schema_helper_use_generics as ::typeshift::schemars::JsonSchema>::json_schema(schema_generator)
194 }
195
196 fn is_referenceable() -> bool {
197 #[allow(dead_code)]
198 #[derive(::typeshift::schemars::JsonSchema)]
199 #[schemars(crate = "typeshift::schemars")]
200 #(#schema_container_attrs)*
201 #schema_helper_def
202
203 <__TypeShiftSchemaHelper #schema_helper_use_generics as ::typeshift::schemars::JsonSchema>::is_referenceable()
204 }
205 }
206 };
207
208 expanded.into()
209}
210
211fn filter_attrs(attrs: &[syn::Attribute], allowed: &[&str]) -> Vec<syn::Attribute> {
212 attrs
213 .iter()
214 .filter(|attr| {
215 attr.path()
216 .segments
217 .first()
218 .map(|seg| allowed.iter().any(|name| seg.ident == name))
219 .unwrap_or(false)
220 })
221 .cloned()
222 .collect()
223}
224
225fn serialize_struct_helpers(
226 data: &DataStruct,
227 generics: &syn::Generics,
228) -> (proc_macro2::TokenStream, proc_macro2::TokenStream) {
229 let ref_lt = (!matches!(data.fields, Fields::Unit)).then_some("'__typeshift_ser");
230 let helper_def_generics = helper_def_generics(generics, ref_lt);
231 let helper_where_clause = helper_where_clause(generics);
232
233 let helper_def = match &data.fields {
234 Fields::Named(named) => {
235 let defs = named.named.iter().map(|field| {
236 let attrs = filter_attrs(&field.attrs, &["serde"]);
237 let name = named_field_ident(field);
238 let ty = &field.ty;
239 quote! { #(#attrs)* #name: &'__typeshift_ser #ty }
240 });
241 quote! { struct __TypeShiftSerializeHelper #helper_def_generics { #(#defs,)* } #helper_where_clause }
242 }
243 Fields::Unnamed(unnamed) => {
244 let defs = unnamed.unnamed.iter().map(|field| {
245 let attrs = filter_attrs(&field.attrs, &["serde"]);
246 let ty = &field.ty;
247 quote! { #(#attrs)* &'__typeshift_ser #ty }
248 });
249 quote! { struct __TypeShiftSerializeHelper #helper_def_generics ( #(#defs,)* ) #helper_where_clause ; }
250 }
251 Fields::Unit => quote! { struct __TypeShiftSerializeHelper; },
252 };
253
254 let helper_init = match &data.fields {
255 Fields::Named(named) => {
256 let inits = named.named.iter().map(|field| {
257 let name = named_field_ident(field);
258 quote! { #name: &self.#name }
259 });
260 quote! { __TypeShiftSerializeHelper { #(#inits,)* } }
261 }
262 Fields::Unnamed(unnamed) => {
263 let inits = unnamed.unnamed.iter().enumerate().map(|(idx, _)| {
264 let idx = syn::Index::from(idx);
265 quote! { &self.#idx }
266 });
267 quote! { __TypeShiftSerializeHelper( #(#inits,)* ) }
268 }
269 Fields::Unit => quote! { __TypeShiftSerializeHelper },
270 };
271
272 (helper_def, helper_init)
273}
274
275fn deserialize_struct_helpers(
276 data: &DataStruct,
277 generics: &syn::Generics,
278) -> (proc_macro2::TokenStream, proc_macro2::TokenStream) {
279 let helper_def_generics = helper_def_generics(generics, None);
280 let helper_where_clause = helper_where_clause(generics);
281
282 let helper_def = match &data.fields {
283 Fields::Named(named) => {
284 let defs = named.named.iter().map(|field| {
285 let attrs = filter_attrs(&field.attrs, &["serde"]);
286 let name = named_field_ident(field);
287 let ty = &field.ty;
288 quote! { #(#attrs)* #name: #ty }
289 });
290 quote! { struct __TypeShiftDeserializeHelper #helper_def_generics { #(#defs,)* } #helper_where_clause }
291 }
292 Fields::Unnamed(unnamed) => {
293 let defs = unnamed.unnamed.iter().map(|field| {
294 let attrs = filter_attrs(&field.attrs, &["serde"]);
295 let ty = &field.ty;
296 quote! { #(#attrs)* #ty }
297 });
298 quote! { struct __TypeShiftDeserializeHelper #helper_def_generics ( #(#defs,)* ) #helper_where_clause ; }
299 }
300 Fields::Unit => quote! { struct __TypeShiftDeserializeHelper; },
301 };
302
303 let construct = match &data.fields {
304 Fields::Named(named) => {
305 let builds = named.named.iter().map(|field| {
306 let name = named_field_ident(field);
307 quote! { #name: helper.#name }
308 });
309 quote! { Self { #(#builds,)* } }
310 }
311 Fields::Unnamed(unnamed) => {
312 let builds = unnamed.unnamed.iter().enumerate().map(|(idx, _)| {
313 let idx = syn::Index::from(idx);
314 quote! { helper.#idx }
315 });
316 quote! { Self( #(#builds,)* ) }
317 }
318 Fields::Unit => quote! { Self },
319 };
320
321 (helper_def, construct)
322}
323
324fn validate_struct_helpers(
325 data: &DataStruct,
326 generics: &syn::Generics,
327) -> (proc_macro2::TokenStream, proc_macro2::TokenStream) {
328 let ref_lt = (!matches!(data.fields, Fields::Unit)).then_some("'__typeshift_val");
329 let helper_def_generics = helper_def_generics(generics, ref_lt);
330 let helper_where_clause = helper_where_clause(generics);
331
332 let helper_def = match &data.fields {
333 Fields::Named(named) => {
334 let defs = named.named.iter().map(|field| {
335 let attrs = filter_attrs(&field.attrs, &["validate"]);
336 let name = named_field_ident(field);
337 let ty = &field.ty;
338 quote! { #(#attrs)* #name: &'__typeshift_val #ty }
339 });
340 quote! { struct __TypeShiftValidateHelper #helper_def_generics { #(#defs,)* } #helper_where_clause }
341 }
342 Fields::Unnamed(unnamed) => {
343 let defs = unnamed.unnamed.iter().map(|field| {
344 let attrs = filter_attrs(&field.attrs, &["validate"]);
345 let ty = &field.ty;
346 quote! { #(#attrs)* &'__typeshift_val #ty }
347 });
348 quote! { struct __TypeShiftValidateHelper #helper_def_generics ( #(#defs,)* ) #helper_where_clause ; }
349 }
350 Fields::Unit => quote! { struct __TypeShiftValidateHelper; },
351 };
352
353 let helper_init = match &data.fields {
354 Fields::Named(named) => {
355 let inits = named.named.iter().map(|field| {
356 let name = named_field_ident(field);
357 quote! { #name: &self.#name }
358 });
359 quote! { __TypeShiftValidateHelper { #(#inits,)* } }
360 }
361 Fields::Unnamed(unnamed) => {
362 let inits = unnamed.unnamed.iter().enumerate().map(|(idx, _)| {
363 let idx = syn::Index::from(idx);
364 quote! { &self.#idx }
365 });
366 quote! { __TypeShiftValidateHelper( #(#inits,)* ) }
367 }
368 Fields::Unit => quote! { __TypeShiftValidateHelper },
369 };
370
371 (helper_def, helper_init)
372}
373
374fn schema_struct_helper(data: &DataStruct, generics: &syn::Generics) -> proc_macro2::TokenStream {
375 let helper_def_generics = helper_def_generics(generics, None);
376 let helper_where_clause = helper_where_clause(generics);
377
378 match &data.fields {
379 Fields::Named(named) => {
380 let defs = named.named.iter().map(|field| {
381 let attrs = filter_attrs(&field.attrs, &["serde", "schemars"]);
382 let name = named_field_ident(field);
383 let ty = &field.ty;
384 quote! { #(#attrs)* #name: #ty }
385 });
386 quote! { struct __TypeShiftSchemaHelper #helper_def_generics { #(#defs,)* } #helper_where_clause }
387 }
388 Fields::Unnamed(unnamed) => {
389 let defs = unnamed.unnamed.iter().map(|field| {
390 let attrs = filter_attrs(&field.attrs, &["serde", "schemars"]);
391 let ty = &field.ty;
392 quote! { #(#attrs)* #ty }
393 });
394 quote! { struct __TypeShiftSchemaHelper #helper_def_generics ( #(#defs,)* ) #helper_where_clause ; }
395 }
396 Fields::Unit => quote! { struct __TypeShiftSchemaHelper; },
397 }
398}
399
400fn serialize_enum_helpers(
401 data: &DataEnum,
402 generics: &syn::Generics,
403) -> (proc_macro2::TokenStream, proc_macro2::TokenStream) {
404 let ref_lt = enum_has_payload(data).then_some("'__typeshift_ser");
405 let helper_def_generics = helper_def_generics(generics, ref_lt);
406 let helper_where_clause = helper_where_clause(generics);
407
408 let variants = data
409 .variants
410 .iter()
411 .map(|variant| enum_variant_definition(variant, &["serde"], ref_lt, true));
412
413 let helper_def = quote! {
414 enum __TypeShiftSerializeHelper #helper_def_generics #helper_where_clause {
415 #(#variants,)*
416 }
417 };
418
419 let arms = data
420 .variants
421 .iter()
422 .map(|variant| enum_match_arm_self_to_helper(variant, "__TypeShiftSerializeHelper"));
423 let helper_init = quote! {
424 match self {
425 #(#arms,)*
426 }
427 };
428
429 (helper_def, helper_init)
430}
431
432fn deserialize_enum_helpers(
433 data: &DataEnum,
434 generics: &syn::Generics,
435) -> (proc_macro2::TokenStream, proc_macro2::TokenStream) {
436 let helper_def_generics = helper_def_generics(generics, None);
437 let helper_where_clause = helper_where_clause(generics);
438
439 let variants = data
440 .variants
441 .iter()
442 .map(|variant| enum_variant_definition(variant, &["serde"], None, false));
443
444 let helper_def = quote! {
445 enum __TypeShiftDeserializeHelper #helper_def_generics #helper_where_clause {
446 #(#variants,)*
447 }
448 };
449
450 let arms = data
451 .variants
452 .iter()
453 .map(|variant| enum_match_arm_helper_to_self(variant, "__TypeShiftDeserializeHelper"));
454
455 let construct = quote! {
456 match helper {
457 #(#arms,)*
458 }
459 };
460
461 (helper_def, construct)
462}
463
464fn schema_enum_helper(data: &DataEnum, generics: &syn::Generics) -> proc_macro2::TokenStream {
465 let helper_def_generics = helper_def_generics(generics, None);
466 let helper_where_clause = helper_where_clause(generics);
467
468 let variants = data
469 .variants
470 .iter()
471 .map(|variant| enum_variant_definition(variant, &["serde", "schemars"], None, false));
472
473 quote! {
474 enum __TypeShiftSchemaHelper #helper_def_generics #helper_where_clause {
475 #(#variants,)*
476 }
477 }
478}
479
480fn enum_variant_definition(
481 variant: &Variant,
482 attr_allowlist: &[&str],
483 ref_lifetime: Option<&str>,
484 include_serde_field_attrs_for_refs: bool,
485) -> proc_macro2::TokenStream {
486 let variant_attrs = filter_attrs(&variant.attrs, attr_allowlist);
487 let name = &variant.ident;
488 let discriminant = enum_discriminant(&variant.discriminant);
489
490 match &variant.fields {
491 Fields::Named(named) => {
492 let fields = named.named.iter().map(|field| {
493 let mut allowlist = attr_allowlist.to_vec();
494 if include_serde_field_attrs_for_refs && !allowlist.contains(&"serde") {
495 allowlist.push("serde");
496 }
497 let attrs = filter_attrs(&field.attrs, &allowlist);
498 let field_name = named_field_ident(field);
499 let ty = &field.ty;
500 if let Some(lt) = ref_lifetime {
501 let lt_ident = syn::Lifetime::new(lt, proc_macro2::Span::call_site());
502 quote! { #(#attrs)* #field_name: &#lt_ident #ty }
503 } else {
504 quote! { #(#attrs)* #field_name: #ty }
505 }
506 });
507 quote! { #(#variant_attrs)* #name { #(#fields,)* } #discriminant }
508 }
509 Fields::Unnamed(unnamed) => {
510 let fields = unnamed.unnamed.iter().map(|field| {
511 let mut allowlist = attr_allowlist.to_vec();
512 if include_serde_field_attrs_for_refs && !allowlist.contains(&"serde") {
513 allowlist.push("serde");
514 }
515 let attrs = filter_attrs(&field.attrs, &allowlist);
516 let ty = &field.ty;
517 if let Some(lt) = ref_lifetime {
518 let lt_ident = syn::Lifetime::new(lt, proc_macro2::Span::call_site());
519 quote! { #(#attrs)* &#lt_ident #ty }
520 } else {
521 quote! { #(#attrs)* #ty }
522 }
523 });
524 quote! { #(#variant_attrs)* #name( #(#fields,)* ) #discriminant }
525 }
526 Fields::Unit => quote! { #(#variant_attrs)* #name #discriminant },
527 }
528}
529
530fn enum_match_arm_self_to_helper(variant: &Variant, helper_name: &str) -> proc_macro2::TokenStream {
531 let helper_ident = syn::Ident::new(helper_name, proc_macro2::Span::call_site());
532 let var_ident = &variant.ident;
533
534 match &variant.fields {
535 Fields::Named(named) => {
536 let names: Vec<_> = named.named.iter().map(named_field_ident).cloned().collect();
537 quote! {
538 Self::#var_ident { #(#names,)* } => #helper_ident::#var_ident { #(#names,)* }
539 }
540 }
541 Fields::Unnamed(unnamed) => {
542 let binds: Vec<_> = unnamed
543 .unnamed
544 .iter()
545 .enumerate()
546 .map(|(idx, _)| {
547 syn::Ident::new(&format!("__f{idx}"), proc_macro2::Span::call_site())
548 })
549 .collect();
550 quote! {
551 Self::#var_ident( #(#binds,)* ) => #helper_ident::#var_ident( #(#binds,)* )
552 }
553 }
554 Fields::Unit => quote! { Self::#var_ident => #helper_ident::#var_ident },
555 }
556}
557
558fn enum_match_arm_helper_to_self(variant: &Variant, helper_name: &str) -> proc_macro2::TokenStream {
559 let helper_ident = syn::Ident::new(helper_name, proc_macro2::Span::call_site());
560 let var_ident = &variant.ident;
561
562 match &variant.fields {
563 Fields::Named(named) => {
564 let names: Vec<_> = named.named.iter().map(named_field_ident).cloned().collect();
565 quote! {
566 #helper_ident::#var_ident { #(#names,)* } => Self::#var_ident { #(#names,)* }
567 }
568 }
569 Fields::Unnamed(unnamed) => {
570 let binds: Vec<_> = unnamed
571 .unnamed
572 .iter()
573 .enumerate()
574 .map(|(idx, _)| {
575 syn::Ident::new(&format!("__f{idx}"), proc_macro2::Span::call_site())
576 })
577 .collect();
578 quote! {
579 #helper_ident::#var_ident( #(#binds,)* ) => Self::#var_ident( #(#binds,)* )
580 }
581 }
582 Fields::Unit => quote! { #helper_ident::#var_ident => Self::#var_ident },
583 }
584}
585
586fn enum_discriminant(discriminant: &Option<(syn::token::Eq, Expr)>) -> proc_macro2::TokenStream {
587 match discriminant {
588 Some((_, expr)) => quote! { = #expr },
589 None => quote! {},
590 }
591}
592
593fn generics_with_type_bound(generics: &syn::Generics, bound: syn::TypeParamBound) -> syn::Generics {
594 let mut output = generics.clone();
595 for param in &mut output.params {
596 if let syn::GenericParam::Type(type_param) = param {
597 type_param.bounds.push(bound.clone());
598 }
599 }
600 output
601}
602
603fn helper_def_generics(
604 generics: &syn::Generics,
605 extra_lifetime: Option<&str>,
606) -> proc_macro2::TokenStream {
607 let params = &generics.params;
608 match (extra_lifetime, params.is_empty()) {
609 (Some(lt), true) => {
610 let lt = syn::Lifetime::new(lt, proc_macro2::Span::call_site());
611 quote! { <#lt> }
612 }
613 (Some(lt), false) => {
614 let lt = syn::Lifetime::new(lt, proc_macro2::Span::call_site());
615 quote! { <#lt, #params> }
616 }
617 (None, true) => quote! {},
618 (None, false) => quote! { <#params> },
619 }
620}
621
622fn helper_use_generics(
623 generics: &syn::Generics,
624 extra_lifetime: Option<&str>,
625) -> proc_macro2::TokenStream {
626 let args: Vec<proc_macro2::TokenStream> = generics
627 .params
628 .iter()
629 .map(|param| match param {
630 syn::GenericParam::Type(ty) => {
631 let ident = &ty.ident;
632 quote! { #ident }
633 }
634 syn::GenericParam::Lifetime(lt) => {
635 let lifetime = <.lifetime;
636 quote! { #lifetime }
637 }
638 syn::GenericParam::Const(konst) => {
639 let ident = &konst.ident;
640 quote! { #ident }
641 }
642 })
643 .collect();
644
645 match (extra_lifetime, args.is_empty()) {
646 (Some(lt), true) => {
647 let lt = syn::Lifetime::new(lt, proc_macro2::Span::call_site());
648 quote! { <#lt> }
649 }
650 (Some(lt), false) => {
651 let lt = syn::Lifetime::new(lt, proc_macro2::Span::call_site());
652 quote! { <#lt, #(#args,)*> }
653 }
654 (None, true) => quote! {},
655 (None, false) => quote! { <#(#args,)*> },
656 }
657}
658
659fn helper_where_clause(generics: &syn::Generics) -> proc_macro2::TokenStream {
660 match &generics.where_clause {
661 Some(where_clause) => quote! { #where_clause },
662 None => quote! {},
663 }
664}
665
666fn enum_has_payload(data: &DataEnum) -> bool {
667 data.variants
668 .iter()
669 .any(|variant| !matches!(variant.fields, Fields::Unit))
670}
671
672fn named_field_ident(field: &Field) -> &syn::Ident {
673 match &field.ident {
674 Some(name) => name,
675 None => unreachable!("named field must have ident"),
676 }
677}