use crate::error::KeyParseError;
use crate::types::{Key, Modifier, Platform};
pub const STANDARD_KEY_MAPPINGS: &[(&str, Key, usize, usize, usize)] = &[
("Escape", Key::Escape, 0x1B, 1, 53),
("Enter", Key::Enter, 0x0D, 28, 36),
("Tab", Key::Tab, 0x09, 15, 48),
("Backspace", Key::Backspace, 0x08, 14, 51),
("Space", Key::Space, 0x20, 57, 49),
("Insert", Key::Insert, 0x2D, 110, 114),
("Delete", Key::Delete, 0x2E, 111, 117),
("Home", Key::Home, 0x24, 102, 115),
("End", Key::End, 0x23, 107, 119),
("PageUp", Key::PageUp, 0x21, 104, 116),
("PageDown", Key::PageDown, 0x22, 109, 121),
("ArrowLeft", Key::ArrowLeft, 0x25, 105, 123),
("ArrowUp", Key::ArrowUp, 0x26, 103, 126),
("ArrowRight", Key::ArrowRight, 0x27, 106, 124),
("ArrowDown", Key::ArrowDown, 0x28, 108, 125),
("A", Key::A, 0x41, 30, 0),
("B", Key::B, 0x42, 48, 11),
("C", Key::C, 0x43, 46, 8),
("D", Key::D, 0x44, 32, 2),
("E", Key::E, 0x45, 18, 14),
("F", Key::F, 0x46, 33, 3),
("G", Key::G, 0x47, 34, 5),
("H", Key::H, 0x48, 35, 4),
("I", Key::I, 0x49, 23, 34),
("J", Key::J, 0x4A, 36, 38),
("K", Key::K, 0x4B, 37, 40),
("L", Key::L, 0x4C, 38, 37),
("M", Key::M, 0x4D, 50, 46),
("N", Key::N, 0x4E, 49, 45),
("O", Key::O, 0x4F, 24, 31),
("P", Key::P, 0x50, 25, 35),
("Q", Key::Q, 0x51, 16, 12),
("R", Key::R, 0x52, 19, 15),
("S", Key::S, 0x53, 31, 1),
("T", Key::T, 0x54, 20, 17),
("U", Key::U, 0x55, 22, 32),
("V", Key::V, 0x56, 47, 9),
("W", Key::W, 0x57, 17, 13),
("X", Key::X, 0x58, 45, 7),
("Y", Key::Y, 0x59, 21, 16),
("Z", Key::Z, 0x5A, 44, 6),
("D0", Key::D0, 0x30, 11, 29),
("D1", Key::D1, 0x31, 2, 18),
("D2", Key::D2, 0x32, 3, 19),
("D3", Key::D3, 0x33, 4, 20),
("D4", Key::D4, 0x34, 5, 21),
("D5", Key::D5, 0x35, 6, 23),
("D6", Key::D6, 0x36, 7, 22),
("D7", Key::D7, 0x37, 8, 26),
("D8", Key::D8, 0x38, 9, 28),
("D9", Key::D9, 0x39, 10, 25),
("Num0", Key::Num0, 0x60, 82, 82),
("Num1", Key::Num1, 0x61, 79, 83),
("Num2", Key::Num2, 0x62, 80, 84),
("Num3", Key::Num3, 0x63, 81, 85),
("Num4", Key::Num4, 0x64, 75, 86),
("Num5", Key::Num5, 0x65, 76, 87),
("Num6", Key::Num6, 0x66, 77, 88),
("Num7", Key::Num7, 0x67, 71, 89),
("Num8", Key::Num8, 0x68, 72, 91),
("Num9", Key::Num9, 0x69, 73, 92),
("NumMultiply", Key::NumMultiply, 0x6A, 55, 67),
("NumAdd", Key::NumAdd, 0x6B, 78, 69),
("NumSubtract", Key::NumSubtract, 0x6D, 74, 78),
("NumDivide", Key::NumDivide, 0x6F, 53, 75),
("NumDecimal", Key::NumDecimal, 0x6E, 83, 65),
("F1", Key::F1, 0x70, 59, 122),
("F2", Key::F2, 0x71, 60, 120),
("F3", Key::F3, 0x72, 61, 99),
("F4", Key::F4, 0x73, 62, 118),
("F5", Key::F5, 0x74, 63, 96),
("F6", Key::F6, 0x75, 64, 97),
("F7", Key::F7, 0x76, 65, 98),
("F8", Key::F8, 0x77, 66, 100),
("F9", Key::F9, 0x78, 67, 101),
("F10", Key::F10, 0x79, 68, 109),
("F11", Key::F11, 0x7A, 87, 103),
("F12", Key::F12, 0x7B, 88, 111),
("F13", Key::F13, 0x7C, 183, 104),
("F14", Key::F14, 0x7D, 184, 105),
("F15", Key::F15, 0x7E, 185, 106),
("F16", Key::F16, 0x7F, 186, 107),
("F17", Key::F17, 0x80, 187, 108),
("F18", Key::F18, 0x81, 188, 110),
("F19", Key::F19, 0x82, 189, 112),
("F20", Key::F20, 0x83, 190, 113),
("F21", Key::F21, 0x84, 191, 102),
("F22", Key::F22, 0x85, 192, 121),
("F23", Key::F23, 0x86, 193, 122),
("F24", Key::F24, 0x87, 194, 123),
("CapsLock", Key::CapsLock, 0x14, 58, 57),
("NumLock", Key::NumLock, 0x90, 69, 71),
("ScrollLock", Key::ScrollLock, 0x91, 70, 72),
("Pause", Key::Pause, 0x13, 119, 117),
("Apps", Key::Apps, 0x5D, 135, 110),
("Sleep", Key::Sleep, 0x5F, 142, 142),
("MediaPlayPause", Key::MediaPlayPause, 0xB3, 164, 163),
("MediaStop", Key::MediaStop, 0xB2, 165, 165),
("MediaNext", Key::MediaNext, 0xB0, 163, 171),
("MediaPrevious", Key::MediaPrevious, 0xB1, 166, 172),
("VolumeUp", Key::VolumeUp, 0xAF, 123, 126),
("VolumeDown", Key::VolumeDown, 0xAE, 122, 125),
("VolumeMute", Key::VolumeMute, 0xAD, 121, 127),
("BrowserBack", Key::BrowserBack, 0xA6, 166, 178),
("BrowserForward", Key::BrowserForward, 0xA7, 167, 179),
("BrowserRefresh", Key::BrowserRefresh, 0xA8, 168, 177),
("BrowserHome", Key::BrowserHome, 0xAC, 178, 174),
];
pub const STANDARD_MODIFIER_MAPPINGS: &[(&str, Modifier, usize, usize, usize)] = &[
("Alt", Modifier::Alt, 0x12, 56, 61),
("Control", Modifier::Control, 0x11, 29, 59),
("Shift", Modifier::Shift, 0x10, 42, 60),
("Meta", Modifier::Meta, 0x5B, 125, 55),
("LeftAlt", Modifier::LeftAlt, 0xA4, 56, 61),
("RightAlt", Modifier::RightAlt, 0xA5, 100, 61),
("LeftControl", Modifier::LeftControl, 0xA2, 29, 59),
("RightControl", Modifier::RightControl, 0xA3, 97, 59),
("LeftShift", Modifier::LeftShift, 0xA0, 42, 60),
("RightShift", Modifier::RightShift, 0xA1, 54, 60),
("LeftMeta", Modifier::LeftMeta, 0x5B, 125, 55),
("RightMeta", Modifier::RightMeta, 0x5C, 126, 55),
];
pub fn parse_key_from_str(s: &str) -> Result<Key, KeyParseError> {
STANDARD_KEY_MAPPINGS
.iter()
.find(|(name, _, _, _, _)| *name == s)
.map(|(_, key, _, _, _)| *key)
.ok_or_else(|| KeyParseError::UnknownKey(s.to_string()))
}
pub fn parse_modifier_from_str(s: &str) -> Result<Modifier, KeyParseError> {
STANDARD_MODIFIER_MAPPINGS
.iter()
.find(|(name, _, _, _, _)| *name == s)
.map(|(_, modifier, _, _, _)| *modifier)
.ok_or_else(|| KeyParseError::UnknownModifier(s.to_string()))
}
pub fn parse_key_ignore_case(s: &str) -> Result<Key, KeyParseError> {
STANDARD_KEY_MAPPINGS
.iter()
.find(|(name, _, _, _, _)| name.eq_ignore_ascii_case(s))
.map(|(_, key, _, _, _)| *key)
.ok_or_else(|| KeyParseError::UnknownKey(s.to_string()))
}
pub fn parse_modifier_ignore_case(s: &str) -> Result<Modifier, KeyParseError> {
STANDARD_MODIFIER_MAPPINGS
.iter()
.find(|(name, _, _, _, _)| name.eq_ignore_ascii_case(s))
.map(|(_, modifier, _, _, _)| *modifier)
.ok_or_else(|| KeyParseError::UnknownModifier(s.to_string()))
}
pub fn key_to_code(key: Key, platform: Platform) -> usize {
STANDARD_KEY_MAPPINGS
.iter()
.find(|(_, k, _, _, _)| k == &key)
.map(|(_, _, win, linux, mac)| match platform {
Platform::Windows => *win,
Platform::Linux => *linux,
Platform::MacOS => *mac,
})
.unwrap_or_else(|| panic!("Unsupported key in standard mapping: {:?}", key))
}
pub fn modifier_to_code(modifier: Modifier, platform: Platform) -> usize {
STANDARD_MODIFIER_MAPPINGS
.iter()
.find(|(_, m, _, _, _)| m == &modifier)
.map(|(_, _, win, linux, mac)| match platform {
Platform::Windows => *win,
Platform::Linux => *linux,
Platform::MacOS => *mac,
})
.unwrap_or_else(|| panic!("Unsupported modifier in standard mapping: {:?}", modifier))
}
pub fn key_from_code(code: usize, platform: Platform) -> Option<Key> {
STANDARD_KEY_MAPPINGS
.iter()
.find(|(_, _, win, linux, mac)| match platform {
Platform::Windows => *win == code,
Platform::Linux => *linux == code,
Platform::MacOS => *mac == code,
})
.map(|(_, key, _, _, _)| *key)
}
pub fn modifier_from_code(code: usize, platform: Platform) -> Option<Modifier> {
STANDARD_MODIFIER_MAPPINGS
.iter()
.find(|(_, _, win, linux, mac)| match platform {
Platform::Windows => *win == code,
Platform::Linux => *linux == code,
Platform::MacOS => *mac == code,
})
.map(|(_, modifier, _, _, _)| *modifier)
}
impl crate::types::KeyCodeMapper for Key {
fn to_code(&self, platform: Platform) -> usize {
key_to_code(*self, platform)
}
fn from_code(code: usize, platform: Platform) -> Option<Self> {
key_from_code(code, platform)
}
}
impl crate::types::KeyCodeMapper for Modifier {
fn to_code(&self, platform: Platform) -> usize {
modifier_to_code(*self, platform)
}
fn from_code(code: usize, platform: Platform) -> Option<Self> {
modifier_from_code(code, platform)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_key_from_str() {
assert_eq!(parse_key_from_str("Escape").unwrap(), Key::Escape);
assert_eq!(parse_key_from_str("A").unwrap(), Key::A);
assert!(parse_key_from_str("UnknownKey").is_err());
}
#[test]
fn test_parse_modifier_from_str() {
assert_eq!(parse_modifier_from_str("Shift").unwrap(), Modifier::Shift);
assert_eq!(
parse_modifier_from_str("Control").unwrap(),
Modifier::Control
);
assert!(parse_modifier_from_str("UnknownModifier").is_err());
}
#[test]
fn test_parse_key_ignore_case() {
assert_eq!(parse_key_ignore_case("escape").unwrap(), Key::Escape);
assert_eq!(parse_key_ignore_case("ESCAPE").unwrap(), Key::Escape);
assert_eq!(parse_key_ignore_case("Escape").unwrap(), Key::Escape);
assert_eq!(parse_key_ignore_case("a").unwrap(), Key::A);
assert_eq!(parse_key_ignore_case("A").unwrap(), Key::A);
}
#[test]
fn test_parse_modifier_ignore_case() {
assert_eq!(
parse_modifier_ignore_case("shift").unwrap(),
Modifier::Shift
);
assert_eq!(
parse_modifier_ignore_case("SHIFT").unwrap(),
Modifier::Shift
);
assert_eq!(
parse_modifier_ignore_case("Shift").unwrap(),
Modifier::Shift
);
assert_eq!(
parse_modifier_ignore_case("control").unwrap(),
Modifier::Control
);
assert_eq!(
parse_modifier_ignore_case("CONTROL").unwrap(),
Modifier::Control
);
}
#[test]
fn test_key_to_code() {
assert_eq!(key_to_code(Key::Enter, Platform::Windows), 0x0D);
assert_eq!(key_to_code(Key::Enter, Platform::Linux), 28);
assert_eq!(key_to_code(Key::Enter, Platform::MacOS), 36);
}
#[test]
fn test_key_from_code() {
assert_eq!(key_from_code(0x1B, Platform::Windows), Some(Key::Escape));
assert_eq!(key_from_code(1, Platform::Linux), Some(Key::Escape));
assert_eq!(key_from_code(53, Platform::MacOS), Some(Key::Escape));
}
#[test]
fn test_key_code_mapper_impl() {
use crate::types::KeyCodeMapper;
let key = Key::Enter;
assert_eq!(key.to_code(Platform::Windows), 0x0D);
assert_eq!(Key::from_code(0x0D, Platform::Windows), Some(Key::Enter));
}
}