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}