use item::{parse_items, Item};
use proc_macro::TokenStream;
use quote::quote;
use syn::{DataEnum, DeriveInput, Fields, Ident};
mod item;
#[proc_macro_derive(KeyMap, attributes(key))]
pub fn keymap(input: TokenStream) -> TokenStream {
let ast: DeriveInput = syn::parse(input).unwrap();
let syn::Data::Enum(DataEnum { variants, .. }) = ast.data else {
return syn::Error::new_spanned(
ast.ident,
"#[derive(KeyMap)] can only be derived for enums",
)
.to_compile_error()
.into();
};
match parse_items(&variants) {
Ok(items) => {
let config = impl_keymap_config(&ast.ident, &items);
quote! {
#config
}
.into()
}
Err(err) => err.to_compile_error().into(),
}
}
fn impl_keymap_config(name: &Ident, items: &Vec<Item>) -> proc_macro2::TokenStream {
let mut entries = Vec::new();
let mut match_arms = Vec::new();
for item in items {
let ident = &item.variant.ident;
let keys = &item
.keys
.iter()
.map(|key| quote! { #key.to_string() })
.collect::<Vec<_>>();
let doc = &item.description;
let variant = match &item.variant.fields {
Fields::Unit => quote! { #name::#ident },
Fields::Unnamed(_) => quote! { #name::#ident(..) },
Fields::Named(_) => quote! { #name::#ident { .. } },
};
match_arms.push(quote! {
#variant => ::keymap::Item::new(
vec![#(#keys),*],
#doc.to_string()
),
});
if !item.ignore {
entries.push(quote! {
(
#variant,
::keymap::Item::new(
vec![#(#keys),*],
#doc.to_string()
)
),
});
}
}
quote! {
impl ::keymap::KeyMapConfig<#name> for #name {
fn keymap_config() -> ::keymap::Config<#name> {
::keymap::Config::new(vec![#(#entries)*])
}
fn keymap_item(&self) -> ::keymap::Item {
match self {
#(#match_arms)*
}
}
}
}
}