nameth_macro/
lib.rs

1#![doc = include_str!("../README.md")]
2
3use darling::FromMeta;
4use darling::ast::NestedMeta;
5use proc_macro::TokenStream;
6use quote::format_ident;
7use quote::quote;
8use syn::Ident;
9
10#[proc_macro_attribute]
11pub fn nameth(attr: TokenStream, tokens: TokenStream) -> TokenStream {
12    let attr_args = match NestedMeta::parse_meta_list(attr.into()) {
13        Ok(args) => args,
14        Err(error) => {
15            return TokenStream::from(darling::Error::from(error).write_errors());
16        }
17    };
18    let attr_args = match NamedMacroArgs::from_list(&attr_args) {
19        Ok(args) => args,
20        Err(error) => {
21            return TokenStream::from(error.write_errors());
22        }
23    };
24
25    let item: syn::Item = match syn::parse(tokens.clone()) {
26        Ok(item) => item,
27        Err(err) => return err.into_compile_error().into(),
28    };
29
30    let crate_name = attr_args.crate_override.as_deref().unwrap_or("nameth");
31    let crate_name = format_ident!("{}", crate_name);
32
33    let name = match item {
34        syn::Item::Struct(item_struct) => process_struct(&crate_name, item_struct),
35        syn::Item::Enum(item_enum) => process_enum(&crate_name, item_enum),
36        syn::Item::Fn(item_fn) => process_fn(item_fn),
37        _ => return quote! { compile_error!("Unexpected item kind"); }.into(),
38    };
39
40    if attr_args.debug {
41        println!("\nGenerated:\n{name}\n");
42    }
43
44    let mut tokens = tokens;
45    tokens.extend(name);
46    return tokens;
47}
48
49fn process_struct(crate_name: &Ident, item_struct: syn::ItemStruct) -> TokenStream {
50    let syn::ItemStruct {
51        ident, generics, ..
52    } = item_struct;
53    let name = ident.to_string();
54    let param_names_only = param_names_only(&generics);
55    quote! {
56        impl #generics #crate_name::NamedType for #ident #param_names_only {
57            fn type_name() -> &'static str {
58                return #name;
59            }
60        }
61    }
62    .into()
63}
64
65fn process_enum(crate_name: &Ident, item_enum: syn::ItemEnum) -> TokenStream {
66    let syn::ItemEnum {
67        ident,
68        generics,
69        variants,
70        ..
71    } = item_enum;
72    let cases: Vec<_> = variants
73        .iter()
74        .map(|variant| {
75            let ident = &variant.ident;
76            let name = ident.to_string();
77            quote! { Self::#ident { .. } => #name, }
78        })
79        .collect();
80
81    let param_names_only = param_names_only(&generics);
82    let name = ident.to_string();
83    quote! {
84        impl #generics #crate_name::NamedType for #ident #param_names_only {
85            fn type_name() -> &'static str {
86                return #name;
87            }
88        }
89        impl #generics #crate_name::NamedEnumValues for #ident #param_names_only {
90            fn name(&self) -> &'static str {
91                match self {
92                    #(#cases)*
93                }
94            }
95        }
96    }
97    .into()
98}
99
100fn process_fn(item_fn: syn::ItemFn) -> TokenStream {
101    let name = item_fn.sig.ident.to_string();
102    let vis = item_fn.vis;
103    let ident = format_ident!("{}", name.to_uppercase());
104    quote! { #vis const #ident : &'static str = #name; }.into()
105}
106
107#[derive(Debug, FromMeta)]
108struct NamedMacroArgs {
109    #[darling(default)]
110    debug: bool,
111
112    #[darling(default)]
113    crate_override: Option<String>,
114}
115
116fn param_names_only(generics: &syn::Generics) -> syn::Generics {
117    let mut generics = generics.clone();
118    for param in &mut generics.params {
119        match param {
120            syn::GenericParam::Lifetime(syn::LifetimeParam {
121                attrs,
122                colon_token,
123                bounds,
124                ..
125            }) => {
126                *attrs = vec![];
127                *colon_token = None;
128                *bounds = syn::punctuated::Punctuated::default()
129            }
130            syn::GenericParam::Type(syn::TypeParam {
131                attrs,
132                colon_token,
133                bounds,
134                eq_token,
135                default,
136                ..
137            }) => {
138                *attrs = vec![];
139                *colon_token = None;
140                *bounds = syn::punctuated::Punctuated::default();
141                *eq_token = None;
142                *default = None;
143            }
144            syn::GenericParam::Const(syn::ConstParam {
145                attrs,
146                eq_token,
147                default,
148                ..
149            }) => {
150                *attrs = vec![];
151                *eq_token = None;
152                *default = None;
153            }
154        }
155    }
156    generics
157}