use crate::error::KeyParseError;
use crate::mapping::standard::parse_key_ignore_case;
use crate::parser::{normalize_key_name, parse_modifier_with_aliases};
use crate::types::{Key, KeyCodeMapper, Modifier, Platform};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum KeyboardInput {
Key(Key),
Modifier(Modifier),
}
impl KeyboardInput {
pub fn as_str(&self) -> &'static str {
match self {
KeyboardInput::Key(key) => key.as_str(),
KeyboardInput::Modifier(modifier) => modifier.as_str(),
}
}
pub fn to_code(&self, platform: Platform) -> usize {
match self {
KeyboardInput::Key(key) => KeyCodeMapper::to_code(key, platform),
KeyboardInput::Modifier(modifier) => KeyCodeMapper::to_code(modifier, platform),
}
}
pub fn from_code(code: usize, platform: Platform) -> Option<Self> {
Key::from_code(code, platform)
.map(KeyboardInput::Key)
.or_else(|| Modifier::from_code(code, platform).map(KeyboardInput::Modifier))
}
pub fn is_key(&self) -> bool {
matches!(self, KeyboardInput::Key(_))
}
pub fn is_modifier(&self) -> bool {
matches!(self, KeyboardInput::Modifier(_))
}
pub fn as_key(&self) -> Option<Key> {
match self {
KeyboardInput::Key(key) => Some(*key),
_ => None,
}
}
pub fn as_modifier(&self) -> Option<Modifier> {
match self {
KeyboardInput::Modifier(modifier) => Some(*modifier),
_ => None,
}
}
pub fn parse_with_aliases(input: &str) -> Result<Self, KeyParseError> {
if input.is_empty() {
return Err(KeyParseError::UnknownKey("empty string".to_string()));
}
if let Ok(modifier) = parse_modifier_with_aliases(input) {
return Ok(KeyboardInput::Modifier(modifier));
}
let normalized_key = normalize_key_name(input);
if let Ok(key) = parse_key_ignore_case(normalized_key) {
return Ok(KeyboardInput::Key(key));
}
Err(KeyParseError::UnknownKey(input.to_string()))
}
}
impl std::fmt::Display for KeyboardInput {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
impl std::str::FromStr for KeyboardInput {
type Err = KeyParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if let Ok(key) = s.parse::<Key>() {
return Ok(KeyboardInput::Key(key));
}
if let Ok(modifier) = s.parse::<Modifier>() {
return Ok(KeyboardInput::Modifier(modifier));
}
Self::parse_with_aliases(s)
}
}
impl KeyCodeMapper for KeyboardInput {
fn to_code(&self, platform: Platform) -> usize {
self.to_code(platform)
}
fn from_code(code: usize, platform: Platform) -> Option<Self> {
Self::from_code(code, platform)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_keyboard_input_creation() {
let key_input = KeyboardInput::Key(Key::A);
let modifier_input = KeyboardInput::Modifier(Modifier::Control);
assert!(key_input.is_key());
assert!(modifier_input.is_modifier());
assert_eq!(key_input.as_key(), Some(Key::A));
assert_eq!(modifier_input.as_modifier(), Some(Modifier::Control));
}
#[test]
fn test_keyboard_input_from_str() {
assert_eq!(
"A".parse::<KeyboardInput>().unwrap(),
KeyboardInput::Key(Key::A)
);
assert_eq!(
"Control".parse::<KeyboardInput>().unwrap(),
KeyboardInput::Modifier(Modifier::Control)
);
assert_eq!(
"ctrl".parse::<KeyboardInput>().unwrap(),
KeyboardInput::Modifier(Modifier::Control)
);
assert_eq!(
"esc".parse::<KeyboardInput>().unwrap(),
KeyboardInput::Key(Key::Escape)
);
}
#[test]
fn test_keyboard_input_to_code() {
let key_input = KeyboardInput::Key(Key::Enter);
let modifier_input = KeyboardInput::Modifier(Modifier::Shift);
assert_eq!(key_input.to_code(Platform::Windows), 0x0D);
assert_eq!(modifier_input.to_code(Platform::Windows), 0x10);
}
#[test]
fn test_keyboard_input_from_code() {
assert_eq!(
KeyboardInput::from_code(0x41, Platform::Windows),
Some(KeyboardInput::Key(Key::A))
);
assert_eq!(
KeyboardInput::from_code(0x10, Platform::Windows),
Some(KeyboardInput::Modifier(Modifier::Shift))
);
}
#[test]
fn test_keyboard_input_display() {
assert_eq!(KeyboardInput::Key(Key::Enter).to_string(), "Enter");
assert_eq!(
KeyboardInput::Modifier(Modifier::Control).to_string(),
"Control"
);
}
}