cliask-derive 0.1.0

proc-macro crate for cliask
Documentation
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"));
    };

    // confirm all variants are units
    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()
    }
}