|
@@ -0,0 +1,61 @@
|
|
|
+use std::collections::BTreeMap;
|
|
|
+
|
|
|
+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();
|
|
|
+ println!("variant name: {}", name);
|
|
|
+
|
|
|
+ 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 action_count = enumdata.variants.len();
|
|
|
+ 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<_>>();
|
|
|
+
|
|
|
+ Ok(quote! {
|
|
|
+ impl ::cliask::ActionEnum for #enum_ident {
|
|
|
+ const ACTION_COUNT : usize = #action_count;
|
|
|
+ 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
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }.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()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|