use quote::quote;
fn do_action(input: syn::DeriveInput) -> Result<proc_macro::TokenStream, syn::Error> {
assert_eq!(input.generics.params.len(), 0);
let syn::Data::Enum(enumdata) = input.data else {
return Err(syn::Error::new(input.ident.span(), "must be enum"));
};
for var in enumdata.variants.iter() {
assert!(var.fields.is_empty());
}
let mut keys = vec![];
for var in enumdata.variants.iter() {
let name = var.ident.to_string();
let Some(key) = name.chars().filter(|v| v.is_uppercase() && !keys.contains(v)).next() else {
return Err(syn::Error::new(var.ident.span(), "no unused key for action entry"))
};
keys.push(key);
}
let enum_ident = input.ident;
let idents = enumdata.variants.iter().map(|v| v.ident.clone()).collect::<Vec<_>>();
let labels = enumdata.variants.iter().map(|v| v.ident.to_string()).collect::<Vec<_>>();
let indicies = 0..(enumdata.variants.len());
Ok(quote! {
impl ::cliask::ActionEnum for #enum_ident {
const LABELS : &'static [&'static str] = &[ #(#labels),* ];
const KEYS : &'static [char] = &[ #(#keys),* ];
fn try_parse(from: char) -> Option<#enum_ident> {
match from.to_uppercase().next().unwrap() {
#(
#keys => Some ( Self :: #idents ),
)*
_ => None
}
}
fn action_index(&self) -> usize {
match self {
#( Self :: #idents => #indicies ),*
}
}
}
}.into())
}
#[proc_macro_derive(ActionEnum)]
pub fn action(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = syn::parse_macro_input!(input as syn::DeriveInput);
match do_action(input) {
Ok(ts) => ts,
Err(err) => err.to_compile_error().into()
}
}