1use quote::quote;
2
3fn do_action(input: syn::DeriveInput) -> Result<proc_macro::TokenStream, syn::Error> {
4 assert_eq!(input.generics.params.len(), 0);
5
6 let syn::Data::Enum(enumdata) = input.data else {
7 return Err(syn::Error::new(input.ident.span(), "must be enum"));
8 };
9
10 for var in enumdata.variants.iter() {
12 assert!(var.fields.is_empty());
13 }
14
15 let mut keys = vec![];
16
17 for var in enumdata.variants.iter() {
18 let name = var.ident.to_string();
19
20 let Some(key) = name.chars().filter(|v| v.is_uppercase() && !keys.contains(v)).next() else {
21 return Err(syn::Error::new(var.ident.span(), "no unused key for action entry"))
22 };
23 keys.push(key);
24 }
25
26 let enum_ident = input.ident;
27 let idents = enumdata.variants.iter().map(|v| v.ident.clone()).collect::<Vec<_>>();
28 let labels = enumdata.variants.iter().map(|v| v.ident.to_string()).collect::<Vec<_>>();
29
30 let indicies = 0..(enumdata.variants.len());
31
32 Ok(quote! {
33 impl ::cliask::ActionEnum for #enum_ident {
34 const LABELS : &'static [&'static str] = &[ #(#labels),* ];
35 const KEYS : &'static [char] = &[ #(#keys),* ];
36
37 fn try_parse(from: char) -> Option<#enum_ident> {
38 match from.to_uppercase().next().unwrap() {
39 #(
40 #keys => Some ( Self :: #idents ),
41 )*
42 _ => None
43 }
44 }
45
46 fn action_index(&self) -> usize {
47 match self {
48 #( Self :: #idents => #indicies ),*
49 }
50 }
51 }
52 }.into())
53}
54
55#[proc_macro_derive(ActionEnum)]
56pub fn action(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
57 let input = syn::parse_macro_input!(input as syn::DeriveInput);
58
59 match do_action(input) {
60 Ok(ts) => ts,
61 Err(err) => err.to_compile_error().into()
62 }
63}