rust_kanban/inputs/
key.rs

1use crossterm::event::{self, KeyModifiers};
2use serde::{Deserialize, Serialize};
3use serde_json::{Map, Value};
4use std::fmt::{self, Display, Formatter};
5
6#[derive(PartialEq, Eq, Clone, Copy, Hash, Debug, Serialize, Deserialize)]
7pub enum Key {
8    Alt(char),
9    AltShift(char),
10    AltBackspace,
11    AltDelete,
12    BackTab,
13    Backspace,
14    Char(char),
15    Ctrl(char),
16    CtrlAlt(char),
17    CtrlShift(char),
18    CtrlShiftRight,
19    CtrlShiftLeft,
20    CtrlShiftUp,
21    CtrlShiftDown,
22    CtrlAltShift(char),
23    CtrlAltDown,
24    CtrlAltShiftDown,
25    CtrlAltLeft,
26    CtrlAltShiftLeft,
27    CtrlAltRight,
28    CtrlAltShiftRight,
29    CtrlAltUp,
30    CtrlAltShiftUp,
31    CtrlDown,
32    CtrlLeft,
33    CtrlRight,
34    CtrlUp,
35    Delete,
36    Down,
37    End,
38    Enter,
39    Esc,
40    F0,
41    F1,
42    F10,
43    F11,
44    F12,
45    F2,
46    F3,
47    F4,
48    F5,
49    F6,
50    F7,
51    F8,
52    F9,
53    Home,
54    Ins,
55    Left,
56    PageDown,
57    ShiftPageDown,
58    PageUp,
59    ShiftPageUp,
60    Right,
61    ShiftHome,
62    ShiftEnd,
63    ShiftDown,
64    ShiftLeft,
65    ShiftRight,
66    ShiftUp,
67    Space,
68    Tab,
69    Unknown,
70    Up,
71}
72
73impl Key {
74    pub fn from_f(n: u8) -> Key {
75        match n {
76            0 => Key::F0,
77            1 => Key::F1,
78            2 => Key::F2,
79            3 => Key::F3,
80            4 => Key::F4,
81            5 => Key::F5,
82            6 => Key::F6,
83            7 => Key::F7,
84            8 => Key::F8,
85            9 => Key::F9,
86            10 => Key::F10,
87            11 => Key::F11,
88            12 => Key::F12,
89            _ => {
90                log::warn!("Unknown Function key: F{}", n);
91                Key::Unknown
92            }
93        }
94    }
95}
96
97impl Display for Key {
98    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
99        match *self {
100            Key::Alt(c) => write!(f, "<Alt+{}>", c),
101            Key::AltBackspace => write!(f, "<Alt+Backspace>"),
102            Key::AltDelete => write!(f, "<Alt+Delete>"),
103            Key::BackTab => write!(f, "<Shift+Tab>"),
104            Key::Backspace => write!(f, "<Backspace>"),
105            Key::Char(c) => write!(f, "<{}>", c),
106            Key::Ctrl(c) => write!(f, "<Ctrl+{}>", c),
107            Key::CtrlAlt(c) => write!(f, "<Ctrl+Alt+{}>", c),
108            Key::CtrlAltDown => write!(f, "<Ctrl+Alt+Down>"),
109            Key::CtrlAltLeft => write!(f, "<Ctrl+Alt+Left>"),
110            Key::CtrlAltRight => write!(f, "<Ctrl+Alt+Right>"),
111            Key::CtrlAltUp => write!(f, "<Ctrl+Alt+Up>"),
112            Key::CtrlDown => write!(f, "<Ctrl+Down>"),
113            Key::CtrlLeft => write!(f, "<Ctrl+Left>"),
114            Key::CtrlRight => write!(f, "<Ctrl+Right>"),
115            Key::CtrlUp => write!(f, "<Ctrl+Up>"),
116            Key::Delete => write!(f, "<Delete>"),
117            Key::Down => write!(f, "<Down>"),
118            Key::End => write!(f, "<End>"),
119            Key::Enter => write!(f, "<Enter>"),
120            Key::Esc => write!(f, "<Esc>"),
121            Key::Home => write!(f, "<Home>"),
122            Key::Ins => write!(f, "<Ins>"),
123            Key::Left => write!(f, "<Left>"),
124            Key::PageDown => write!(f, "<PageDown>"),
125            Key::PageUp => write!(f, "<PageUp>"),
126            Key::Right => write!(f, "<Right>"),
127            Key::ShiftDown => write!(f, "<Shift+Down>"),
128            Key::ShiftLeft => write!(f, "<Shift+Left>"),
129            Key::ShiftRight => write!(f, "<Shift+Right>"),
130            Key::ShiftUp => write!(f, "<Shift+Up>"),
131            Key::Space => write!(f, "<Space>"),
132            Key::Tab => write!(f, "<Tab>"),
133            Key::Unknown => write!(f, "<Unknown>"),
134            Key::Up => write!(f, "<Up>"),
135            _ => write!(f, "<{:?}>", self),
136        }
137    }
138}
139
140impl From<event::KeyEvent> for Key {
141    fn from(key_event: event::KeyEvent) -> Self {
142        let ctrl = key_event.modifiers.contains(KeyModifiers::CONTROL);
143        let alt = key_event.modifiers.contains(KeyModifiers::ALT);
144        let shift = key_event.modifiers.contains(KeyModifiers::SHIFT);
145        match key_event {
146            event::KeyEvent {
147                code: event::KeyCode::Esc,
148                kind: event::KeyEventKind::Press,
149                ..
150            } => Key::Esc,
151            event::KeyEvent {
152                code: event::KeyCode::Backspace,
153                kind: event::KeyEventKind::Press,
154                ..
155            } => {
156                if alt {
157                    Key::AltBackspace
158                } else {
159                    Key::Backspace
160                }
161            }
162            event::KeyEvent {
163                code: event::KeyCode::Up,
164                kind: event::KeyEventKind::Press,
165                ..
166            } => {
167                if ctrl && alt {
168                    Key::CtrlAltUp
169                } else if ctrl {
170                    Key::CtrlUp
171                } else if shift {
172                    Key::ShiftUp
173                } else {
174                    Key::Up
175                }
176            }
177            event::KeyEvent {
178                code: event::KeyCode::Down,
179                kind: event::KeyEventKind::Press,
180                ..
181            } => {
182                if ctrl && alt {
183                    Key::CtrlAltDown
184                } else if ctrl {
185                    Key::CtrlDown
186                } else if shift {
187                    Key::ShiftDown
188                } else {
189                    Key::Down
190                }
191            }
192            event::KeyEvent {
193                code: event::KeyCode::Left,
194                kind: event::KeyEventKind::Press,
195                ..
196            } => {
197                if ctrl && alt {
198                    Key::CtrlAltLeft
199                } else if ctrl {
200                    Key::CtrlLeft
201                } else if shift {
202                    Key::ShiftLeft
203                } else {
204                    Key::Left
205                }
206            }
207            event::KeyEvent {
208                code: event::KeyCode::Right,
209                kind: event::KeyEventKind::Press,
210                ..
211            } => {
212                if ctrl && alt {
213                    Key::CtrlAltRight
214                } else if ctrl {
215                    Key::CtrlRight
216                } else if shift {
217                    Key::ShiftRight
218                } else {
219                    Key::Right
220                }
221            }
222            event::KeyEvent {
223                code: event::KeyCode::Delete,
224                kind: event::KeyEventKind::Press,
225                ..
226            } => {
227                if alt {
228                    Key::AltDelete
229                } else {
230                    Key::Delete
231                }
232            }
233            event::KeyEvent {
234                code: event::KeyCode::Home,
235                kind: event::KeyEventKind::Press,
236                ..
237            } => Key::Home,
238            event::KeyEvent {
239                code: event::KeyCode::End,
240                kind: event::KeyEventKind::Press,
241                ..
242            } => Key::End,
243            event::KeyEvent {
244                code: event::KeyCode::PageUp,
245                kind: event::KeyEventKind::Press,
246                ..
247            } => Key::PageUp,
248            event::KeyEvent {
249                code: event::KeyCode::PageDown,
250                kind: event::KeyEventKind::Press,
251                ..
252            } => Key::PageDown,
253            event::KeyEvent {
254                code: event::KeyCode::Insert,
255                kind: event::KeyEventKind::Press,
256                ..
257            } => Key::Ins,
258            event::KeyEvent {
259                code: event::KeyCode::F(n),
260                kind: event::KeyEventKind::Press,
261                ..
262            } => Key::from_f(n),
263            event::KeyEvent {
264                code: event::KeyCode::Enter,
265                kind: event::KeyEventKind::Press,
266                ..
267            } => Key::Enter,
268            event::KeyEvent {
269                code: event::KeyCode::BackTab,
270                kind: event::KeyEventKind::Press,
271                ..
272            } => Key::BackTab,
273            event::KeyEvent {
274                code: event::KeyCode::Tab,
275                kind: event::KeyEventKind::Press,
276                ..
277            } => Key::Tab,
278            event::KeyEvent {
279                code: event::KeyCode::Char(c),
280                kind: event::KeyEventKind::Press,
281                ..
282            } => {
283                if ctrl && alt {
284                    Key::CtrlAlt(c)
285                } else if ctrl {
286                    Key::Ctrl(c)
287                } else if alt {
288                    Key::Alt(c)
289                } else if shift {
290                    Key::Char(c.to_ascii_uppercase())
291                } else {
292                    Key::Char(c)
293                }
294            }
295            _ => Key::Unknown,
296        }
297    }
298}
299
300impl From<&str> for Key {
301    fn from(s: &str) -> Self {
302        // handle char
303        if s.len() == 1 {
304            return Key::Char(s.chars().next().unwrap());
305        }
306        // handle alt+char
307        if s.len() == 6 && s.starts_with("<Alt+") && s.ends_with('>') {
308            return Key::Alt(s.chars().nth(5).unwrap());
309        }
310        // handle ctrl+char
311        if s.len() == 7 && s.starts_with("<Ctrl+") && s.ends_with('>') {
312            return Key::Ctrl(s.chars().nth(6).unwrap());
313        }
314        // handle ctrl+alt+char
315        if s.len() == 10 && s.starts_with("<Ctrl+Alt+") && s.ends_with('>') {
316            return Key::CtrlAlt(s.chars().nth(9).unwrap());
317        }
318        match s {
319            "<Alt+Backspace>" => Key::AltBackspace,
320            "<Alt+Delete>" => Key::AltDelete,
321            "<Backspace>" => Key::Backspace,
322            "<Ctrl+Alt+Down>" => Key::CtrlAltDown,
323            "<Ctrl+Alt+Left>" => Key::CtrlAltLeft,
324            "<Ctrl+Alt+Right>" => Key::CtrlAltRight,
325            "<Ctrl+Alt+Up>" => Key::CtrlAltUp,
326            "<Ctrl+Down>" => Key::CtrlDown,
327            "<Ctrl+Left>" => Key::CtrlLeft,
328            "<Ctrl+Right>" => Key::CtrlRight,
329            "<Ctrl+Up>" => Key::CtrlUp,
330            "<Delete>" => Key::Delete,
331            "<Down>" => Key::Down,
332            "<End>" => Key::End,
333            "<Enter>" => Key::Enter,
334            "<Esc>" => Key::Esc,
335            "<Home>" => Key::Home,
336            "<Ins>" => Key::Ins,
337            "<Left>" => Key::Left,
338            "<PageDown>" => Key::PageDown,
339            "<PageUp>" => Key::PageUp,
340            "<Right>" => Key::Right,
341            "<Shift+Down>" => Key::ShiftDown,
342            "<Shift+Left>" => Key::ShiftLeft,
343            "<Shift+Right>" => Key::ShiftRight,
344            "<Shift+Up>" => Key::ShiftUp,
345            "<Space>" => Key::Space,
346            "<Tab>" => Key::Tab,
347            "<Unknown>" => Key::Unknown,
348            "<Up>" => Key::Up,
349            _ => Key::Unknown,
350        }
351    }
352}
353
354impl From<&Map<String, Value>> for Key {
355    // TODO: handle more key types
356    fn from(value: &Map<String, Value>) -> Self {
357        fn char_from_value(val: &Value) -> char {
358            val.as_str().and_then(|s| s.chars().next()).unwrap()
359        }
360        if let Some(char_value) = value.get("Char") {
361            Key::Char(char_from_value(char_value))
362        } else if let Some(alt_value) = value.get("Alt") {
363            Key::Alt(char_from_value(alt_value))
364        } else if let Some(ctrl_value) = value.get("Ctrl") {
365            Key::Ctrl(char_from_value(ctrl_value))
366        } else {
367            Key::Unknown
368        }
369    }
370}