Skip to main content

crossterm_keybind_core/
lib.rs

1#[cfg(feature = "crossterm_0_29_0")]
2use crossterm_0_29_0::event::{KeyCode, KeyEvent, KeyModifiers, MediaKeyCode};
3#[cfg(feature = "crossterm_0_28_1")]
4use crossterm_0_28_1::event::{KeyCode, KeyEvent, KeyModifiers, MediaKeyCode};
5
6use serde::{de, ser, Deserialize, Deserializer, Serialize, Serializer};
7use std::fmt;
8#[cfg(feature = "case_ignore")]
9use str_utils::StartsWithIgnoreAsciiCase;
10
11#[derive(Default, PartialEq)]
12pub enum DisplayFormat {
13    /// use symbol for each key stroke
14    #[default]
15    Symbols,
16
17    /// Debug print same as config format
18    Debug,
19
20    /// Display with full name of key
21    Full,
22
23    /// Display with abbreviated key names (e.g., 'Ctrl' instead of 'Control')
24    Abbreviation
25}
26
27#[derive(PartialEq)]
28pub struct KeyBinding {
29    pub code: KeyCode,
30    pub modifiers: KeyModifiers,
31}
32
33impl Serialize for KeyBinding {
34    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
35    where
36        S: Serializer,
37    {
38        let mut s = match self.modifiers {
39            KeyModifiers::SHIFT => "Shift+".to_string(),
40            KeyModifiers::CONTROL => "Control+".to_string(),
41            KeyModifiers::ALT => "Alternate+".to_string(),
42            KeyModifiers::SUPER => "Super+".to_string(),
43            KeyModifiers::HYPER => "Hyper+".to_string(),
44            KeyModifiers::META => "Meta+".to_string(),
45            // TODO support more KeyModifiers
46            _ => String::new(),
47        };
48        match self.code {
49            KeyCode::Char(c) => s.push(c),
50            KeyCode::Backspace => s.push_str("Backspace"),
51            KeyCode::Enter => s.push_str("Enter"),
52            KeyCode::Left => s.push_str("Left"),
53            KeyCode::Right => s.push_str("Right"),
54            KeyCode::Up => s.push_str("Up"),
55            KeyCode::Down => s.push_str("Down"),
56            KeyCode::Home => s.push_str("Home"),
57            KeyCode::End => s.push_str("End"),
58            KeyCode::PageUp => s.push_str("PageUp"),
59            KeyCode::PageDown => s.push_str("PageDown"),
60            KeyCode::Tab => s.push_str("Tab"),
61            KeyCode::BackTab => s.push_str("BackTab"),
62            KeyCode::Delete => s.push_str("Delete"),
63            KeyCode::Insert => s.push_str("Insert"),
64            KeyCode::F(n) => s.push_str(&format!("F{n}")),
65            KeyCode::Esc => s.push_str("Esc"),
66            KeyCode::CapsLock => s.push_str("CapsLock"),
67            KeyCode::ScrollLock => s.push_str("ScrollLock"),
68            KeyCode::NumLock => s.push_str("NumLock"),
69            KeyCode::PrintScreen => s.push_str("PrintScreen"),
70            KeyCode::Pause => s.push_str("Pause"),
71            KeyCode::Menu => s.push_str("Menu"),
72            KeyCode::KeypadBegin => s.push_str("KeypadBegin"),
73            KeyCode::Media(media_keycode) => s.push_str(&media_keycode.to_string()),
74            _ => {
75                return Err(ser::Error::custom(format!(
76                    "Unsupported KeyCode: {:}",
77                    self.code
78                )))
79            }
80        }
81        serializer.serialize_str(&s)
82    }
83}
84
85fn str_to_keycode(s: &str) -> KeyCode {
86    // Interpreting a single whitespace as Space
87    if s == " " {
88        return KeyCode::Char(' ');
89    }
90    let s = s.trim();
91    if s.len() == 1 {
92        KeyCode::Char(s.chars().next().unwrap())
93    } else if s == "Backspace" {
94        KeyCode::Backspace
95    } else if s == "Enter" {
96        KeyCode::Enter
97    } else if s == "Left" {
98        KeyCode::Left
99    } else if s == "Right" {
100        KeyCode::Right
101    } else if s == "Up" {
102        KeyCode::Up
103    } else if s == "Down" {
104        KeyCode::Down
105    } else if s == "Home" {
106        KeyCode::Home
107    } else if s == "End" {
108        KeyCode::End
109    } else if s == "PageUp" {
110        KeyCode::PageUp
111    } else if s == "PageDown" {
112        KeyCode::PageDown
113    } else if s == "Tab" {
114        KeyCode::Tab
115    } else if s == "BackTab" {
116        KeyCode::BackTab
117    } else if s == "Delete" {
118        KeyCode::Delete
119    } else if s == "Insert" {
120        KeyCode::Insert
121    } else if s == "F1" {
122        KeyCode::F(1)
123    } else if s == "F2" {
124        KeyCode::F(2)
125    } else if s == "F3" {
126        KeyCode::F(3)
127    } else if s == "F4" {
128        KeyCode::F(4)
129    } else if s == "F5" {
130        KeyCode::F(5)
131    } else if s == "F6" {
132        KeyCode::F(6)
133    } else if s == "F7" {
134        KeyCode::F(7)
135    } else if s == "F8" {
136        KeyCode::F(8)
137    } else if s == "F9" {
138        KeyCode::F(9)
139    } else if s == "F10" {
140        KeyCode::F(10)
141    } else if s == "F11" {
142        KeyCode::F(11)
143    } else if s == "F12" {
144        KeyCode::F(12)
145    } else if s == "Esc" {
146        KeyCode::Esc
147    } else if s == "CapsLock" {
148        KeyCode::CapsLock
149    } else if s == "ScrollLock" {
150        KeyCode::ScrollLock
151    } else if s == "NumLock" {
152        KeyCode::NumLock
153    } else if s == "PrintScreen" {
154        KeyCode::PrintScreen
155    } else if s == "Pause" {
156        KeyCode::Pause
157    } else if s == "Menu" {
158        KeyCode::Menu
159    } else if s == "KeypadBegin" {
160        KeyCode::KeypadBegin
161    } else if s == "Play" {
162        KeyCode::Media(MediaKeyCode::Play)
163    } else if s == "PlayPause" {
164        KeyCode::Media(MediaKeyCode::PlayPause)
165    } else if s == "Reverse" {
166        KeyCode::Media(MediaKeyCode::Reverse)
167    } else if s == "Stop" {
168        KeyCode::Media(MediaKeyCode::Stop)
169    } else if s == "FastForward" {
170        KeyCode::Media(MediaKeyCode::FastForward)
171    } else if s == "Rewind" {
172        KeyCode::Media(MediaKeyCode::Rewind)
173    } else if s == "TrackNext" {
174        KeyCode::Media(MediaKeyCode::TrackNext)
175    } else if s == "TrackPrevious" {
176        KeyCode::Media(MediaKeyCode::TrackPrevious)
177    } else if s == "Record" {
178        KeyCode::Media(MediaKeyCode::Record)
179    } else if s == "LowerVolume" {
180        KeyCode::Media(MediaKeyCode::LowerVolume)
181    } else if s == "RaiseVolume" {
182        KeyCode::Media(MediaKeyCode::RaiseVolume)
183    } else if s == "MuteVolume" {
184        KeyCode::Media(MediaKeyCode::MuteVolume)
185    } else {
186        KeyCode::Null
187    }
188}
189
190impl<'de> Deserialize<'de> for KeyBinding {
191    fn deserialize<D>(deserializer: D) -> Result<KeyBinding, D::Error>
192    where
193        D: Deserializer<'de>,
194    {
195        let mut key_bindings = KeyBinding {
196            code: KeyCode::Null,
197            modifiers: KeyModifiers::NONE,
198        };
199        let mut error = None;
200
201        <String as Deserialize>::deserialize(deserializer).map(|s| {
202            if s.contains('+') {
203                #[cfg(feature = "case_ignore")]
204                if s.starts_with_ignore_ascii_case("Shift") {
205                    key_bindings.modifiers = KeyModifiers::SHIFT;
206                } else if s.starts_with_ignore_ascii_case("Control") || s.starts_with_ignore_ascii_case("Ctrl") {
207                    key_bindings.modifiers = KeyModifiers::CONTROL;
208                } else if s.starts_with_ignore_ascii_case("Alternate") || s.starts_with_ignore_ascii_case("Alt") {
209                    key_bindings.modifiers = KeyModifiers::ALT;
210                } else if s.starts_with_ignore_ascii_case("Super") {
211                    key_bindings.modifiers = KeyModifiers::SUPER;
212                } else if s.starts_with_ignore_ascii_case("Hyper") {
213                    key_bindings.modifiers = KeyModifiers::HYPER;
214                } else if s.starts_with_ignore_ascii_case("Meta") {
215                    key_bindings.modifiers = KeyModifiers::META;
216                } else {
217                    error = Some(de::Error::custom(
218                        "Currently only support following KeyModifiers: Shift, Control, Alternate, Super, Hyper, Meta"
219                    ));
220                }
221                #[cfg(not(feature = "case_ignore"))]
222                if s.starts_with("Shift") {
223                    key_bindings.modifiers = KeyModifiers::SHIFT;
224                } else if s.starts_with("Control") || s.starts_with("Ctrl") {
225                    key_bindings.modifiers = KeyModifiers::CONTROL;
226                } else if s.starts_with("Alternate") || s.starts_with("Alt") {
227                    key_bindings.modifiers = KeyModifiers::ALT;
228                } else if s.starts_with("Super") {
229                    key_bindings.modifiers = KeyModifiers::SUPER;
230                } else if s.starts_with("Hyper") {
231                    key_bindings.modifiers = KeyModifiers::HYPER;
232                } else if s.starts_with("Meta") {
233                    key_bindings.modifiers = KeyModifiers::META;
234                } else {
235                    error = Some(de::Error::custom(
236                        "Currently only support following KeyModifiers: Shift, Control, Alternate, Super, Hyper, Meta"
237                    ));
238                }
239                let mut splitter = s.splitn(2, '+');
240                key_bindings.code = str_to_keycode(splitter.nth(1).unwrap());
241            } else {
242                key_bindings.code = str_to_keycode(&s);
243            }
244        })?;
245        if let Some(e) = error {
246            Err(e)
247        } else if key_bindings.code == KeyCode::Null {
248            Err(de::Error::custom(
249                r#"Can not load a KeyCode, please use a char or one of following KeyCodes:
250"Backspace", "Enter", "Left", "Right", "Up", "Down", "Home", "End", "PageUp", "PageDown", "Tab", "BackTab", "Delete",
251"Insert", "F1" ~ "F12", "Esc", "CapsLock", "ScrollLock", "NumLock", "PrintScreen", "Pause", "Menu", "KeypadBegin",
252"Play", "PlayPause", "Reverse", "Stop", "FastForward", "Rewind", "TrackNext", "TrackPrevious", "Record", "LowerVolume", 
253"RaiseVolume", "MuteVolume""#,
254            ))
255        } else {
256            Ok(key_bindings)
257        }
258    }
259}
260
261// ref: http://xahlee.info/comp/unicode_computing_symbols.html
262// TODO add FormattingOptions for different layout,
263// ex: Canadian Multilingual Layout, Truly Ergonomic Keyboard
264impl fmt::Display for KeyBinding {
265    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
266        match self.modifiers {
267            KeyModifiers::SHIFT => write!(f, "\u{21e7}")?, //⇧
268            KeyModifiers::CONTROL => write!(f, "^")?,
269            KeyModifiers::ALT => write!(f, "\u{2387}")?, //⎇
270            KeyModifiers::SUPER => write!(f, "\u{2756}")?, //❖
271            KeyModifiers::HYPER => write!(f, "\u{2388}")?, //⎈
272            KeyModifiers::META => write!(f, "\u{2318}")?, //⌘
273            KeyModifiers::NONE => write!(f, "")?,
274            _ => write!(f, "?")?,
275        };
276        match self.code {
277            KeyCode::Char(c) => write!(f, "{}", c),
278            KeyCode::Backspace => write!(f, "\u{232b}"), //⌫
279            KeyCode::Enter => write!(f, "\u{23ce}"),     //⏎
280            KeyCode::Left => write!(f, "\u{2190}"),      //←
281            KeyCode::Right => write!(f, "\u{2192}"),     //→
282            KeyCode::Up => write!(f, "\u{2191}"),        //↑
283            KeyCode::Down => write!(f, "\u{2193}"),      //↓
284            KeyCode::Home => write!(f, "\u{2912}"),      //⤒
285            KeyCode::End => write!(f, "\u{2913}"),       //⤓
286            KeyCode::PageUp => write!(f, "\u{21de}"),    //⇞
287            KeyCode::PageDown => write!(f, "\u{21df}"),  //⇟
288            KeyCode::Tab => write!(f, "\u{21e5}"),       //⇥
289            KeyCode::BackTab => write!(f, "\u{21e4}"),   //⇤
290            KeyCode::Delete => write!(f, "\u{2326}"),    //⌦
291            KeyCode::Insert => write!(f, "\u{2380}"),    //⎀
292            KeyCode::F(n) => write!(f, "F{}", n),
293            KeyCode::Esc => write!(f, "\u{238b}"),          //⎋
294            KeyCode::CapsLock => write!(f, "\u{1F130}"),    //🄰
295            KeyCode::ScrollLock => write!(f, "\u{1F4DC}"),  //📜
296            KeyCode::NumLock => write!(f, "\u{2460}"),      //①
297            KeyCode::PrintScreen => write!(f, "\u{2399}"),  //⎙
298            KeyCode::Pause => write!(f, "\u{2389}"),        //⎉
299            KeyCode::Menu => write!(f, "\u{1F5C7}"),        //🗇
300            KeyCode::KeypadBegin => write!(f, "\u{1F5CA}"), //🗊
301            KeyCode::Media(MediaKeyCode::Play) => write!(f, "\u{23F5}"), //⏵
302            KeyCode::Media(MediaKeyCode::PlayPause) => write!(f, "\u{23EF}"), //⏯
303            KeyCode::Media(MediaKeyCode::Reverse) => write!(f, "\u{2B6F}"), //⭯
304            KeyCode::Media(MediaKeyCode::Stop) => write!(f, "\u{23F9}"), //⏹
305            KeyCode::Media(MediaKeyCode::FastForward) => write!(f, "\u{23ED}"), //⏭
306            KeyCode::Media(MediaKeyCode::Rewind) => write!(f, "\u{2B6E}"), //⭮
307            KeyCode::Media(MediaKeyCode::TrackNext) => write!(f, "\u{29D0}"), //⧐
308            KeyCode::Media(MediaKeyCode::TrackPrevious) => write!(f, "\u{29CF}"), //⧏
309            KeyCode::Media(MediaKeyCode::Record) => write!(f, "\u{241E}"), //␞
310            KeyCode::Media(MediaKeyCode::LowerVolume) => write!(f, "\u{1F508}"), //🔈
311            KeyCode::Media(MediaKeyCode::RaiseVolume) => write!(f, "\u{1F50A}"), //🔊
312            KeyCode::Media(MediaKeyCode::MuteVolume) => write!(f, "\u{1F507}"), //🔇
313            _ => write!(f, "?"),
314        }
315    }
316}
317
318impl fmt::Debug for KeyBinding {
319    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
320        write!(f,"\"")?;
321        match self.modifiers {
322            KeyModifiers::SHIFT => write!(f, "Shift+")?,
323            KeyModifiers::CONTROL => write!(f, "Control+")?,
324            KeyModifiers::ALT => write!(f, "Alternate+")?,
325            KeyModifiers::SUPER => write!(f, "Super+")?,
326            KeyModifiers::HYPER => write!(f, "Hyper+")?,
327            KeyModifiers::META => write!(f, "Meta+")?,
328            KeyModifiers::NONE => write!(f, "")?,
329            _ => write!(f, "UNKNOWN+")?,
330        };
331        match self.code {
332            KeyCode::Char(c) => write!(f, "{}", c)?,
333            KeyCode::Backspace => write!(f, "Backspace")?,
334            KeyCode::Enter => write!(f, "Enter")?,
335            KeyCode::Left => write!(f, "Left")?,
336            KeyCode::Right => write!(f, "Right")?,
337            KeyCode::Up => write!(f, "Up")?,
338            KeyCode::Down => write!(f, "Down")?,
339            KeyCode::Home => write!(f, "Home")?,
340            KeyCode::End => write!(f, "End")?,
341            KeyCode::PageUp => write!(f, "PageUp")?,
342            KeyCode::PageDown => write!(f, "PageDown")?,
343            KeyCode::Tab => write!(f, "Tab")?,
344            KeyCode::BackTab => write!(f, "BackTab")?,
345            KeyCode::Delete => write!(f, "Delete")?,
346            KeyCode::Insert => write!(f, "Insert")?,
347            KeyCode::F(n) => write!(f, "F{}", n)?,
348            KeyCode::Esc => write!(f, "Esc")?,
349            KeyCode::CapsLock => write!(f, "CapsLock")?,
350            KeyCode::ScrollLock => write!(f, "ScrollLock")?,
351            KeyCode::NumLock => write!(f, "NumLock")?,
352            KeyCode::PrintScreen => write!(f, "PrintScreen")?,
353            KeyCode::Pause => write!(f, "Pause")?,
354            KeyCode::Menu => write!(f, "Menu")?,
355            KeyCode::KeypadBegin => write!(f, "KeypadBegin")?,
356            KeyCode::Media(MediaKeyCode::Play) => write!(f, "Play")?,
357            KeyCode::Media(MediaKeyCode::PlayPause) => write!(f, "PlayPause")?,
358            KeyCode::Media(MediaKeyCode::Reverse) => write!(f, "Reverse")?,
359            KeyCode::Media(MediaKeyCode::Stop) => write!(f, "Stop")?,
360            KeyCode::Media(MediaKeyCode::FastForward) => write!(f, "FastForward")?,
361            KeyCode::Media(MediaKeyCode::Rewind) => write!(f, "Rewind")?,
362            KeyCode::Media(MediaKeyCode::TrackNext) => write!(f, "TrackNext")?,
363            KeyCode::Media(MediaKeyCode::TrackPrevious) => write!(f, "TrackPrevious")?,
364            KeyCode::Media(MediaKeyCode::Record) => write!(f, "Record")?,
365            KeyCode::Media(MediaKeyCode::LowerVolume) => write!(f, "LowerVolume")?,
366            KeyCode::Media(MediaKeyCode::RaiseVolume) => write!(f, "RaiseVolume")?,
367            KeyCode::Media(MediaKeyCode::MuteVolume) => write!(f, "MuteVolume")?,
368            _ => write!(f, "?")?,
369        }
370        write!(f,"\"")
371    }
372}
373
374impl KeyBinding {
375    pub fn display(&self, f: &DisplayFormat) -> String {
376        match f {
377            DisplayFormat::Symbols => format!("{}", self),
378            DisplayFormat::Debug => format!("{:?}", self),
379            DisplayFormat::Full | DisplayFormat::Abbreviation =>  {
380                let mut display = match (f, self.modifiers) {
381                    (_, KeyModifiers::SHIFT) => "Shift+".to_string(),
382                    (DisplayFormat::Full, KeyModifiers::CONTROL) => "Control+".to_string(),
383                    (DisplayFormat::Abbreviation, KeyModifiers::CONTROL) => "Ctrl+".to_string(),
384                    (DisplayFormat::Full, KeyModifiers::ALT) => "Alternate+".to_string(),
385                    (DisplayFormat::Abbreviation, KeyModifiers::ALT) => "Alt+".to_string(),
386                    (_, KeyModifiers::SUPER) => "Super+".to_string(),
387                    (_, KeyModifiers::HYPER) => "Hyper+".to_string(),
388                    (_, KeyModifiers::META) =>  "Meta+".to_string(),
389                    (_, KeyModifiers::NONE) => String::new(),
390                    (_, _) => "UNKNOWN+".to_string(),
391                };
392                match self.code {
393                    KeyCode::Char(' ') => display.push_str("Space"),
394                    KeyCode::Char(c) => display.push(c),
395                    KeyCode::Backspace => display.push_str("Backspace"),
396                    KeyCode::Enter => display.push_str("Enter"),
397                    KeyCode::Left => display.push_str("Left"),
398                    KeyCode::Right => display.push_str("Right"),
399                    KeyCode::Up => display.push_str("Up"),
400                    KeyCode::Down => display.push_str("Down"),
401                    KeyCode::Home => display.push_str("Home"),
402                    KeyCode::End => display.push_str("End"),
403                    KeyCode::PageUp => display.push_str("PageUp"),
404                    KeyCode::PageDown => display.push_str("PageDown"),
405                    KeyCode::Tab => display.push_str("Tab"),
406                    KeyCode::BackTab => display.push_str("BackTab"),
407                    KeyCode::Delete => display.push_str("Delete"),
408                    KeyCode::Insert => display.push_str("Insert"),
409                    KeyCode::F(n) => display.push_str(&format!("F{}", n)),
410                    KeyCode::Esc => display.push_str("Esc"),
411                    KeyCode::CapsLock => display.push_str("CapsLock"),
412                    KeyCode::ScrollLock => display.push_str("ScrollLock"),
413                    KeyCode::NumLock => display.push_str("NumLock"),
414                    KeyCode::PrintScreen => display.push_str("PrintScreen"),
415                    KeyCode::Pause => display.push_str("Pause"),
416                    KeyCode::Menu => display.push_str("Menu"),
417                    KeyCode::KeypadBegin => display.push_str("KeypadBegin"),
418                    KeyCode::Media(MediaKeyCode::Play) => display.push_str("Play"),
419                    KeyCode::Media(MediaKeyCode::PlayPause) => display.push_str("PlayPause"),
420                    KeyCode::Media(MediaKeyCode::Reverse) => display.push_str("Reverse"),
421                    KeyCode::Media(MediaKeyCode::Stop) => display.push_str("Stop"),
422                    KeyCode::Media(MediaKeyCode::FastForward) => display.push_str("FastForward"),
423                    KeyCode::Media(MediaKeyCode::Rewind) => display.push_str("Rewind"),
424                    KeyCode::Media(MediaKeyCode::TrackNext) => display.push_str("TrackNext"),
425                    KeyCode::Media(MediaKeyCode::TrackPrevious) => display.push_str("TrackPrevious"),
426                    KeyCode::Media(MediaKeyCode::Record) => display.push_str("Record"),
427                    KeyCode::Media(MediaKeyCode::LowerVolume) => display.push_str("LowerVolume"),
428                    KeyCode::Media(MediaKeyCode::RaiseVolume) => display.push_str("RaiseVolume"),
429                    KeyCode::Media(MediaKeyCode::MuteVolume) => display.push_str("MuteVolume"),
430                    _ => display.push('?'),
431                }
432                display
433            }
434        }
435    }
436}
437
438/// KeyBindings struct for key bind configure
439#[derive(Serialize, Deserialize, PartialEq)]
440pub struct KeyBindings(Vec<KeyBinding>);
441
442impl KeyBindings {
443    /// Match one of key bindings
444    pub fn match_any(&self, key_event: &KeyEvent) -> bool {
445        for key_bind in self.0.iter() {
446            if key_bind.code == key_event.code && key_bind.modifiers == key_event.modifiers {
447                return true;
448            }
449        }
450        false
451    }
452}
453
454impl fmt::Display for KeyBindings {
455    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
456        for (i, kb) in self.0.iter().enumerate() {
457            if i > 0 {
458                write!(f, "|{}", kb)?; // Add delimiter
459            } else {
460                write!(f, "{}", kb)?;
461            }
462        }
463        Ok(())
464    }
465}
466
467impl fmt::Debug for KeyBindings {
468    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
469        write!(f, "[")?;
470        for (i, kb) in self.0.iter().enumerate() {
471            if i > 0 {
472                write!(f, ", {:?}", kb)?;
473            } else {
474                write!(f, "{:?}", kb)?;
475            }
476        }
477        write!(f, "]")?;
478        Ok(())
479    }
480}
481impl KeyBindings {
482    pub fn display(&self, f: &DisplayFormat) -> String {
483        match f {
484            DisplayFormat::Symbols => format!("{}", self),
485            DisplayFormat::Debug => format!("{:?}", self),
486            _ => {
487                let mut display = String::new();
488                for (i, kb) in self.0.iter().enumerate() {
489                    if i > 0 {
490                        display.push_str(" | ");
491                        display.push_str(&kb.display(f));
492                    } else {
493                        display.push_str(&kb.display(f));
494                    }
495                }
496                display
497            }
498        }
499    }
500}
501
502#[cfg(test)]
503mod tests {
504    use super::*;
505
506    #[derive(Serialize, Deserialize)]
507    struct T {
508        kb: KeyBinding,
509    }
510    #[derive(Serialize, Deserialize)]
511    struct U {
512        kbs: KeyBindings,
513    }
514
515    #[test]
516    fn ser_keybinding_config() {
517        let (t_with_ctrl_modifier, t_with_alt_modifier,t, only_modifiers, t_with_esc) = keybinding_configs();
518
519        let serialized = toml::to_string(&t_with_ctrl_modifier).unwrap();
520        assert_eq!(serialized, "kb = \"Control+c\"\n");
521
522        let serialized = toml::to_string(&t_with_alt_modifier).unwrap();
523        assert_eq!(serialized, "kb = \"Alternate+c\"\n");
524
525        let serialized = toml::to_string(&t).unwrap();
526        assert_eq!(serialized, "kb = \"Q\"\n");
527
528        let serialized = toml::to_string(&only_modifiers);
529        assert_eq!(
530            serialized,
531            Err(ser::Error::custom("Unsupported KeyCode: Null"))
532        );
533
534        let serialized = toml::to_string(&t_with_esc).unwrap();
535        assert_eq!(serialized, "kb = \"Esc\"\n");
536    }
537
538    #[test]
539    #[cfg(feature = "case_ignore")]
540    fn deserialize_with_wrong_config() {
541        let desered_t: T = toml::from_str("kb = \"control+c\"\n").unwrap();
542        assert_eq!(desered_t.kb.modifiers, KeyModifiers::CONTROL);
543    }
544
545    #[test]
546    fn de_keybinding_config() {
547        let (t_with_ctrl_modifier, t_with_alt_modifier, t, _only_modifiers, t_with_esc) = keybinding_configs();
548
549        let serialized = toml::to_string(&t_with_ctrl_modifier).unwrap();
550        let desered_t: T = toml::from_str(serialized.as_str()).unwrap();
551        assert_eq!(desered_t.kb.code, t_with_ctrl_modifier.kb.code);
552        assert_eq!(desered_t.kb.modifiers, t_with_ctrl_modifier.kb.modifiers);
553
554        let serialized = toml::to_string(&t_with_alt_modifier).unwrap();
555        let desered_t: T = toml::from_str(serialized.as_str()).unwrap();
556        assert_eq!(desered_t.kb.code, t_with_alt_modifier.kb.code);
557        assert_eq!(desered_t.kb.modifiers, t_with_alt_modifier.kb.modifiers);
558
559        let serialized = toml::to_string(&t).unwrap();
560        let desered_t: T = toml::from_str(serialized.as_str()).unwrap();
561        assert_eq!(desered_t.kb.code, t.kb.code);
562        assert_eq!(desered_t.kb.modifiers, t.kb.modifiers);
563
564        let serialized = toml::to_string(&t_with_esc).unwrap();
565        let desered_t: T = toml::from_str(serialized.as_str()).unwrap();
566        assert_eq!(desered_t.kb.code, t_with_esc.kb.code);
567        assert_eq!(desered_t.kb.modifiers, t_with_esc.kb.modifiers);
568    }
569
570    #[test]
571    fn fmt_keybinding_config() {
572        let (t_with_modifiers, _t_with_alt, _t, _only_modifiers, t_with_esc) = keybinding_configs();
573
574        assert_eq!(format!("{}", t_with_modifiers.kb), "^c");
575        assert_eq!(format!("{}", t_with_esc.kb), "⎋");
576
577        assert_eq!(t_with_modifiers.kb.display(&DisplayFormat::Full), "Control+c");
578        assert_eq!(t_with_modifiers.kb.display(&DisplayFormat::Abbreviation), "Ctrl+c");
579        assert_eq!(t_with_esc.kb.display(&DisplayFormat::Full), "Esc");
580    }
581
582    #[test]
583    fn ser_keybindings_config() {
584        let config = keybindings_config();
585
586        let serialized = toml::to_string(&config).unwrap();
587        assert_eq!(serialized, "kbs = [\"Control+c\", \"Q\"]\n");
588    }
589
590    #[test]
591    fn fmt_keybindings_config() {
592        let config = keybindings_config();
593        assert_eq!(format!("{}", config.kbs), "^c|Q");
594        assert_eq!(config.kbs.display(&DisplayFormat::Full), "Control+c | Q");
595        assert_eq!(config.kbs.display(&DisplayFormat::Abbreviation), "Ctrl+c | Q");
596    }
597
598    /// Return keybind config with modifiers, keybind without modifiers, only modifiers
599    fn keybinding_configs() -> (T, T, T, T, T) {
600        (
601            T {
602                kb: KeyBinding {
603                    code: KeyCode::Char('c'),
604                    modifiers: KeyModifiers::CONTROL,
605                },
606            },
607            T {
608                kb: KeyBinding {
609                    code: KeyCode::Char('c'),
610                    modifiers: KeyModifiers::ALT,
611                },
612            },
613            T {
614                kb: KeyBinding {
615                    code: KeyCode::Char('Q'),
616                    modifiers: KeyModifiers::NONE,
617                },
618            },
619            T {
620                kb: KeyBinding {
621                    code: KeyCode::Null,
622                    modifiers: KeyModifiers::ALT,
623                },
624            },
625            T {
626                kb: KeyBinding {
627                    code: KeyCode::Esc,
628                    modifiers: KeyModifiers::NONE,
629                },
630            },
631        )
632    }
633
634    /// Return keybind config with multiple keybindings
635    fn keybindings_config() -> U {
636        U {
637            kbs: KeyBindings(vec![
638                KeyBinding {
639                    code: KeyCode::Char('c'),
640                    modifiers: KeyModifiers::CONTROL,
641                },
642                KeyBinding {
643                    code: KeyCode::Char('Q'),
644                    modifiers: KeyModifiers::NONE,
645                },
646            ]),
647        }
648    }
649}