keyboard_codes/mapping/
custom.rs

1use crate::error::KeyParseError;
2use crate::types::Platform;
3use std::collections::HashMap;
4use std::hash::{Hash, Hasher};
5
6/// Custom key definition
7#[derive(Debug, Clone, PartialEq, Eq)]
8#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
9pub struct CustomKey {
10    name: String,
11    codes: HashMap<Platform, usize>,
12}
13
14// 手动实现 Hash,只基于 name 字段
15impl Hash for CustomKey {
16    fn hash<H: Hasher>(&self, state: &mut H) {
17        self.name.hash(state);
18    }
19}
20
21impl CustomKey {
22    /// Create a new custom key with the given name
23    pub fn new(name: &str) -> Self {
24        Self {
25            name: name.to_string(),
26            codes: HashMap::new(),
27        }
28    }
29
30    /// Add a platform-specific code mapping for this key
31    pub fn add_platform_code(&mut self, platform: Platform, code: usize) -> &mut Self {
32        self.codes.insert(platform, code);
33        self
34    }
35
36    /// Get the key name
37    pub fn name(&self) -> &str {
38        &self.name
39    }
40
41    /// Get the platform-specific code for this key
42    pub fn code(&self, platform: Platform) -> Option<usize> {
43        self.codes.get(&platform).copied()
44    }
45
46    /// Get all platform codes for this key
47    pub fn codes(&self) -> &HashMap<Platform, usize> {
48        &self.codes
49    }
50}
51
52/// Custom key mapping manager
53#[derive(Debug, Default)]
54pub struct CustomKeyMap {
55    name_to_key: HashMap<String, CustomKey>,
56    code_to_key: HashMap<(usize, Platform), CustomKey>,
57}
58
59impl CustomKeyMap {
60    /// Create a new empty custom key mapping manager
61    pub fn new() -> Self {
62        Self::default()
63    }
64
65    /// Add a custom key to the mapping
66    ///
67    /// # Errors
68    ///
69    /// Returns `KeyParseError::DuplicateCustomKey` if a key with the same name already exists
70    pub fn add_key(&mut self, key: CustomKey) -> Result<(), KeyParseError> {
71        if self.name_to_key.contains_key(key.name()) {
72            return Err(KeyParseError::DuplicateCustomKey(key.name().to_string()));
73        }
74
75        // Record name to key mapping
76        self.name_to_key.insert(key.name().to_string(), key.clone());
77
78        // Record (code, platform) to key mapping
79        for (platform, code) in key.codes() {
80            self.code_to_key.insert((*code, *platform), key.clone());
81        }
82
83        Ok(())
84    }
85
86    /// Remove a custom key by name
87    pub fn remove_key(&mut self, name: &str) -> Option<CustomKey> {
88        if let Some(key) = self.name_to_key.remove(name) {
89            // Remove all code mappings for this key
90            for (platform, code) in key.codes() {
91                self.code_to_key.remove(&(*code, *platform));
92            }
93            Some(key)
94        } else {
95            None
96        }
97    }
98
99    /// Parse a custom key by name
100    pub fn parse_by_name(&self, name: &str) -> Option<&CustomKey> {
101        self.name_to_key.get(name)
102    }
103
104    /// Parse a custom key by code and platform
105    pub fn parse_by_code(&self, code: usize, platform: Platform) -> Option<&CustomKey> {
106        self.code_to_key.get(&(code, platform))
107    }
108
109    /// Convert a custom key to a platform-specific code
110    pub fn to_code(&self, key: &CustomKey, platform: Platform) -> Option<usize> {
111        key.code(platform)
112    }
113
114    /// Get all custom keys
115    pub fn keys(&self) -> impl Iterator<Item = &CustomKey> {
116        self.name_to_key.values()
117    }
118
119    /// Check if a custom key with the given name exists
120    pub fn contains_key(&self, name: &str) -> bool {
121        self.name_to_key.contains_key(name)
122    }
123
124    /// Get the number of custom keys
125    pub fn len(&self) -> usize {
126        self.name_to_key.len()
127    }
128
129    /// Check if there are no custom keys
130    pub fn is_empty(&self) -> bool {
131        self.name_to_key.is_empty()
132    }
133}
134
135#[cfg(test)]
136mod tests {
137    use super::*;
138
139    #[test]
140    fn test_custom_key_creation() {
141        let mut key = CustomKey::new("Macro1");
142        key.add_platform_code(Platform::Windows, 0x100)
143            .add_platform_code(Platform::Linux, 200);
144
145        assert_eq!(key.name(), "Macro1");
146        assert_eq!(key.code(Platform::Windows), Some(0x100));
147        assert_eq!(key.code(Platform::Linux), Some(200));
148        assert_eq!(key.code(Platform::MacOS), None);
149    }
150
151    #[test]
152    fn test_custom_key_hash() {
153        use std::collections::hash_map::DefaultHasher;
154        use std::hash::{Hash, Hasher};
155
156        let mut key1 = CustomKey::new("Test");
157        key1.add_platform_code(Platform::Windows, 100);
158
159        let mut key2 = CustomKey::new("Test");
160        key2.add_platform_code(Platform::Linux, 200); // 不同的 codes,但相同的 name
161
162        let mut hasher1 = DefaultHasher::new();
163        let mut hasher2 = DefaultHasher::new();
164
165        key1.hash(&mut hasher1);
166        key2.hash(&mut hasher2);
167
168        assert_eq!(hasher1.finish(), hasher2.finish()); // 应该相等,因为只基于 name
169    }
170
171    #[test]
172    fn test_custom_key_map() {
173        let mut custom_map = CustomKeyMap::new();
174
175        // Create and add a custom key
176        let mut custom_key = CustomKey::new("Macro1");
177        custom_key
178            .add_platform_code(Platform::Windows, 0x100)
179            .add_platform_code(Platform::Linux, 200);
180
181        assert!(custom_map.add_key(custom_key).is_ok());
182
183        // Test parsing by name
184        assert_eq!(custom_map.parse_by_name("Macro1").unwrap().name(), "Macro1");
185
186        // Test parsing by code
187        assert_eq!(
188            custom_map
189                .parse_by_code(0x100, Platform::Windows)
190                .unwrap()
191                .name(),
192            "Macro1"
193        );
194        assert_eq!(
195            custom_map
196                .parse_by_code(200, Platform::Linux)
197                .unwrap()
198                .name(),
199            "Macro1"
200        );
201
202        // Test code conversion
203        let key = custom_map.parse_by_name("Macro1").unwrap();
204        assert_eq!(custom_map.to_code(key, Platform::Linux), Some(200));
205
206        // Test duplicate key error
207        let duplicate_key = CustomKey::new("Macro1");
208        assert!(custom_map.add_key(duplicate_key).is_err());
209    }
210
211    #[test]
212    fn test_custom_key_map_removal() {
213        let mut custom_map = CustomKeyMap::new();
214
215        let mut key = CustomKey::new("Macro1");
216        key.add_platform_code(Platform::Windows, 0x100);
217        custom_map.add_key(key).unwrap();
218
219        assert!(custom_map.contains_key("Macro1"));
220        assert_eq!(custom_map.len(), 1);
221
222        let removed = custom_map.remove_key("Macro1");
223        assert!(removed.is_some());
224        assert!(!custom_map.contains_key("Macro1"));
225        assert!(custom_map.is_empty());
226    }
227}