1use itertools::Itertools;
6use proc_macro2::TokenStream;
7use quote::{format_ident, quote};
8use syn::{Data, DeriveInput, Fields, parse_macro_input};
9
10#[proc_macro_derive(TokRepr)]
11pub fn derive_tokrepr(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
12 let input = parse_macro_input!(input as DeriveInput);
13 tokrepr_impl(input).into()
14}
15
16pub(crate) fn tokrepr_impl(input: DeriveInput) -> TokenStream {
17 let name = &input.ident;
18 let tokrepr_path = quote! { tokrepr };
19 let self_path = quote! {};
20
21 let body = match &input.data {
22 Data::Struct(data) => match &data.fields {
23 Fields::Named(fields) => {
24 let fields = fields.named.iter().map(|f| &f.ident).collect_vec();
25 quote! {
26 #(let #fields = #tokrepr_path::TokRepr::tok_repr(&self.#fields);)*
27
28 #tokrepr_path::quote::quote! {
29 #self_path #name {
30 #(#fields: # #fields,)*
31 }
32 }
33 }
34 }
35 Fields::Unnamed(f) => {
36 let fields = (0..f.unnamed.len())
37 .map(|n| format_ident!("f{n}"))
38 .collect_vec();
39
40 quote! {
41 #(let #fields = #tokrepr_path::TokRepr::tok_repr(#fields);)*
42
43 #tokrepr_path::quote::quote!{
44 #self_path #name(#(# #fields,)*)
45 }
46 }
47 }
48 Fields::Unit => {
49 quote! {
50 #tokrepr_path::quote::quote! {
51 #self_path #name
52 }
53 }
54 }
55 },
56 Data::Enum(data) => {
57 let fields = data.variants.iter().map(|v| {
58 let variant = &v.ident;
59 match &v.fields {
60 Fields::Named(_) => unimplemented!(),
61 Fields::Unnamed(f) => {
62 let fields = (0..f.unnamed.len())
63 .map(|n| format_ident!("f{n}"))
64 .collect_vec();
65
66 quote! {
67 Self::#variant(#(#fields),*) => {
68 #(let #fields = #tokrepr_path::TokRepr::tok_repr(#fields);)*
69
70 #tokrepr_path::quote::quote!{
71 #self_path #name::#variant(#(# #fields,)*)
72 }
73 }
74 }
75 },
76 Fields::Unit => {
77 quote! {
78 Self::#variant => #tokrepr_path::quote::quote! { #self_path #name::#variant }
79 }
80 },
81 }
82 });
83
84 quote! {
85 match self {
86 #(#fields,)*
87 }
88 }
89 }
90 Data::Union(_) => unimplemented!("tokrepr derive is not implemented for unions"),
91 };
92
93 quote! {
94 impl #tokrepr_path::TokRepr for #name {
95 fn tok_repr(&self) -> #tokrepr_path::proc_macro2::TokenStream {
96 #body
97 }
98 }
99 }
100}