use crate::error::KeyParseError;
use crate::types::Platform;
use std::collections::HashMap;
use std::hash::{Hash, Hasher};
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct CustomKey {
name: String,
codes: HashMap<Platform, usize>,
}
impl Hash for CustomKey {
fn hash<H: Hasher>(&self, state: &mut H) {
self.name.hash(state);
}
}
impl CustomKey {
pub fn new(name: &str) -> Self {
Self {
name: name.to_string(),
codes: HashMap::new(),
}
}
pub fn add_platform_code(&mut self, platform: Platform, code: usize) -> &mut Self {
self.codes.insert(platform, code);
self
}
pub fn name(&self) -> &str {
&self.name
}
pub fn code(&self, platform: Platform) -> Option<usize> {
self.codes.get(&platform).copied()
}
pub fn codes(&self) -> &HashMap<Platform, usize> {
&self.codes
}
}
#[derive(Debug, Default)]
pub struct CustomKeyMap {
name_to_key: HashMap<String, CustomKey>,
code_to_key: HashMap<(usize, Platform), CustomKey>,
}
impl CustomKeyMap {
pub fn new() -> Self {
Self::default()
}
pub fn add_key(&mut self, key: CustomKey) -> Result<(), KeyParseError> {
if self.name_to_key.contains_key(key.name()) {
return Err(KeyParseError::DuplicateCustomKey(key.name().to_string()));
}
self.name_to_key.insert(key.name().to_string(), key.clone());
for (platform, code) in key.codes() {
self.code_to_key.insert((*code, *platform), key.clone());
}
Ok(())
}
pub fn remove_key(&mut self, name: &str) -> Option<CustomKey> {
if let Some(key) = self.name_to_key.remove(name) {
for (platform, code) in key.codes() {
self.code_to_key.remove(&(*code, *platform));
}
Some(key)
} else {
None
}
}
pub fn parse_by_name(&self, name: &str) -> Option<&CustomKey> {
self.name_to_key.get(name)
}
pub fn parse_by_code(&self, code: usize, platform: Platform) -> Option<&CustomKey> {
self.code_to_key.get(&(code, platform))
}
pub fn to_code(&self, key: &CustomKey, platform: Platform) -> Option<usize> {
key.code(platform)
}
pub fn keys(&self) -> impl Iterator<Item = &CustomKey> {
self.name_to_key.values()
}
pub fn contains_key(&self, name: &str) -> bool {
self.name_to_key.contains_key(name)
}
pub fn len(&self) -> usize {
self.name_to_key.len()
}
pub fn is_empty(&self) -> bool {
self.name_to_key.is_empty()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_custom_key_creation() {
let mut key = CustomKey::new("Macro1");
key.add_platform_code(Platform::Windows, 0x100)
.add_platform_code(Platform::Linux, 200);
assert_eq!(key.name(), "Macro1");
assert_eq!(key.code(Platform::Windows), Some(0x100));
assert_eq!(key.code(Platform::Linux), Some(200));
assert_eq!(key.code(Platform::MacOS), None);
}
#[test]
fn test_custom_key_hash() {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
let mut key1 = CustomKey::new("Test");
key1.add_platform_code(Platform::Windows, 100);
let mut key2 = CustomKey::new("Test");
key2.add_platform_code(Platform::Linux, 200);
let mut hasher1 = DefaultHasher::new();
let mut hasher2 = DefaultHasher::new();
key1.hash(&mut hasher1);
key2.hash(&mut hasher2);
assert_eq!(hasher1.finish(), hasher2.finish()); }
#[test]
fn test_custom_key_map() {
let mut custom_map = CustomKeyMap::new();
let mut custom_key = CustomKey::new("Macro1");
custom_key
.add_platform_code(Platform::Windows, 0x100)
.add_platform_code(Platform::Linux, 200);
assert!(custom_map.add_key(custom_key).is_ok());
assert_eq!(custom_map.parse_by_name("Macro1").unwrap().name(), "Macro1");
assert_eq!(
custom_map
.parse_by_code(0x100, Platform::Windows)
.unwrap()
.name(),
"Macro1"
);
assert_eq!(
custom_map
.parse_by_code(200, Platform::Linux)
.unwrap()
.name(),
"Macro1"
);
let key = custom_map.parse_by_name("Macro1").unwrap();
assert_eq!(custom_map.to_code(key, Platform::Linux), Some(200));
let duplicate_key = CustomKey::new("Macro1");
assert!(custom_map.add_key(duplicate_key).is_err());
}
#[test]
fn test_custom_key_map_removal() {
let mut custom_map = CustomKeyMap::new();
let mut key = CustomKey::new("Macro1");
key.add_platform_code(Platform::Windows, 0x100);
custom_map.add_key(key).unwrap();
assert!(custom_map.contains_key("Macro1"));
assert_eq!(custom_map.len(), 1);
let removed = custom_map.remove_key("Macro1");
assert!(removed.is_some());
assert!(!custom_map.contains_key("Macro1"));
assert!(custom_map.is_empty());
}
}