1use std::collections::BTreeMap;
4
5use serde::{Deserialize, Serialize};
6
7use crate::binding::Action;
8
9#[derive(
15 Debug, Clone, Copy, Default, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize,
16)]
17#[expect(
18 clippy::struct_excessive_bools,
19 reason = "four independent modifier flags mirrored from the OS hook"
20)]
21pub struct KeyModifiers {
22 pub shift: bool,
24 pub control: bool,
26 pub option: bool,
28 pub command: bool,
30}
31
32impl KeyModifiers {
33 #[must_use]
35 pub fn is_empty(&self) -> bool {
36 !self.shift && !self.control && !self.option && !self.command
37 }
38}
39
40#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
48pub struct KeyTrigger {
49 pub keycode: u16,
51 pub modifiers: KeyModifiers,
53}
54
55impl std::fmt::Display for KeyTrigger {
56 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57 let mut parts: Vec<&str> = Vec::new();
58 let m = &self.modifiers;
59 if m.shift {
60 parts.push("shift");
61 }
62 if m.control {
63 parts.push("control");
64 }
65 if m.option {
66 parts.push("option");
67 }
68 if m.command {
69 parts.push("command");
70 }
71 parts.push(keycode_to_name(self.keycode).ok_or(std::fmt::Error)?);
72 write!(f, "{}", parts.join("+"))
73 }
74}
75
76fn keycode_to_name(code: u16) -> Option<&'static str> {
79 Some(match code {
80 0x35 => "esc",
81 0x7A => "f1",
82 0x78 => "f2",
83 0x63 => "f3",
84 0x76 => "f4",
85 0x60 => "f5",
86 0x61 => "f6",
87 0x62 => "f7",
88 0x64 => "f8",
89 0x65 => "f9",
90 0x6D => "f10",
91 0x67 => "f11",
92 0x6F => "f12",
93 0x69 => "f13",
94 0x6B => "f14",
95 0x71 => "f15",
96 0x6A => "f16",
97 0x40 => "f17",
98 0x4F => "f18",
99 0x50 => "f19",
100 _ => return None,
101 })
102}
103
104impl Serialize for KeyTrigger {
106 fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
107 s.collect_str(self)
108 }
109}
110impl<'de> Deserialize<'de> for KeyTrigger {
111 fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
112 let s = String::deserialize(d)?;
113 s.parse().map_err(serde::de::Error::custom)
114 }
115}
116
117#[derive(Debug)]
119pub struct ParseTriggerError(pub String);
120impl std::fmt::Display for ParseTriggerError {
121 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
122 write!(f, "invalid key trigger: {}", self.0)
123 }
124}
125impl std::error::Error for ParseTriggerError {}
126
127impl std::str::FromStr for KeyTrigger {
128 type Err = ParseTriggerError;
129 fn from_str(s: &str) -> Result<Self, Self::Err> {
130 let mut mods = KeyModifiers::default();
131 let parts: Vec<&str> = s.split('+').map(str::trim).collect();
132 if parts.is_empty() || parts.iter().any(|p| p.is_empty()) {
133 return Err(ParseTriggerError("empty segment".into()));
134 }
135 let (mod_parts, key_part) = parts.split_at(parts.len() - 1);
137 for part in mod_parts {
138 match part.to_ascii_lowercase().as_str() {
139 "shift" => mods.shift = true,
140 "control" | "ctrl" => mods.control = true,
141 "option" | "alt" => mods.option = true,
142 "command" | "cmd" => mods.command = true,
143 other => return Err(ParseTriggerError(format!("unknown modifier '{other}'"))),
144 }
145 }
146 let keycode = match key_part[0].to_ascii_lowercase().as_str() {
147 "esc" => 0x35,
148 "f1" => 0x7A,
149 "f2" => 0x78,
150 "f3" => 0x63,
151 "f4" => 0x76,
152 "f5" => 0x60,
153 "f6" => 0x61,
154 "f7" => 0x62,
155 "f8" => 0x64,
156 "f9" => 0x65,
157 "f10" => 0x6D,
158 "f11" => 0x67,
159 "f12" => 0x6F,
160 "f13" => 0x69,
161 "f14" => 0x6B,
162 "f15" => 0x71,
163 "f16" => 0x6A,
164 "f17" => 0x40,
165 "f18" => 0x4F,
166 "f19" => 0x50,
167 other => return Err(ParseTriggerError(format!("unknown key '{other}'"))),
168 };
169 Ok(KeyTrigger {
170 keycode,
171 modifiers: mods,
172 })
173 }
174}
175
176#[derive(Debug, Clone, Default, Serialize, Deserialize)]
178#[serde(deny_unknown_fields)]
179pub struct KeyboardConfig {
180 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
182 pub bindings: BTreeMap<KeyTrigger, Action>,
183}