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