matchmaker/
binds.rs

1use crokey::KeyCombination;
2use crossterm::event::{KeyModifiers, MouseEventKind};
3use serde::Deserializer;
4use std::{collections::HashMap, fmt, str::FromStr};
5
6use crossterm::event::MouseButton;
7use serde::de::{self, Visitor};
8
9use crate::{action::Actions, message::Event};
10
11pub type BindMap = HashMap<Trigger, Actions>;
12
13#[derive(Debug, Hash, PartialEq, Eq, Clone)]
14pub enum Trigger {
15    Key(KeyCombination),
16    Mouse(MouseEvent),
17    Event(Event),
18}
19
20#[derive(Debug, Hash, PartialEq, Eq, Clone)]
21pub struct MouseEvent {
22    pub kind: MouseEventKind,
23    pub modifiers: KeyModifiers,
24}
25
26// ---------- BOILERPLATE
27impl From<crossterm::event::MouseEvent> for Trigger {
28    fn from(e: crossterm::event::MouseEvent) -> Self {
29        Trigger::Mouse(MouseEvent {
30            kind: e.kind,
31            modifiers: e.modifiers,
32        })
33    }
34}
35
36impl From<KeyCombination> for Trigger {
37    fn from(key: KeyCombination) -> Self {
38        Trigger::Key(key)
39    }
40}
41
42impl From<Event> for Trigger {
43    fn from(event: Event) -> Self {
44        Trigger::Event(event)
45    }
46}
47// ------------ SERDE
48
49impl<'de> serde::Deserialize<'de> for Trigger {
50    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
51    where
52        D: Deserializer<'de>,
53    {
54        struct TriggerVisitor;
55
56        impl<'de> Visitor<'de> for TriggerVisitor {
57            type Value = Trigger;
58
59            fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
60                write!(f, "a string representing a Trigger")
61            }
62
63            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
64            where
65                E: de::Error,
66            {
67                // 1. Try KeyCombination
68                if let Ok(key) = KeyCombination::from_str(value) {
69                    return Ok(Trigger::Key(key));
70                }
71
72                // 2. Try MouseEvent: modifiers split by '+', last = mouse button
73                let parts: Vec<&str> = value.split('+').collect();
74                if let Some(last) = parts.last() {
75                    if let Some(kind) = match last.to_lowercase().as_str() {
76                        "left" => Some(MouseEventKind::Down(MouseButton::Left)),
77                        "middle" => Some(MouseEventKind::Down(MouseButton::Middle)),
78                        "right" => Some(MouseEventKind::Down(MouseButton::Right)),
79                        "scrolldown" => Some(MouseEventKind::ScrollDown),
80                        "scrollup" => Some(MouseEventKind::ScrollUp),
81                        "scrollleft" => Some(MouseEventKind::ScrollLeft),
82                        "scrollright" => Some(MouseEventKind::ScrollRight),
83                        _ => None,
84                    } {
85                        let mut modifiers = KeyModifiers::empty();
86                        for m in &parts[..parts.len() - 1] {
87                            match m.to_lowercase().as_str() {
88                                "shift" => modifiers |= KeyModifiers::SHIFT,
89                                "ctrl" => modifiers |= KeyModifiers::CONTROL,
90                                "alt" => modifiers |= KeyModifiers::ALT,
91                                "super" => modifiers |= KeyModifiers::SUPER,
92                                "hyper" => modifiers |= KeyModifiers::HYPER,
93                                "meta" => modifiers |= KeyModifiers::META,
94                                "none" => {}
95                                unknown => {
96                                    return Err(E::custom(format!(
97                                        "Unknown modifier: {}",
98                                        unknown
99                                    )));
100                                }
101                            }
102                        }
103                        return Ok(Trigger::Mouse(MouseEvent { kind, modifiers }));
104                    }
105                }
106
107                // 3. Try PickerEvent
108                if let Ok(evt) = value.parse::<Event>() {
109                    return Ok(Trigger::Event(evt));
110                }
111
112                Err(E::custom(format!(
113                    "failed to parse trigger from '{}'",
114                    value
115                )))
116            }
117        }
118
119        deserializer.deserialize_str(TriggerVisitor)
120    }
121}