keyboard_codes/mapping/
custom.rs1use crate::error::KeyParseError;
2use crate::types::Platform;
3use std::collections::HashMap;
4use std::hash::{Hash, Hasher};
5
6#[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
14impl Hash for CustomKey {
16 fn hash<H: Hasher>(&self, state: &mut H) {
17 self.name.hash(state);
18 }
19}
20
21impl CustomKey {
22 pub fn new(name: &str) -> Self {
24 Self {
25 name: name.to_string(),
26 codes: HashMap::new(),
27 }
28 }
29
30 pub fn add_platform_code(&mut self, platform: Platform, code: usize) -> &mut Self {
32 self.codes.insert(platform, code);
33 self
34 }
35
36 pub fn name(&self) -> &str {
38 &self.name
39 }
40
41 pub fn code(&self, platform: Platform) -> Option<usize> {
43 self.codes.get(&platform).copied()
44 }
45
46 pub fn codes(&self) -> &HashMap<Platform, usize> {
48 &self.codes
49 }
50}
51
52#[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 pub fn new() -> Self {
62 Self::default()
63 }
64
65 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 self.name_to_key.insert(key.name().to_string(), key.clone());
77
78 for (platform, code) in key.codes() {
80 self.code_to_key.insert((*code, *platform), key.clone());
81 }
82
83 Ok(())
84 }
85
86 pub fn remove_key(&mut self, name: &str) -> Option<CustomKey> {
88 if let Some(key) = self.name_to_key.remove(name) {
89 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 pub fn parse_by_name(&self, name: &str) -> Option<&CustomKey> {
101 self.name_to_key.get(name)
102 }
103
104 pub fn parse_by_code(&self, code: usize, platform: Platform) -> Option<&CustomKey> {
106 self.code_to_key.get(&(code, platform))
107 }
108
109 pub fn to_code(&self, key: &CustomKey, platform: Platform) -> Option<usize> {
111 key.code(platform)
112 }
113
114 pub fn keys(&self) -> impl Iterator<Item = &CustomKey> {
116 self.name_to_key.values()
117 }
118
119 pub fn contains_key(&self, name: &str) -> bool {
121 self.name_to_key.contains_key(name)
122 }
123
124 pub fn len(&self) -> usize {
126 self.name_to_key.len()
127 }
128
129 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); 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()); }
170
171 #[test]
172 fn test_custom_key_map() {
173 let mut custom_map = CustomKeyMap::new();
174
175 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 assert_eq!(custom_map.parse_by_name("Macro1").unwrap().name(), "Macro1");
185
186 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 let key = custom_map.parse_by_name("Macro1").unwrap();
204 assert_eq!(custom_map.to_code(key, Platform::Linux), Some(200));
205
206 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}