1use alloc::string::{String, ToString};
2use core::fmt;
3use core::str::FromStr;
4
5use crate::{first_char, NamedKey};
6
7#[cfg(feature = "serde")]
8use serde::{Deserialize, Serialize};
9
10#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
12#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
13pub enum Key {
14 Character(String),
18 Named(NamedKey),
19}
20
21impl fmt::Display for Key {
22 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
23 match self {
24 Self::Character(s) => f.write_str(s),
25 Self::Named(k) => k.fmt(f),
26 }
27 }
28}
29
30#[derive(Clone, Debug)]
32pub struct UnrecognizedKeyError;
33
34impl FromStr for Key {
35 type Err = UnrecognizedKeyError;
36
37 fn from_str(s: &str) -> Result<Self, Self::Err> {
38 if is_key_string(s) {
39 Ok(Self::Character(s.to_string()))
40 } else {
41 Ok(Self::Named(
42 NamedKey::from_str(s).map_err(|_| UnrecognizedKeyError)?,
43 ))
44 }
45 }
46}
47
48impl From<NamedKey> for Key {
49 fn from(value: NamedKey) -> Self {
50 Self::Named(value)
51 }
52}
53
54impl Key {
55 pub fn legacy_charcode(&self) -> u32 {
62 match self {
65 Key::Character(ref c) => c.chars().next().unwrap_or('\0') as u32,
66 Key::Named(_) => 0,
67 }
68 }
69
70 pub fn legacy_keycode(&self) -> u32 {
76 match self {
77 Key::Named(NamedKey::Backspace) => 8,
79 Key::Named(NamedKey::Tab) => 9,
80 Key::Named(NamedKey::Enter) => 13,
81 Key::Named(NamedKey::Shift) => 16,
82 Key::Named(NamedKey::Control) => 17,
83 Key::Named(NamedKey::Alt) => 18,
84 Key::Named(NamedKey::CapsLock) => 20,
85 Key::Named(NamedKey::Escape) => 27,
86 Key::Named(NamedKey::PageUp) => 33,
87 Key::Named(NamedKey::PageDown) => 34,
88 Key::Named(NamedKey::End) => 35,
89 Key::Named(NamedKey::Home) => 36,
90 Key::Named(NamedKey::ArrowLeft) => 37,
91 Key::Named(NamedKey::ArrowUp) => 38,
92 Key::Named(NamedKey::ArrowRight) => 39,
93 Key::Named(NamedKey::ArrowDown) => 40,
94 Key::Named(NamedKey::Delete) => 46,
95 Key::Character(ref c) if c.len() == 1 => match first_char(c) {
96 ' ' => 32,
97 x @ '0'..='9' => x as u32,
98 x @ 'a'..='z' => x.to_ascii_uppercase() as u32,
99 x @ 'A'..='Z' => x as u32,
100 ';' | ':' => 186,
102 '=' | '+' => 187,
103 ',' | '<' => 188,
104 '-' | '_' => 189,
105 '.' | '>' => 190,
106 '/' | '?' => 191,
107 '`' | '~' => 192,
108 '[' | '{' => 219,
109 '\\' | '|' => 220,
110 ']' | '}' => 221,
111 '\'' | '\"' => 222,
112 _ => 0,
113 },
114 _ => 0,
115 }
116 }
117}
118
119impl Default for Key {
120 fn default() -> Self {
121 Self::Named(NamedKey::default())
122 }
123}
124
125fn is_key_string(s: &str) -> bool {
130 s.chars().all(|c| !c.is_control()) && s.chars().skip(1).all(|c| !c.is_ascii())
131}
132
133#[cfg(test)]
134mod test {
135 use super::*;
136
137 #[test]
138 fn test_is_key_string() {
139 assert!(is_key_string("A"));
140 assert!(!is_key_string("AA"));
141 assert!(!is_key_string(" "));
142 }
143
144 #[test]
145 fn into() {
146 assert_eq!(Key::Named(NamedKey::Enter), NamedKey::Enter.into());
147 }
148}