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