use std::collections::BTreeMap;
use crate::{
error::Error,
key_parser,
menu::{Menu, PendingMenu},
ops::Op,
};
use crossterm::event::{KeyCode, KeyModifiers};
pub struct Bindings {
vec: Vec<Binding>,
}
impl TryFrom<BTreeMap<Menu, BTreeMap<Op, Vec<String>>>> for Bindings {
type Error = crate::error::Error;
fn try_from(value: BTreeMap<Menu, BTreeMap<Op, Vec<String>>>) -> Result<Self, Self::Error> {
let mut bindings = Vec::new();
let mut bad_bindings = Vec::new();
for (menu, ops) in value {
for (op, binds) in ops {
for keys in binds {
if let Some(binding) = Binding::parse(menu, &keys, op.clone()) {
bindings.push(binding);
} else {
bad_bindings.push(format!(
"- {}.{} = {}",
menu.as_ref(),
op.as_ref(),
keys
));
}
}
}
}
if !bad_bindings.is_empty() {
return Err(Error::Bindings {
bad_key_bindings: bad_bindings,
});
}
Ok(Self { vec: bindings })
}
}
impl Bindings {
pub(crate) fn match_bindings<'a>(
&'a self,
pending: &'a Menu,
events: &'a [(KeyModifiers, KeyCode)],
) -> impl Iterator<Item = &'a Binding> + 'a {
self.vec
.iter()
.filter(move |binding| &binding.menu == pending)
.filter(|binding| binding.keys.starts_with(events))
}
pub(crate) fn list<'a>(&'a self, pending: &Menu) -> impl Iterator<Item = &'a Binding> {
let expected = if pending == &Menu::Help {
Menu::Root
} else {
*pending
};
self.vec
.iter()
.filter(|keybind| !matches!(keybind.op, Op::ToggleArg(_)))
.filter(move |keybind| keybind.menu == expected)
}
pub(crate) fn arg_list<'a>(
&'a self,
pending: &'a PendingMenu,
) -> impl Iterator<Item = &'a Binding> {
let expected = if pending.menu == Menu::Help {
Menu::Root
} else {
pending.menu
};
self.vec
.iter()
.filter(|keybind| {
if let Op::ToggleArg(ref arg) = keybind.op {
pending.args.contains_key(arg.as_str())
} else {
false
}
})
.filter(move |keybind| keybind.menu == expected)
}
}
pub(crate) struct Binding {
pub menu: Menu,
pub raw: String,
pub keys: Vec<(KeyModifiers, KeyCode)>,
pub op: Op,
}
impl Binding {
pub fn parse(menu: Menu, raw_keys: &str, op: Op) -> Option<Self> {
if let Ok(("", keys)) = key_parser::parse_config_keys(raw_keys) {
Some(Self {
menu,
raw: raw_keys.to_string(),
keys,
op,
})
} else {
None
}
}
}