diff --git a/app/input_mapping_common/src/lib.rs b/app/input_mapping_common/src/lib.rs index 09ab634..8ea9643 100644 --- a/app/input_mapping_common/src/lib.rs +++ b/app/input_mapping_common/src/lib.rs @@ -1,17 +1,23 @@ use ratatui::crossterm::event::{Event, KeyCode}; pub trait InputMappingT: Sized { - fn get_mapping(&self) -> InputMapping; + fn get_mapping() -> InputMapping; - fn map_event(&self, event: Event) -> Option; + fn map_event(event: Event) -> Option; } -pub struct InputMapping { - pub mapping: Vec>, +pub struct InputMapping { + pub mapping: Vec, } -pub struct MappingEntry { +impl InputMapping { + pub fn merge(mut self, mut other: InputMapping) -> Self { + self.mapping.append(&mut other.mapping); + self + } +} + +pub struct MappingEntry { pub key: KeyCode, pub description: String, - pub event: M, } diff --git a/app/input_mapping_derive/src/lib.rs b/app/input_mapping_derive/src/lib.rs index 2e4473f..7b17661 100644 --- a/app/input_mapping_derive/src/lib.rs +++ b/app/input_mapping_derive/src/lib.rs @@ -17,22 +17,21 @@ pub fn derive_mapping(input: proc_macro::TokenStream) -> proc_macro::TokenStream // TODO: Add check that mappings don't overlap. fn generate_trait_impl(item_enum: ItemEnum) -> TokenStream { - let mapping_entries = item_enum + let (unit, to_be_flattened): (Vec<_>, Vec<_>) = item_enum .variants - .iter() - .filter(|variant| matches!(variant.fields, Fields::Unit)) - .map(generate_mapping_entry); + .into_iter() + .partition(|variant| matches!(variant.fields, Fields::Unit)); + + let mapping_entries = unit.into_iter().map(generate_mapping_entry); let mapping_constructors = mapping_entries.clone().map(|entry| { let key = entry.key; let description = entry.description; - let event = entry.event; quote! { ::input_mapping_common::MappingEntry { key: ::ratatui::crossterm::event::KeyCode::Char(#key), description: (#description).to_string(), - event: Self:: #event } } }); @@ -42,7 +41,48 @@ fn generate_trait_impl(item_enum: ItemEnum) -> TokenStream { let event = entry.event; quote! { - () if event.is_key_pressed(::ratatui::crossterm::event::KeyCode::Char(#key)) => ::std::option::Option::Some(Self:: #event) + () if event.is_key_pressed(::ratatui::crossterm::event::KeyCode::Char(#key)) => + ::std::option::Option::Some(Self:: #event) + } + }); + + let get_mapping_flattening = to_be_flattened.iter().map(|variant| { + let field_ty = match &variant.fields { + Fields::Unnamed(fields) => { + let fields = &fields.unnamed; + if fields.len() != 1 { + panic!("Multiple unnamed fields are not supported"); + } + &fields.first().expect("fields.len() checked to be = 1").ty + } + Fields::Unit => panic!("Unit fields have been filtered above"), + Fields::Named(_) => panic!("Named variant fields are not supported"), + }; + + quote! { + .merge(#field_ty ::get_mapping()) + } + }); + + let map_event_flattening = to_be_flattened.iter().map(|variant| { + let field_ty = match &variant.fields { + Fields::Unnamed(fields) => { + let fields = &fields.unnamed; + if fields.len() != 1 { + panic!("Multiple unnamed fields are not supported"); + } + &fields.first().expect("fields.len() checked to be = 1").ty + } + Fields::Unit => panic!("Unit fields have been filtered above"), + Fields::Named(_) => panic!("Named variant fields are not supported"), + }; + + let ident = &variant.ident; + + quote! { + .or_else(|| { + #field_ty ::map_event(event).map(Self:: #ident) + }) } }); @@ -50,21 +90,23 @@ fn generate_trait_impl(item_enum: ItemEnum) -> TokenStream { quote! { impl ::input_mapping_common::InputMappingT for #ident { - fn get_mapping(&self) -> ::input_mapping_common::InputMapping { + fn get_mapping() -> ::input_mapping_common::InputMapping { ::input_mapping_common::InputMapping { - // TODO: Concat flattened mapping: vec![ #(#mapping_constructors,)* ] } + + #(#get_mapping_flattening)* } - fn map_event(&self, event: ::ratatui::crossterm::event::Event) -> ::std::option::Option { - // TODO: Concat flattened + fn map_event(event: ::ratatui::crossterm::event::Event) -> ::std::option::Option { match () { #(#mapping_matchers,)* _ => None, } + + #(#map_event_flattening)* } } } @@ -76,7 +118,7 @@ struct MappingEntry { event: TokenStream, } -fn generate_mapping_entry(variant: &Variant) -> MappingEntry { +fn generate_mapping_entry(variant: Variant) -> MappingEntry { let mut key: Option = None; let mut description: Option = None;