keyboard_codes/types/
keyboard_input.rs

1use crate::error::KeyParseError;
2use crate::mapping::standard::parse_key_ignore_case;
3use crate::parser::{normalize_key_name, parse_modifier_with_aliases};
4use crate::types::{Key, KeyCodeMapper, Modifier, Platform};
5
6/// Unified keyboard input type that can represent both keys and modifiers
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
8#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
9pub enum KeyboardInput {
10    /// Regular key
11    Key(Key),
12    /// Modifier key
13    Modifier(Modifier),
14}
15
16impl KeyboardInput {
17    /// Get the string representation of the keyboard input
18    pub fn as_str(&self) -> &'static str {
19        match self {
20            KeyboardInput::Key(key) => key.as_str(),
21            KeyboardInput::Modifier(modifier) => modifier.as_str(),
22        }
23    }
24
25    /// Convert to platform-specific code
26    pub fn to_code(&self, platform: Platform) -> usize {
27        match self {
28            KeyboardInput::Key(key) => KeyCodeMapper::to_code(key, platform),
29            KeyboardInput::Modifier(modifier) => KeyCodeMapper::to_code(modifier, platform),
30        }
31    }
32
33    /// Parse from platform-specific code
34    pub fn from_code(code: usize, platform: Platform) -> Option<Self> {
35        // Try to parse as Key first, then as Modifier
36        Key::from_code(code, platform)
37            .map(KeyboardInput::Key)
38            .or_else(|| Modifier::from_code(code, platform).map(KeyboardInput::Modifier))
39    }
40
41    /// Check if this is a regular key
42    pub fn is_key(&self) -> bool {
43        matches!(self, KeyboardInput::Key(_))
44    }
45
46    /// Check if this is a modifier key
47    pub fn is_modifier(&self) -> bool {
48        matches!(self, KeyboardInput::Modifier(_))
49    }
50
51    /// Get the inner key if this is a Key variant
52    pub fn as_key(&self) -> Option<Key> {
53        match self {
54            KeyboardInput::Key(key) => Some(*key),
55            _ => None,
56        }
57    }
58
59    /// Get the inner modifier if this is a Modifier variant
60    pub fn as_modifier(&self) -> Option<Modifier> {
61        match self {
62            KeyboardInput::Modifier(modifier) => Some(*modifier),
63            _ => None,
64        }
65    }
66
67    /// Parse with alias and case-insensitive support
68    pub fn parse_with_aliases(input: &str) -> Result<Self, KeyParseError> {
69        if input.is_empty() {
70            return Err(KeyParseError::UnknownKey("empty string".to_string()));
71        }
72
73        // First try to parse as modifier with aliases
74        if let Ok(modifier) = parse_modifier_with_aliases(input) {
75            return Ok(KeyboardInput::Modifier(modifier));
76        }
77
78        // Then try to parse as key with aliases
79        let normalized_key = normalize_key_name(input);
80        if let Ok(key) = parse_key_ignore_case(normalized_key) {
81            return Ok(KeyboardInput::Key(key));
82        }
83
84        Err(KeyParseError::UnknownKey(input.to_string()))
85    }
86}
87
88impl std::fmt::Display for KeyboardInput {
89    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
90        write!(f, "{}", self.as_str())
91    }
92}
93
94impl std::str::FromStr for KeyboardInput {
95    type Err = KeyParseError;
96
97    fn from_str(s: &str) -> Result<Self, Self::Err> {
98        // First try case-sensitive parsing for exact matches
99        if let Ok(key) = s.parse::<Key>() {
100            return Ok(KeyboardInput::Key(key));
101        }
102
103        if let Ok(modifier) = s.parse::<Modifier>() {
104            return Ok(KeyboardInput::Modifier(modifier));
105        }
106
107        // Fall back to alias-aware parsing
108        Self::parse_with_aliases(s)
109    }
110}
111
112// Implement KeyCodeMapper for KeyboardInput
113impl KeyCodeMapper for KeyboardInput {
114    fn to_code(&self, platform: Platform) -> usize {
115        self.to_code(platform)
116    }
117
118    fn from_code(code: usize, platform: Platform) -> Option<Self> {
119        Self::from_code(code, platform)
120    }
121}
122
123#[cfg(test)]
124mod tests {
125    use super::*;
126
127    #[test]
128    fn test_keyboard_input_creation() {
129        let key_input = KeyboardInput::Key(Key::A);
130        let modifier_input = KeyboardInput::Modifier(Modifier::Control);
131
132        assert!(key_input.is_key());
133        assert!(modifier_input.is_modifier());
134        assert_eq!(key_input.as_key(), Some(Key::A));
135        assert_eq!(modifier_input.as_modifier(), Some(Modifier::Control));
136    }
137
138    #[test]
139    fn test_keyboard_input_from_str() {
140        assert_eq!(
141            "A".parse::<KeyboardInput>().unwrap(),
142            KeyboardInput::Key(Key::A)
143        );
144        assert_eq!(
145            "Control".parse::<KeyboardInput>().unwrap(),
146            KeyboardInput::Modifier(Modifier::Control)
147        );
148        assert_eq!(
149            "ctrl".parse::<KeyboardInput>().unwrap(),
150            KeyboardInput::Modifier(Modifier::Control)
151        );
152        assert_eq!(
153            "esc".parse::<KeyboardInput>().unwrap(),
154            KeyboardInput::Key(Key::Escape)
155        );
156    }
157
158    #[test]
159    fn test_keyboard_input_to_code() {
160        let key_input = KeyboardInput::Key(Key::Enter);
161        let modifier_input = KeyboardInput::Modifier(Modifier::Shift);
162
163        assert_eq!(key_input.to_code(Platform::Windows), 0x0D);
164        assert_eq!(modifier_input.to_code(Platform::Windows), 0x10);
165    }
166
167    #[test]
168    fn test_keyboard_input_from_code() {
169        assert_eq!(
170            KeyboardInput::from_code(0x41, Platform::Windows),
171            Some(KeyboardInput::Key(Key::A))
172        );
173        assert_eq!(
174            KeyboardInput::from_code(0x10, Platform::Windows),
175            Some(KeyboardInput::Modifier(Modifier::Shift))
176        );
177    }
178
179    #[test]
180    fn test_keyboard_input_display() {
181        assert_eq!(KeyboardInput::Key(Key::Enter).to_string(), "Enter");
182        assert_eq!(
183            KeyboardInput::Modifier(Modifier::Control).to_string(),
184            "Control"
185        );
186    }
187}