pub(crate) fn parse_key_combo(combo: &str) -> (i32, &str) {
use crate::cdp::types::modifiers;
let parts: Vec<&str> = combo.split('+').collect();
let mut mods = 0;
let mut key = combo;
for (i, part) in parts.iter().enumerate() {
match part.to_lowercase().as_str() {
"ctrl" | "control" => mods |= modifiers::CTRL,
"alt" | "option" => mods |= modifiers::ALT,
"shift" => mods |= modifiers::SHIFT,
"cmd" | "meta" | "command" => mods |= modifiers::META,
_ => key = parts[i],
}
}
if key.is_empty() {
key = "+";
}
(mods, key)
}
pub(crate) fn key_to_codes(key: &str) -> (&str, &str, Option<i32>) {
static KEYS: &[(&str, &str, &str, i32)] = &[
("enter", "Enter", "Enter", 13),
("return", "Enter", "Enter", 13),
("tab", "Tab", "Tab", 9),
("escape", "Escape", "Escape", 27),
("esc", "Escape", "Escape", 27),
("backspace", "Backspace", "Backspace", 8),
("delete", "Delete", "Delete", 46),
("arrowup", "ArrowUp", "ArrowUp", 38),
("up", "ArrowUp", "ArrowUp", 38),
("arrowdown", "ArrowDown", "ArrowDown", 40),
("down", "ArrowDown", "ArrowDown", 40),
("arrowleft", "ArrowLeft", "ArrowLeft", 37),
("left", "ArrowLeft", "ArrowLeft", 37),
("arrowright", "ArrowRight", "ArrowRight", 39),
("right", "ArrowRight", "ArrowRight", 39),
("home", "Home", "Home", 36),
("end", "End", "End", 35),
("pageup", "PageUp", "PageUp", 33),
("pagedown", "PageDown", "PageDown", 34),
("space", " ", "Space", 32),
("a", "a", "KeyA", 65),
("b", "b", "KeyB", 66),
("c", "c", "KeyC", 67),
("d", "d", "KeyD", 68),
("e", "e", "KeyE", 69),
("f", "f", "KeyF", 70),
("g", "g", "KeyG", 71),
("h", "h", "KeyH", 72),
("i", "i", "KeyI", 73),
("j", "j", "KeyJ", 74),
("k", "k", "KeyK", 75),
("l", "l", "KeyL", 76),
("m", "m", "KeyM", 77),
("n", "n", "KeyN", 78),
("o", "o", "KeyO", 79),
("p", "p", "KeyP", 80),
("q", "q", "KeyQ", 81),
("r", "r", "KeyR", 82),
("s", "s", "KeyS", 83),
("t", "t", "KeyT", 84),
("u", "u", "KeyU", 85),
("v", "v", "KeyV", 86),
("w", "w", "KeyW", 87),
("x", "x", "KeyX", 88),
("y", "y", "KeyY", 89),
("z", "z", "KeyZ", 90),
("f1", "F1", "F1", 112),
("f2", "F2", "F2", 113),
("f3", "F3", "F3", 114),
("f4", "F4", "F4", 115),
("f5", "F5", "F5", 116),
("f6", "F6", "F6", 117),
("f7", "F7", "F7", 118),
("f8", "F8", "F8", 119),
("f9", "F9", "F9", 120),
("f10", "F10", "F10", 121),
("f11", "F11", "F11", 122),
("f12", "F12", "F12", 123),
];
let lower = key.to_lowercase();
KEYS.iter()
.find(|(name, _, _, _)| *name == lower)
.map(|(_, k, c, vk)| (*k, *c, Some(*vk)))
.unwrap_or((key, key, None))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_key_combo_simple() {
let (mods, key) = parse_key_combo("Enter");
assert_eq!(mods, 0);
assert_eq!(key, "Enter");
}
#[test]
fn test_parse_key_combo_ctrl() {
use crate::cdp::types::modifiers;
let (mods, key) = parse_key_combo("Ctrl+A");
assert_eq!(mods, modifiers::CTRL);
assert_eq!(key, "A");
}
#[test]
fn test_parse_key_combo_cmd_shift() {
use crate::cdp::types::modifiers;
let (mods, key) = parse_key_combo("Cmd+Shift+S");
assert_eq!(mods, modifiers::META | modifiers::SHIFT);
assert_eq!(key, "S");
}
#[test]
fn test_parse_key_combo_all_modifiers() {
use crate::cdp::types::modifiers;
let (mods, key) = parse_key_combo("Ctrl+Alt+Shift+Cmd+X");
assert_eq!(
mods,
modifiers::CTRL | modifiers::ALT | modifiers::SHIFT | modifiers::META
);
assert_eq!(key, "X");
}
#[test]
fn test_parse_key_combo_case_insensitive() {
use crate::cdp::types::modifiers;
let (mods, key) = parse_key_combo("ctrl+a");
assert_eq!(mods, modifiers::CTRL);
assert_eq!(key, "a");
}
#[test]
fn test_key_to_codes_enter() {
let (key, code, vk) = key_to_codes("Enter");
assert_eq!(key, "Enter");
assert_eq!(code, "Enter");
assert_eq!(vk, Some(13));
}
#[test]
fn test_key_to_codes_tab() {
let (key, code, vk) = key_to_codes("Tab");
assert_eq!(key, "Tab");
assert_eq!(code, "Tab");
assert_eq!(vk, Some(9));
}
#[test]
fn test_key_to_codes_letter() {
let (key, code, vk) = key_to_codes("a");
assert_eq!(key, "a");
assert_eq!(code, "KeyA");
assert_eq!(vk, Some(65));
}
#[test]
fn test_key_to_codes_arrow() {
let (key, code, vk) = key_to_codes("ArrowDown");
assert_eq!(key, "ArrowDown");
assert_eq!(code, "ArrowDown");
assert_eq!(vk, Some(40));
}
#[test]
fn test_key_to_codes_case_insensitive() {
let (key, code, vk) = key_to_codes("ESCAPE");
assert_eq!(key, "Escape");
assert_eq!(code, "Escape");
assert_eq!(vk, Some(27));
}
#[test]
fn test_key_to_codes_alias() {
let (key, code, vk) = key_to_codes("esc");
assert_eq!(key, "Escape");
assert_eq!(code, "Escape");
assert_eq!(vk, Some(27));
let (key, code, vk) = key_to_codes("up");
assert_eq!(key, "ArrowUp");
assert_eq!(code, "ArrowUp");
assert_eq!(vk, Some(38));
}
#[test]
fn test_key_to_codes_unknown() {
let (key, code, vk) = key_to_codes("SomeWeirdKey");
assert_eq!(key, "SomeWeirdKey");
assert_eq!(code, "SomeWeirdKey");
assert_eq!(vk, None);
}
#[test]
fn test_parse_key_combo_literal_plus() {
let (mods, key) = parse_key_combo("+");
assert_eq!(mods, 0);
assert_eq!(key, "+");
}
#[test]
fn test_parse_key_combo_shift_plus() {
use crate::cdp::types::modifiers;
let (mods, key) = parse_key_combo("Shift++");
assert_eq!(mods, modifiers::SHIFT);
assert_eq!(key, "+");
}
}