Skip to main content

handy_keys/types/
key.rs

1//! Keyboard key and mouse button definitions and parsing
2
3use serde::{Deserialize, Serialize};
4use std::fmt;
5use std::str::FromStr;
6
7use crate::error::{Error, Result};
8
9/// Keyboard keys and mouse buttons that can be used in hotkey combinations
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
11#[non_exhaustive]
12pub enum Key {
13    // Letters
14    A,
15    B,
16    C,
17    D,
18    E,
19    F,
20    G,
21    H,
22    I,
23    J,
24    K,
25    L,
26    M,
27    N,
28    O,
29    P,
30    Q,
31    R,
32    S,
33    T,
34    U,
35    V,
36    W,
37    X,
38    Y,
39    Z,
40
41    // Numbers
42    Num0,
43    Num1,
44    Num2,
45    Num3,
46    Num4,
47    Num5,
48    Num6,
49    Num7,
50    Num8,
51    Num9,
52
53    // Function keys
54    F1,
55    F2,
56    F3,
57    F4,
58    F5,
59    F6,
60    F7,
61    F8,
62    F9,
63    F10,
64    F11,
65    F12,
66    F13,
67    F14,
68    F15,
69    F16,
70    F17,
71    F18,
72    F19,
73    F20,
74
75    // Special keys
76    Space,
77    Return,
78    Tab,
79    Escape,
80    Delete,
81    ForwardDelete,
82    Insert,
83    Home,
84    End,
85    PageUp,
86    PageDown,
87
88    // Arrow keys
89    LeftArrow,
90    RightArrow,
91    UpArrow,
92    DownArrow,
93
94    // Punctuation and symbols
95    Minus,
96    Equal,
97    LeftBracket,
98    RightBracket,
99    Backslash,
100    Semicolon,
101    Quote,
102    Comma,
103    Period,
104    Slash,
105    Grave,
106    Section,
107    // JIS keyboard keys
108    JisYen,
109    JisUnderscore,
110    JisEisu,
111    JisKana,
112
113    // Keypad
114    Keypad0,
115    Keypad1,
116    Keypad2,
117    Keypad3,
118    Keypad4,
119    Keypad5,
120    Keypad6,
121    Keypad7,
122    Keypad8,
123    Keypad9,
124    KeypadDecimal,
125    KeypadMultiply,
126    KeypadPlus,
127    KeypadClear,
128    KeypadDivide,
129    KeypadEnter,
130    KeypadMinus,
131    KeypadEquals,
132    KeypadComma,
133
134    // Lock keys
135    CapsLock,
136    ScrollLock,
137    NumLock,
138
139    // Mouse buttons
140    MouseLeft,
141    MouseRight,
142    MouseMiddle,
143    /// Extra button 1 (often "back" on mice with side buttons)
144    MouseX1,
145    /// Extra button 2 (often "forward" on mice with side buttons)
146    MouseX2,
147}
148
149impl fmt::Display for Key {
150    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
151        match self {
152            Key::A => write!(f, "A"),
153            Key::B => write!(f, "B"),
154            Key::C => write!(f, "C"),
155            Key::D => write!(f, "D"),
156            Key::E => write!(f, "E"),
157            Key::F => write!(f, "F"),
158            Key::G => write!(f, "G"),
159            Key::H => write!(f, "H"),
160            Key::I => write!(f, "I"),
161            Key::J => write!(f, "J"),
162            Key::K => write!(f, "K"),
163            Key::L => write!(f, "L"),
164            Key::M => write!(f, "M"),
165            Key::N => write!(f, "N"),
166            Key::O => write!(f, "O"),
167            Key::P => write!(f, "P"),
168            Key::Q => write!(f, "Q"),
169            Key::R => write!(f, "R"),
170            Key::S => write!(f, "S"),
171            Key::T => write!(f, "T"),
172            Key::U => write!(f, "U"),
173            Key::V => write!(f, "V"),
174            Key::W => write!(f, "W"),
175            Key::X => write!(f, "X"),
176            Key::Y => write!(f, "Y"),
177            Key::Z => write!(f, "Z"),
178            Key::Num0 => write!(f, "0"),
179            Key::Num1 => write!(f, "1"),
180            Key::Num2 => write!(f, "2"),
181            Key::Num3 => write!(f, "3"),
182            Key::Num4 => write!(f, "4"),
183            Key::Num5 => write!(f, "5"),
184            Key::Num6 => write!(f, "6"),
185            Key::Num7 => write!(f, "7"),
186            Key::Num8 => write!(f, "8"),
187            Key::Num9 => write!(f, "9"),
188            Key::F1 => write!(f, "F1"),
189            Key::F2 => write!(f, "F2"),
190            Key::F3 => write!(f, "F3"),
191            Key::F4 => write!(f, "F4"),
192            Key::F5 => write!(f, "F5"),
193            Key::F6 => write!(f, "F6"),
194            Key::F7 => write!(f, "F7"),
195            Key::F8 => write!(f, "F8"),
196            Key::F9 => write!(f, "F9"),
197            Key::F10 => write!(f, "F10"),
198            Key::F11 => write!(f, "F11"),
199            Key::F12 => write!(f, "F12"),
200            Key::F13 => write!(f, "F13"),
201            Key::F14 => write!(f, "F14"),
202            Key::F15 => write!(f, "F15"),
203            Key::F16 => write!(f, "F16"),
204            Key::F17 => write!(f, "F17"),
205            Key::F18 => write!(f, "F18"),
206            Key::F19 => write!(f, "F19"),
207            Key::F20 => write!(f, "F20"),
208            Key::Space => write!(f, "Space"),
209            Key::Return => write!(f, "Return"),
210            Key::Tab => write!(f, "Tab"),
211            Key::Escape => write!(f, "Escape"),
212            Key::Delete => write!(f, "Delete"),
213            Key::ForwardDelete => write!(f, "ForwardDelete"),
214            Key::Insert => write!(f, "Insert"),
215            Key::Home => write!(f, "Home"),
216            Key::End => write!(f, "End"),
217            Key::PageUp => write!(f, "PageUp"),
218            Key::PageDown => write!(f, "PageDown"),
219            Key::LeftArrow => write!(f, "Left"),
220            Key::RightArrow => write!(f, "Right"),
221            Key::UpArrow => write!(f, "Up"),
222            Key::DownArrow => write!(f, "Down"),
223            Key::Minus => write!(f, "-"),
224            Key::Equal => write!(f, "="),
225            Key::LeftBracket => write!(f, "["),
226            Key::RightBracket => write!(f, "]"),
227            Key::Backslash => write!(f, "\\"),
228            Key::Semicolon => write!(f, ";"),
229            Key::Quote => write!(f, "'"),
230            Key::Comma => write!(f, ","),
231            Key::Period => write!(f, "."),
232            Key::Slash => write!(f, "/"),
233            Key::Grave => write!(f, "`"),
234            Key::Section => write!(f, "§"),
235            Key::JisYen => write!(f, "¥"),
236            Key::JisUnderscore => write!(f, "JisUnderscore"),
237            Key::JisEisu => write!(f, "Eisu"),
238            Key::JisKana => write!(f, "Kana"),
239            Key::Keypad0 => write!(f, "Keypad0"),
240            Key::Keypad1 => write!(f, "Keypad1"),
241            Key::Keypad2 => write!(f, "Keypad2"),
242            Key::Keypad3 => write!(f, "Keypad3"),
243            Key::Keypad4 => write!(f, "Keypad4"),
244            Key::Keypad5 => write!(f, "Keypad5"),
245            Key::Keypad6 => write!(f, "Keypad6"),
246            Key::Keypad7 => write!(f, "Keypad7"),
247            Key::Keypad8 => write!(f, "Keypad8"),
248            Key::Keypad9 => write!(f, "Keypad9"),
249            Key::KeypadDecimal => write!(f, "KeypadDecimal"),
250            Key::KeypadMultiply => write!(f, "KeypadMultiply"),
251            Key::KeypadPlus => write!(f, "KeypadPlus"),
252            Key::KeypadClear => write!(f, "KeypadClear"),
253            Key::KeypadDivide => write!(f, "KeypadDivide"),
254            Key::KeypadEnter => write!(f, "KeypadEnter"),
255            Key::KeypadMinus => write!(f, "KeypadMinus"),
256            Key::KeypadEquals => write!(f, "KeypadEquals"),
257            Key::KeypadComma => write!(f, "KeypadComma"),
258            Key::CapsLock => write!(f, "CapsLock"),
259            Key::ScrollLock => write!(f, "ScrollLock"),
260            Key::NumLock => write!(f, "NumLock"),
261            Key::MouseLeft => write!(f, "MouseLeft"),
262            Key::MouseRight => write!(f, "MouseRight"),
263            Key::MouseMiddle => write!(f, "MouseMiddle"),
264            Key::MouseX1 => write!(f, "MouseX1"),
265            Key::MouseX2 => write!(f, "MouseX2"),
266        }
267    }
268}
269
270impl FromStr for Key {
271    type Err = Error;
272
273    /// Parse a key from its string representation (case-insensitive)
274    fn from_str(s: &str) -> Result<Self> {
275        let s = s.trim();
276        match s.to_lowercase().as_str() {
277            // Letters
278            "a" => Ok(Key::A),
279            "b" => Ok(Key::B),
280            "c" => Ok(Key::C),
281            "d" => Ok(Key::D),
282            "e" => Ok(Key::E),
283            "f" => Ok(Key::F),
284            "g" => Ok(Key::G),
285            "h" => Ok(Key::H),
286            "i" => Ok(Key::I),
287            "j" => Ok(Key::J),
288            "k" => Ok(Key::K),
289            "l" => Ok(Key::L),
290            "m" => Ok(Key::M),
291            "n" => Ok(Key::N),
292            "o" => Ok(Key::O),
293            "p" => Ok(Key::P),
294            "q" => Ok(Key::Q),
295            "r" => Ok(Key::R),
296            "s" => Ok(Key::S),
297            "t" => Ok(Key::T),
298            "u" => Ok(Key::U),
299            "v" => Ok(Key::V),
300            "w" => Ok(Key::W),
301            "x" => Ok(Key::X),
302            "y" => Ok(Key::Y),
303            "z" => Ok(Key::Z),
304
305            // Numbers
306            "0" | "num0" => Ok(Key::Num0),
307            "1" | "num1" => Ok(Key::Num1),
308            "2" | "num2" => Ok(Key::Num2),
309            "3" | "num3" => Ok(Key::Num3),
310            "4" | "num4" => Ok(Key::Num4),
311            "5" | "num5" => Ok(Key::Num5),
312            "6" | "num6" => Ok(Key::Num6),
313            "7" | "num7" => Ok(Key::Num7),
314            "8" | "num8" => Ok(Key::Num8),
315            "9" | "num9" => Ok(Key::Num9),
316
317            // Function keys
318            "f1" => Ok(Key::F1),
319            "f2" => Ok(Key::F2),
320            "f3" => Ok(Key::F3),
321            "f4" => Ok(Key::F4),
322            "f5" => Ok(Key::F5),
323            "f6" => Ok(Key::F6),
324            "f7" => Ok(Key::F7),
325            "f8" => Ok(Key::F8),
326            "f9" => Ok(Key::F9),
327            "f10" => Ok(Key::F10),
328            "f11" => Ok(Key::F11),
329            "f12" => Ok(Key::F12),
330            "f13" => Ok(Key::F13),
331            "f14" => Ok(Key::F14),
332            "f15" => Ok(Key::F15),
333            "f16" => Ok(Key::F16),
334            "f17" => Ok(Key::F17),
335            "f18" => Ok(Key::F18),
336            "f19" => Ok(Key::F19),
337            "f20" => Ok(Key::F20),
338
339            // Special keys
340            "space" | " " => Ok(Key::Space),
341            "return" | "enter" => Ok(Key::Return),
342            "tab" => Ok(Key::Tab),
343            "escape" | "esc" => Ok(Key::Escape),
344            "delete" | "backspace" => Ok(Key::Delete),
345            "forwarddelete" | "del" => Ok(Key::ForwardDelete),
346            "insert" | "ins" => Ok(Key::Insert),
347            "home" => Ok(Key::Home),
348            "end" => Ok(Key::End),
349            "pageup" => Ok(Key::PageUp),
350            "pagedown" => Ok(Key::PageDown),
351
352            // Arrow keys
353            "left" | "leftarrow" => Ok(Key::LeftArrow),
354            "right" | "rightarrow" => Ok(Key::RightArrow),
355            "up" | "uparrow" => Ok(Key::UpArrow),
356            "down" | "downarrow" => Ok(Key::DownArrow),
357
358            // Punctuation and symbols
359            "-" | "minus" => Ok(Key::Minus),
360            "=" | "equal" | "equals" => Ok(Key::Equal),
361            "[" | "leftbracket" => Ok(Key::LeftBracket),
362            "]" | "rightbracket" => Ok(Key::RightBracket),
363            "\\" | "backslash" => Ok(Key::Backslash),
364            ";" | "semicolon" => Ok(Key::Semicolon),
365            "'" | "quote" => Ok(Key::Quote),
366            "," | "comma" => Ok(Key::Comma),
367            "." | "period" => Ok(Key::Period),
368            "/" | "slash" => Ok(Key::Slash),
369            "`" | "grave" | "backtick" => Ok(Key::Grave),
370            "§" | "section" => Ok(Key::Section),
371            "¥" | "jisyen" | "yen" => Ok(Key::JisYen),
372            "jisunderscore" => Ok(Key::JisUnderscore),
373            "eisu" | "jiseisu" | "英数" => Ok(Key::JisEisu),
374            "kana" | "jiskana" | "かな" => Ok(Key::JisKana),
375
376            // Keypad
377            "keypad0" => Ok(Key::Keypad0),
378            "keypad1" => Ok(Key::Keypad1),
379            "keypad2" => Ok(Key::Keypad2),
380            "keypad3" => Ok(Key::Keypad3),
381            "keypad4" => Ok(Key::Keypad4),
382            "keypad5" => Ok(Key::Keypad5),
383            "keypad6" => Ok(Key::Keypad6),
384            "keypad7" => Ok(Key::Keypad7),
385            "keypad8" => Ok(Key::Keypad8),
386            "keypad9" => Ok(Key::Keypad9),
387            "keypad." | "keypaddecimal" => Ok(Key::KeypadDecimal),
388            "keypad*" | "keypadmultiply" => Ok(Key::KeypadMultiply),
389            "keypad+" | "keypadplus" => Ok(Key::KeypadPlus),
390            "keypadclear" => Ok(Key::KeypadClear),
391            "keypad/" | "keypaddivide" => Ok(Key::KeypadDivide),
392            "keypadenter" => Ok(Key::KeypadEnter),
393            "keypad-" | "keypadminus" => Ok(Key::KeypadMinus),
394            "keypad=" | "keypadequals" => Ok(Key::KeypadEquals),
395            "keypad," | "keypadcomma" => Ok(Key::KeypadComma),
396
397            // Lock keys
398            "capslock" | "caps" => Ok(Key::CapsLock),
399            "scrolllock" | "scroll" => Ok(Key::ScrollLock),
400            "numlock" => Ok(Key::NumLock),
401
402            // Mouse buttons
403            "mouseleft" | "leftclick" | "lmb" | "mouse1" => Ok(Key::MouseLeft),
404            "mouseright" | "rightclick" | "rmb" | "mouse2" => Ok(Key::MouseRight),
405            "mousemiddle" | "middleclick" | "mmb" | "mouse3" => Ok(Key::MouseMiddle),
406            "mousex1" | "mouse4" | "back" | "xbutton1" => Ok(Key::MouseX1),
407            "mousex2" | "mouse5" | "forward" | "xbutton2" => Ok(Key::MouseX2),
408
409            _ => Err(Error::UnknownKey(s.to_string())),
410        }
411    }
412}
413
414#[cfg(test)]
415mod tests {
416    use super::*;
417
418    #[test]
419    fn parse_letters() {
420        assert_eq!("a".parse::<Key>().unwrap(), Key::A);
421        assert_eq!("A".parse::<Key>().unwrap(), Key::A);
422        assert_eq!("z".parse::<Key>().unwrap(), Key::Z);
423    }
424
425    #[test]
426    fn parse_numbers() {
427        assert_eq!("0".parse::<Key>().unwrap(), Key::Num0);
428        assert_eq!("9".parse::<Key>().unwrap(), Key::Num9);
429        assert_eq!("num5".parse::<Key>().unwrap(), Key::Num5);
430    }
431
432    #[test]
433    fn parse_function_keys() {
434        assert_eq!("F1".parse::<Key>().unwrap(), Key::F1);
435        assert_eq!("f12".parse::<Key>().unwrap(), Key::F12);
436        assert_eq!("F20".parse::<Key>().unwrap(), Key::F20);
437    }
438
439    #[test]
440    fn parse_special_keys() {
441        assert_eq!("Space".parse::<Key>().unwrap(), Key::Space);
442        assert_eq!("return".parse::<Key>().unwrap(), Key::Return);
443        assert_eq!("enter".parse::<Key>().unwrap(), Key::Return);
444        assert_eq!("Tab".parse::<Key>().unwrap(), Key::Tab);
445        assert_eq!("Escape".parse::<Key>().unwrap(), Key::Escape);
446        assert_eq!("esc".parse::<Key>().unwrap(), Key::Escape);
447        assert_eq!("Delete".parse::<Key>().unwrap(), Key::Delete);
448        assert_eq!("backspace".parse::<Key>().unwrap(), Key::Delete);
449    }
450
451    #[test]
452    fn parse_arrow_keys() {
453        assert_eq!("Left".parse::<Key>().unwrap(), Key::LeftArrow);
454        assert_eq!("leftarrow".parse::<Key>().unwrap(), Key::LeftArrow);
455        assert_eq!("Right".parse::<Key>().unwrap(), Key::RightArrow);
456        assert_eq!("Up".parse::<Key>().unwrap(), Key::UpArrow);
457        assert_eq!("Down".parse::<Key>().unwrap(), Key::DownArrow);
458    }
459
460    #[test]
461    fn parse_punctuation() {
462        assert_eq!("-".parse::<Key>().unwrap(), Key::Minus);
463        assert_eq!("minus".parse::<Key>().unwrap(), Key::Minus);
464        assert_eq!("=".parse::<Key>().unwrap(), Key::Equal);
465        assert_eq!("[".parse::<Key>().unwrap(), Key::LeftBracket);
466        assert_eq!("]".parse::<Key>().unwrap(), Key::RightBracket);
467        assert_eq!("/".parse::<Key>().unwrap(), Key::Slash);
468        assert_eq!("`".parse::<Key>().unwrap(), Key::Grave);
469    }
470
471    #[test]
472    fn parse_unknown_key_fails() {
473        assert!("unknown".parse::<Key>().is_err());
474        assert!("".parse::<Key>().is_err());
475    }
476
477    #[test]
478    fn key_display_roundtrip() {
479        // Test that parsing the display output gives the same key
480        let keys = [
481            Key::A,
482            Key::Z,
483            Key::Num0,
484            Key::Num9,
485            Key::F1,
486            Key::F12,
487            Key::Space,
488            Key::Return,
489            Key::Tab,
490            Key::Escape,
491            Key::LeftArrow,
492            Key::RightArrow,
493            Key::KeypadPlus,
494            Key::KeypadMinus,
495            Key::KeypadMultiply,
496            Key::KeypadDivide,
497            Key::KeypadDecimal,
498            Key::KeypadEquals,
499            Key::KeypadEnter,
500            Key::KeypadClear,
501            Key::KeypadComma,
502            Key::JisYen,
503            Key::JisUnderscore,
504            Key::JisEisu,
505            Key::JisKana,
506        ];
507        for key in keys {
508            let displayed = format!("{}", key);
509            let parsed: Key = displayed.parse().unwrap();
510            assert_eq!(parsed, key, "Roundtrip failed for {:?}", key);
511        }
512    }
513}