use std::{
fmt::{Display, Formatter},
hash::Hash,
ops::BitOr,
};
use serde::{de, Deserialize, Deserializer};
use strum_macros::{AsRefStr, Display, EnumString};
use crate::parse;
pub(crate) const KEY_SEP: char = '-';
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Node {
pub modifiers: Modifiers,
pub key: Key,
pub state: Option<State>,
}
impl Node {
pub fn new(modifiers: Modifiers, key: Key) -> Self {
Self {
modifiers,
key,
state: None,
}
}
}
impl From<Key> for Node {
fn from(key: Key) -> Self {
Self {
modifiers: Modifier::None as u8,
state: None,
key,
}
}
}
#[derive(Copy, Clone, Debug, Default, Display, Hash, PartialEq, Eq)]
pub enum State {
#[default]
Pressed,
Released,
Held,
Repeated,
}
#[derive(Copy, Clone, Debug, Display, Hash, PartialEq, EnumString, AsRefStr)]
#[strum(serialize_all = "lowercase")]
pub enum Modifier {
None = 0b0000,
Alt = 0b0001,
Cmd = 0b0010,
Ctrl = 0b0100,
Shift = 0b1000,
}
impl BitOr for Modifier {
type Output = Modifiers;
fn bitor(self, rhs: Self) -> Self::Output {
self as u8 | rhs as u8
}
}
pub type Modifiers = u8;
pub(crate) const MODIFIERS: [Modifier; 4] = [
Modifier::Alt,
Modifier::Cmd,
Modifier::Ctrl,
Modifier::Shift,
];
#[derive(Clone, Debug, Display, PartialEq, Eq, Hash, EnumString, AsRefStr)]
#[strum(serialize_all = "lowercase")]
pub enum Key {
BackTab,
Backspace,
#[strum(serialize = "del", serialize = "delete")]
Delete,
Down,
End,
Enter,
Esc,
Home,
Insert,
Left,
PageDown,
PageUp,
Right,
Space,
Tab,
Up,
F(u8),
Char(char),
#[strum(disabled)]
Group(CharGroup),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, EnumString)]
#[strum(serialize_all = "lowercase")]
pub enum CharGroup {
Digit,
Lower,
Upper,
Alpha,
Alnum,
Any,
}
impl CharGroup {
#[must_use]
pub fn matches(&self, c: char) -> bool {
match self {
CharGroup::Digit => c.is_ascii_digit(),
CharGroup::Lower => c.is_ascii_lowercase(),
CharGroup::Upper => c.is_ascii_uppercase(),
CharGroup::Alpha => c.is_ascii_alphabetic(),
CharGroup::Alnum => c.is_ascii_alphanumeric(),
CharGroup::Any => true,
}
}
}
impl std::fmt::Display for CharGroup {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let name = match self {
Self::Digit => "digit",
Self::Lower => "lower",
Self::Upper => "upper",
Self::Alpha => "alpha",
Self::Alnum => "alnum",
Self::Any => "any",
};
write!(f, "@{name}")
}
}
impl<'s> Deserialize<'s> for Node {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'s>,
{
let key = String::deserialize(deserializer)?;
parse(&key).map_err(de::Error::custom)
}
}
impl Display for Node {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
for m in &MODIFIERS {
if self.modifiers & *m as u8 != 0 {
write!(f, "{m}{KEY_SEP}").unwrap();
}
}
match self.key {
Key::Char(char) => write!(f, "{char}"),
Key::F(n) => write!(f, "{}{n}", self.key),
Key::Group(n) => write!(f, "{n}"),
_ => write!(f, "{}", self.key),
}
}
}