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 without_defaults = without_defaults(&generics);
55 let param_names_only = param_names_only(&generics);
56 quote! {
57 impl #without_defaults #crate_name::NamedType for #ident #param_names_only {
58 fn type_name() -> &'static str {
59 return #name;
60 }
61 }
62 }
63 .into()
64}
65
66fn process_enum(crate_name: &Ident, item_enum: syn::ItemEnum) -> TokenStream {
67 let syn::ItemEnum {
68 ident,
69 generics,
70 variants,
71 ..
72 } = item_enum;
73 let cases: Vec<_> = variants
74 .iter()
75 .map(|variant| {
76 let ident = &variant.ident;
77 let name = ident.to_string();
78 quote! { Self::#ident { .. } => #name, }
79 })
80 .collect();
81
82 let without_defaults = without_defaults(&generics);
83 let param_names_only = param_names_only(&generics);
84 let name = ident.to_string();
85 quote! {
86 impl #without_defaults #crate_name::NamedType for #ident #param_names_only {
87 fn type_name() -> &'static str {
88 return #name;
89 }
90 }
91 impl #without_defaults #crate_name::NamedEnumValues for #ident #param_names_only {
92 fn name(&self) -> &'static str {
93 match self {
94 #(#cases)*
95 }
96 }
97 }
98 }
99 .into()
100}
101
102fn process_fn(item_fn: syn::ItemFn) -> TokenStream {
103 let name = item_fn.sig.ident.to_string();
104 let vis = item_fn.vis;
105 let ident = format_ident!("{}", name.to_uppercase());
106 quote! { #vis const #ident : &'static str = #name; }.into()
107}
108
109#[derive(Debug, FromMeta)]
110struct NamedMacroArgs {
111 #[darling(default)]
112 debug: bool,
113
114 #[darling(default)]
115 crate_override: Option<String>,
116}
117
118fn param_names_only(generics: &syn::Generics) -> syn::Generics {
119 let mut generics = without_defaults(generics);
120 for param in &mut generics.params {
121 match param {
122 syn::GenericParam::Lifetime(syn::LifetimeParam {
123 colon_token,
124 bounds,
125 ..
126 }) => {
127 *colon_token = None;
128 *bounds = syn::punctuated::Punctuated::default()
129 }
130 syn::GenericParam::Type(syn::TypeParam {
131 colon_token,
132 bounds,
133 ..
134 }) => {
135 *colon_token = None;
136 *bounds = syn::punctuated::Punctuated::default();
137 }
138 syn::GenericParam::Const(syn::ConstParam {
139 eq_token, default, ..
140 }) => {
141 *eq_token = None;
142 *default = None;
143 }
144 }
145 }
146 generics
147}
148
149fn without_defaults(generics: &syn::Generics) -> syn::Generics {
150 let mut generics = generics.clone();
151 for param in &mut generics.params {
152 match param {
153 syn::GenericParam::Lifetime(syn::LifetimeParam { attrs, .. }) => {
154 *attrs = vec![];
155 }
156 syn::GenericParam::Type(syn::TypeParam {
157 attrs,
158 eq_token,
159 default,
160 ..
161 }) => {
162 *attrs = vec![];
163 *eq_token = None;
164 *default = None;
165 }
166 syn::GenericParam::Const(syn::ConstParam { attrs, .. }) => {
167 *attrs = vec![];
168 }
169 }
170 }
171 generics
172}