I have a .toml
config file containing some key bindings expressed as chars, I need to deserialize that file to a struct where each field is a crossterm::event::KeyCode
. I'm using toml
crate to parse the string. What I thought is that maybe there is a way to deserialize the string parsing keys as char
and then mapping them to KeyCode
.
config.toml:
key_0 = 'x'
key_bindings.rs:
use crossterm::event::KeyCode;
use serde::Deserialize;
#[derive(Deserialize, Debug)]
pub struct KeyBindings {
pub key_0: KeyCode,
}
How can I deserialize the config.toml file to the KeyBindings
struct?
To my knowledge, there's neither a crate to do this parsing from a string to a crossterm key, nor even a normalized format.
You'll also have to deal with specifics of both Crossterm and your application. It especially depends on how you want to use the key. For example, if you want to parameterize a mapping from an event to an action, you may want to transform the key a little so that it matches what Crossterm may produce.
You might use a function like this one:
/// parse a string as a keyboard key definition.
///
/// About the case:
/// The char we receive as code from crossterm is usually lowercase
/// but uppercase when it was typed with shift (i.e. we receive
/// "g" for a lowercase, and "shift-G" for an uppercase)
pub fn parse_key(raw: &str) -> Result<KeyEvent, ConfError> {
fn bad_key(raw: &str) -> Result<KeyEvent, ConfError> {
Err(ConfError::InvalidKey {
raw: raw.to_owned(),
})
}
let tokens: Vec<&str> = raw.split('-').collect();
let last = tokens[tokens.len() - 1].to_ascii_lowercase();
let mut code = match last.as_ref() {
"esc" => Esc,
"enter" => Enter,
"left" => Left,
"right" => Right,
"up" => Up,
"down" => Down,
"home" => Home,
"end" => End,
"pageup" => PageUp,
"pagedown" => PageDown,
"backtab" => BackTab,
"backspace" => Backspace,
"del" => Delete,
"delete" => Delete,
"insert" => Insert,
"ins" => Insert,
"f1" => F(1),
"f2" => F(2),
"f3" => F(3),
"f4" => F(4),
"f5" => F(5),
"f6" => F(6),
"f7" => F(7),
"f8" => F(8),
"f9" => F(9),
"f10" => F(10),
"f11" => F(11),
"f12" => F(12),
"space" => Char(' '),
"tab" => Tab,
c if c.len() == 1 => Char(c.chars().next().unwrap()),
_ => {
return bad_key(raw);
}
};
let mut modifiers = KeyModifiers::empty();
if code == BackTab {
// Crossterm always sends the shift key with backtab
modifiers.insert(KeyModifiers::SHIFT);
}
for token in tokens.iter().take(tokens.len() - 1) {
match token.to_ascii_lowercase().as_ref() {
"ctrl" => {
modifiers.insert(KeyModifiers::CONTROL);
}
"alt" => {
modifiers.insert(KeyModifiers::ALT);
}
"shift" => {
modifiers.insert(KeyModifiers::SHIFT);
if let Char(c) = code {
if c.is_ascii_lowercase() {
code = Char(c.to_ascii_uppercase());
}
}
}
_ => {
return bad_key(raw);
}
}
}
Ok(KeyEvent { code, modifiers })
}
source: keys.rs in broot