1#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
12pub enum FunctionKey {
13 Esc,
15 F1,
17 F2,
19 F3,
21 F4,
23 F5,
25 F6,
27 F7,
29 F8,
31 F9,
33 F10,
35 F11,
37 F12,
39 F13,
41 F14,
43 F15,
45 F16,
47 F17,
49 F18,
51 F19,
53}
54
55impl FunctionKey {
56 pub const ALL: [Self; 20] = [
58 Self::Esc,
59 Self::F1,
60 Self::F2,
61 Self::F3,
62 Self::F4,
63 Self::F5,
64 Self::F6,
65 Self::F7,
66 Self::F8,
67 Self::F9,
68 Self::F10,
69 Self::F11,
70 Self::F12,
71 Self::F13,
72 Self::F14,
73 Self::F15,
74 Self::F16,
75 Self::F17,
76 Self::F18,
77 Self::F19,
78 ];
79
80 #[must_use]
84 pub const fn keycode(self) -> u16 {
85 match self {
86 Self::Esc => 0x35,
87 Self::F1 => 0x7A,
88 Self::F2 => 0x78,
89 Self::F3 => 0x63,
90 Self::F4 => 0x76,
91 Self::F5 => 0x60,
92 Self::F6 => 0x61,
93 Self::F7 => 0x62,
94 Self::F8 => 0x64,
95 Self::F9 => 0x65,
96 Self::F10 => 0x6D,
97 Self::F11 => 0x67,
98 Self::F12 => 0x6F,
99 Self::F13 => 0x69,
100 Self::F14 => 0x6B,
101 Self::F15 => 0x71,
102 Self::F16 => 0x6A,
103 Self::F17 => 0x40,
104 Self::F18 => 0x4F,
105 Self::F19 => 0x50,
106 }
107 }
108
109 #[must_use]
111 pub fn from_keycode(keycode: u16) -> Option<Self> {
112 Self::ALL.into_iter().find(|key| key.keycode() == keycode)
113 }
114
115 #[must_use]
117 pub const fn name(self) -> &'static str {
118 match self {
119 Self::Esc => "esc",
120 Self::F1 => "f1",
121 Self::F2 => "f2",
122 Self::F3 => "f3",
123 Self::F4 => "f4",
124 Self::F5 => "f5",
125 Self::F6 => "f6",
126 Self::F7 => "f7",
127 Self::F8 => "f8",
128 Self::F9 => "f9",
129 Self::F10 => "f10",
130 Self::F11 => "f11",
131 Self::F12 => "f12",
132 Self::F13 => "f13",
133 Self::F14 => "f14",
134 Self::F15 => "f15",
135 Self::F16 => "f16",
136 Self::F17 => "f17",
137 Self::F18 => "f18",
138 Self::F19 => "f19",
139 }
140 }
141
142 #[must_use]
144 pub fn from_name(name: &str) -> Option<Self> {
145 Self::ALL
146 .into_iter()
147 .find(|key| key.name().eq_ignore_ascii_case(name))
148 }
149
150 #[must_use]
152 pub const fn label(self) -> &'static str {
153 match self {
154 Self::Esc => "Esc",
155 Self::F1 => "F1",
156 Self::F2 => "F2",
157 Self::F3 => "F3",
158 Self::F4 => "F4",
159 Self::F5 => "F5",
160 Self::F6 => "F6",
161 Self::F7 => "F7",
162 Self::F8 => "F8",
163 Self::F9 => "F9",
164 Self::F10 => "F10",
165 Self::F11 => "F11",
166 Self::F12 => "F12",
167 Self::F13 => "F13",
168 Self::F14 => "F14",
169 Self::F15 => "F15",
170 Self::F16 => "F16",
171 Self::F17 => "F17",
172 Self::F18 => "F18",
173 Self::F19 => "F19",
174 }
175 }
176
177 #[must_use]
180 pub fn nth_f(n: u16) -> Option<Self> {
181 match n {
182 0 => None,
183 n => Self::ALL.get(usize::from(n)).copied(),
184 }
185 }
186}
187
188#[cfg(test)]
189mod tests {
190 use std::collections::BTreeSet;
191
192 use super::FunctionKey;
193
194 #[test]
195 fn every_key_round_trips_through_each_vocabulary() {
196 for key in FunctionKey::ALL {
197 assert_eq!(FunctionKey::from_keycode(key.keycode()), Some(key));
198 assert_eq!(FunctionKey::from_name(key.name()), Some(key));
199 }
200 let keycodes: BTreeSet<u16> = FunctionKey::ALL.iter().map(|key| key.keycode()).collect();
202 let names: BTreeSet<&str> = FunctionKey::ALL.iter().map(|key| key.name()).collect();
203 assert_eq!(keycodes.len(), FunctionKey::ALL.len());
204 assert_eq!(names.len(), FunctionKey::ALL.len());
205 }
206
207 #[test]
208 fn the_nth_f_key_counts_from_one_and_stops_at_f19() {
209 assert_eq!(FunctionKey::nth_f(0), None, "Esc is not an F key");
210 assert_eq!(FunctionKey::nth_f(1), Some(FunctionKey::F1));
211 assert_eq!(FunctionKey::nth_f(19), Some(FunctionKey::F19));
212 assert_eq!(FunctionKey::nth_f(20), None);
213 }
214
215 #[test]
216 fn names_parse_case_insensitively() {
217 assert_eq!(FunctionKey::from_name("F12"), Some(FunctionKey::F12));
218 assert_eq!(FunctionKey::from_name("Esc"), Some(FunctionKey::Esc));
219 assert_eq!(FunctionKey::from_name("f20"), None);
220 }
221}