tokrepr_derive/
lib.rs

1//! Derive macro for tokrepr.
2//!
3//!
4
5use 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}