hotkey_listener/
hotkey.rs1use crate::key::Key;
4use anyhow::{anyhow, Result};
5
6#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
8pub struct Modifiers {
9 pub shift: bool,
10 pub ctrl: bool,
11 pub alt: bool,
12}
13
14#[derive(Debug, Clone, PartialEq, Eq)]
16pub struct Hotkey {
17 pub key: Key,
18 pub modifiers: Modifiers,
19}
20
21impl Hotkey {
22 pub fn new(key: Key) -> Self {
24 Self {
25 key,
26 modifiers: Modifiers::default(),
27 }
28 }
29
30 pub fn with_modifiers(key: Key, modifiers: Modifiers) -> Self {
32 Self { key, modifiers }
33 }
34
35 pub fn with_shift(&self) -> Self {
37 Self {
38 key: self.key,
39 modifiers: Modifiers {
40 shift: true,
41 ctrl: self.modifiers.ctrl,
42 alt: self.modifiers.alt,
43 },
44 }
45 }
46}
47
48impl std::fmt::Display for Hotkey {
49 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
50 let mut parts = Vec::new();
51 if self.modifiers.ctrl {
52 parts.push("Ctrl".to_string());
53 }
54 if self.modifiers.alt {
55 parts.push("Alt".to_string());
56 }
57 if self.modifiers.shift {
58 parts.push("Shift".to_string());
59 }
60 parts.push(self.key.to_string());
61 write!(f, "{}", parts.join("+"))
62 }
63}
64
65pub fn parse_hotkey(s: &str) -> Result<Hotkey> {
67 let parts: Vec<&str> = s.split('+').collect();
68 let mut modifiers = Modifiers::default();
69
70 if parts.is_empty() {
71 return Err(anyhow!("Empty hotkey string"));
72 }
73
74 for part in &parts[..parts.len() - 1] {
76 match part.to_uppercase().as_str() {
77 "SHIFT" => modifiers.shift = true,
78 "CTRL" | "CONTROL" => modifiers.ctrl = true,
79 "ALT" => modifiers.alt = true,
80 _ => return Err(anyhow!("Unknown modifier: {}", part)),
81 }
82 }
83
84 let key_str = parts[parts.len() - 1];
86 let key = Key::parse(key_str)?;
87
88 Ok(Hotkey { key, modifiers })
89}
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94
95 #[test]
96 fn test_parse_simple_key() {
97 let hotkey = parse_hotkey("F8").unwrap();
98 assert_eq!(hotkey.key, Key::F8);
99 assert!(!hotkey.modifiers.shift);
100 assert!(!hotkey.modifiers.ctrl);
101 assert!(!hotkey.modifiers.alt);
102 }
103
104 #[test]
105 fn test_parse_with_shift() {
106 let hotkey = parse_hotkey("Shift+F8").unwrap();
107 assert_eq!(hotkey.key, Key::F8);
108 assert!(hotkey.modifiers.shift);
109 assert!(!hotkey.modifiers.ctrl);
110 assert!(!hotkey.modifiers.alt);
111 }
112
113 #[test]
114 fn test_parse_with_multiple_modifiers() {
115 let hotkey = parse_hotkey("Ctrl+Alt+F1").unwrap();
116 assert_eq!(hotkey.key, Key::F1);
117 assert!(!hotkey.modifiers.shift);
118 assert!(hotkey.modifiers.ctrl);
119 assert!(hotkey.modifiers.alt);
120 }
121
122 #[test]
123 fn test_parse_case_insensitive() {
124 let hotkey = parse_hotkey("SHIFT+f8").unwrap();
125 assert_eq!(hotkey.key, Key::F8);
126 assert!(hotkey.modifiers.shift);
127 }
128
129 #[test]
130 fn test_parse_unknown_key() {
131 assert!(parse_hotkey("Unknown").is_err());
132 }
133
134 #[test]
135 fn test_parse_unknown_modifier() {
136 assert!(parse_hotkey("Meta+F8").is_err());
137 }
138
139 #[test]
140 fn test_display() {
141 let hotkey = parse_hotkey("Shift+F8").unwrap();
142 assert_eq!(hotkey.to_string(), "Shift+F8");
143 }
144}