use std::collections::HashMap;
use crossterm::event::KeyCode;
use serde::{Deserialize, Deserializer, Serialize, Serializer, de};
#[derive(Debug, Clone)]
pub struct Keybindings {
pub exit: Vec<KeyCode>,
pub help: Vec<KeyCode>,
pub enter: Vec<KeyCode>,
pub save: Vec<KeyCode>,
pub reload: Vec<KeyCode>,
pub previous: Vec<KeyCode>,
pub next: Vec<KeyCode>,
pub new: Vec<KeyCode>,
pub rename: Vec<KeyCode>,
pub duplicate: Vec<KeyCode>,
pub delete: Vec<KeyCode>,
pub toggle_activate: Vec<KeyCode>,
pub toggle_enabled: Vec<KeyCode>,
pub view_logs: Vec<KeyCode>,
}
impl Keybindings {
pub fn print_help(&self) -> String {
let mut help = String::new();
macro_rules! add_keybinding_help {
($help:expr, $($field:ident),*) => {
$(
let to_str = |k: &KeyCode| match k {
KeyCode::Char(c) => c.to_string(),
KeyCode::Esc => "esc".to_string(),
KeyCode::Enter => "enter".to_string(),
KeyCode::Up => "up".to_string(),
KeyCode::Down => "down".to_string(),
KeyCode::Left => "left".to_string(),
KeyCode::Right => "right".to_string(),
KeyCode::Backspace => "backspace".to_string(),
KeyCode::Tab => "tab".to_string(),
KeyCode::F(n) => format!("f{n}"),
_ => format!("{k:?}").to_lowercase(),
};
let keys: Vec<String> = self.$field.iter().map(to_str).collect();
$help.push_str(&format!(" {}: [{}]\n", stringify!($field), keys.join(", ")));
)*
};
}
add_keybinding_help!(
help,
exit,
help,
enter,
save,
reload,
previous,
next,
new,
rename,
duplicate,
delete,
toggle_activate,
toggle_enabled,
view_logs
);
help
}
}
impl Default for Keybindings {
fn default() -> Self {
Self {
exit: vec![KeyCode::Char('q'), KeyCode::Esc],
help: vec![KeyCode::Char('?')],
enter: vec![KeyCode::Enter],
save: vec![KeyCode::Char('s')],
reload: vec![KeyCode::Char('f')],
previous: vec![KeyCode::Up],
next: vec![KeyCode::Down],
new: vec![KeyCode::Char('n')],
rename: vec![KeyCode::Char('r')],
duplicate: vec![KeyCode::Char('y')],
delete: vec![KeyCode::Char('d')],
toggle_activate: vec![KeyCode::Char(' ')],
toggle_enabled: vec![KeyCode::Char('e')],
view_logs: vec![KeyCode::Char('l')],
}
}
}
impl Serialize for Keybindings {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let to_str = |k: &KeyCode| match k {
KeyCode::Char(c) => c.to_string(),
KeyCode::Esc => "esc".to_string(),
KeyCode::Enter => "enter".to_string(),
KeyCode::Up => "up".to_string(),
KeyCode::Down => "down".to_string(),
KeyCode::Left => "left".to_string(),
KeyCode::Right => "right".to_string(),
KeyCode::Backspace => "backspace".to_string(),
KeyCode::Tab => "tab".to_string(),
KeyCode::F(n) => format!("f{n}"),
_ => format!("{k:?}").to_lowercase(),
};
let mut map: HashMap<&str, Vec<String>> = HashMap::new();
map.insert("exit", self.exit.iter().map(to_str).collect());
map.insert("help", self.help.iter().map(to_str).collect());
map.insert("enter", self.enter.iter().map(to_str).collect());
map.insert("save", self.save.iter().map(to_str).collect());
map.insert("reload", self.reload.iter().map(to_str).collect());
map.insert("previous", self.previous.iter().map(to_str).collect());
map.insert("next", self.next.iter().map(to_str).collect());
map.insert("new", self.new.iter().map(to_str).collect());
map.insert("rename", self.rename.iter().map(to_str).collect());
map.insert("duplicate", self.duplicate.iter().map(to_str).collect());
map.insert("delete", self.delete.iter().map(to_str).collect());
map.insert(
"toggle_activate",
self.toggle_activate.iter().map(to_str).collect(),
);
map.insert(
"toggle_enabled",
self.toggle_enabled.iter().map(to_str).collect(),
);
map.insert("view_logs", self.view_logs.iter().map(to_str).collect());
map.serialize(serializer)
}
}
impl<'de> Deserialize<'de> for Keybindings {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let map = HashMap::<String, Vec<String>>::deserialize(deserializer)?;
let parse_keys = |key_name: &str| -> Result<Vec<KeyCode>, D::Error> {
let vec = map.get(key_name).ok_or_else(|| {
de::Error::missing_field(Box::leak(key_name.to_owned().into_boxed_str()))
})?;
vec.iter()
.map(|s| match s.to_lowercase().as_str() {
"esc" | "escape" => Ok(KeyCode::Esc),
"enter" | "return" => Ok(KeyCode::Enter),
"up" => Ok(KeyCode::Up),
"down" => Ok(KeyCode::Down),
"left" => Ok(KeyCode::Left),
"right" => Ok(KeyCode::Right),
"backspace" => Ok(KeyCode::Backspace),
"tab" => Ok(KeyCode::Tab),
s if s.len() == 1 => Ok(KeyCode::Char(s.chars().next().unwrap())),
s if s.starts_with('f') && s[1..].parse::<u8>().is_ok() => {
Ok(KeyCode::F(s[1..].parse::<u8>().unwrap()))
}
_ => Err(de::Error::custom(format!("unknown key binding: {s}"))),
})
.collect()
};
Ok(Keybindings {
exit: parse_keys("exit")?,
help: parse_keys("help")?,
enter: parse_keys("enter")?,
save: parse_keys("save")?,
reload: parse_keys("reload")?,
previous: parse_keys("previous")?,
next: parse_keys("next")?,
new: parse_keys("new")?,
rename: parse_keys("rename")?,
duplicate: parse_keys("duplicate")?,
delete: parse_keys("delete")?,
toggle_activate: parse_keys("toggle_activate")?,
toggle_enabled: parse_keys("toggle_enabled")?,
view_logs: parse_keys("view_logs")?,
})
}
}