enum_rename/
lib.rs

1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{parse_macro_input, punctuated::Punctuated, DeriveInput, Meta, Token};
4
5#[proc_macro_derive(EnumRenames, attributes(serde))]
6pub fn enum_renames(input: TokenStream) -> TokenStream {
7    let ast = parse_macro_input!(input as DeriveInput);
8    let name = &ast.ident;
9
10    let mut match_arms = Vec::new();
11    let mut variant_renames = Vec::new();
12
13    if let syn::Data::Enum(data_enum) = ast.data {
14        for variant in data_enum.variants {
15            let ident = &variant.ident;
16            let mut rename = ident.to_string();
17            for attr in variant.attrs {
18                if attr.path().is_ident("serde") {
19                    if let Ok(nested) = attr.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated) {
20                        for meta in nested {
21                            if let Meta::NameValue(nv) = meta {
22                                if nv.path.is_ident("rename") {
23                                    if let syn::Expr::Lit(expr_lit) = &nv.value {
24                                        if let syn::Lit::Str(lit_str) = &expr_lit.lit {
25                                            rename = lit_str.value();
26                                        }
27                                    }
28                                }
29                            }
30                        }
31                    }
32                }
33            }
34            // clone here to avoid move
35            variant_renames.push(rename.clone());
36
37            let pat = match &variant.fields {
38                syn::Fields::Unit => quote! { Self::#ident },
39                syn::Fields::Unnamed(_) => quote! { Self::#ident (..) },
40                syn::Fields::Named(_) => quote! { Self::#ident { .. } },
41            };
42            match_arms.push(quote! { #pat => #rename });
43        }
44    }
45
46    let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
47
48    let expanded = quote! {
49        impl #impl_generics #name #ty_generics #where_clause {
50            pub fn renames() -> &'static [&'static str] {
51                &[#(#variant_renames),*]
52            }
53            pub fn rename(&self) -> &'static str {
54                match self {
55                    #(#match_arms,)*
56                }
57            }
58        }
59    };
60
61    expanded.into()
62}
63#[cfg(test)]
64mod tests {
65    use super::*;
66    #[test]
67    fn test_case() {
68        assert!(false);
69    }
70}