concinnity_core/components/
input_key.rs1macro_rules! key_label {
11 ($variant:ident) => {
12 stringify!($variant)
13 };
14 ($variant:ident => $label:literal) => {
15 $label
16 };
17}
18
19macro_rules! define_keys {
20 ($($variant:ident $(=> $label:literal)?),* $(,)?) => {
21 #[derive(
29 Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize,
30 )]
31 #[expect(missing_docs, reason = "each variant is one key name; the vocabulary is documented on the enum")]
34 pub enum InputKey {
35 $($variant),*
36 }
37
38 impl InputKey {
39 pub const ALL: &'static [InputKey] = &[$(InputKey::$variant),*];
41
42 pub fn name(self) -> &'static str {
48 match self {
49 $(InputKey::$variant => stringify!($variant)),*
50 }
51 }
52
53 pub fn display_name(self) -> &'static str {
57 match self {
58 $(InputKey::$variant => key_label!($variant $(=> $label)?)),*
59 }
60 }
61 }
62 };
63}
64
65define_keys! {
66 A, B, C, D, E, F, G, H, I, J, K, L, M,
67 N, O, P, Q, R, S, T, U, V, W, X, Y, Z,
68 Num0 => "0",
69 Num1 => "1",
70 Num2 => "2",
71 Num3 => "3",
72 Num4 => "4",
73 Num5 => "5",
74 Num6 => "6",
75 Num7 => "7",
76 Num8 => "8",
77 Num9 => "9",
78 Space,
79 Tab,
80 Enter,
81 Backspace => "Bksp",
82 Delete => "Del",
83 Shift,
84 Control => "Ctrl",
85 Alt,
86 Up,
87 Down,
88 Left,
89 Right,
90 Minus => "-",
91 Equals => "=",
92 LeftBracket => "[",
93 RightBracket => "]",
94 Backslash => "\\",
95 Semicolon => ";",
96 Quote => "'",
97 Comma => ",",
98 Period => ".",
99 Slash => "/",
100 Backtick => "`",
101}
102
103#[cfg(test)]
104mod tests {
105 use super::*;
106 use alloc::format;
107
108 #[test]
109 fn serializes_to_variant_name() {
110 let json = serde_json::to_string(&InputKey::W).unwrap();
113 assert_eq!(json, "\"W\"");
114 let back: InputKey = serde_json::from_str(&json).unwrap();
115 assert_eq!(back, InputKey::W);
116 }
117
118 #[test]
119 fn display_names_are_short() {
120 assert_eq!(InputKey::W.display_name(), "W");
121 assert_eq!(InputKey::Space.display_name(), "Space");
122 assert_eq!(InputKey::Shift.display_name(), "Shift");
123 assert_eq!(InputKey::Num1.display_name(), "1");
124 assert_eq!(InputKey::Backspace.display_name(), "Bksp");
125 assert_eq!(InputKey::Control.display_name(), "Ctrl");
126 assert_eq!(InputKey::Minus.display_name(), "-");
127 }
128
129 #[test]
130 fn all_variants_cover_name_and_display() {
131 for &key in InputKey::ALL {
135 assert!(!key.name().is_empty(), "name empty for {key:?}");
136 assert!(!key.display_name().is_empty(), "display empty for {key:?}");
137 let json = serde_json::to_string(&key).unwrap();
138 assert_eq!(
139 json,
140 format!("\"{}\"", key.name()),
141 "serde vs name for {key:?}"
142 );
143 let back: InputKey = serde_json::from_str(&json).unwrap();
144 assert_eq!(back, key, "round trip for {key:?}");
145 }
146 }
147
148 #[test]
149 fn variant_names_are_unique() {
150 let mut seen = alloc::collections::BTreeSet::new();
153 for &key in InputKey::ALL {
154 assert!(seen.insert(key.name()), "duplicate name {}", key.name());
155 }
156 }
157}