1use std::fmt;
26
27#[derive(Debug, Clone, Default, PartialEq, Eq)]
29pub struct Help {
30 pub key: String,
32 pub desc: String,
34}
35
36impl Help {
37 #[must_use]
39 pub fn new(key: impl Into<String>, desc: impl Into<String>) -> Self {
40 Self {
41 key: key.into(),
42 desc: desc.into(),
43 }
44 }
45}
46
47#[derive(Debug, Clone, Default)]
52pub struct Binding {
53 keys: Vec<String>,
54 help: Help,
55 disabled: bool,
56}
57
58impl Binding {
59 #[must_use]
61 pub fn new() -> Self {
62 Self::default()
63 }
64
65 #[must_use]
76 pub fn keys(mut self, keys: &[&str]) -> Self {
77 self.keys = keys.iter().map(|&s| s.to_string()).collect();
78 self
79 }
80
81 #[must_use]
95 pub fn help(mut self, key: impl Into<String>, desc: impl Into<String>) -> Self {
96 self.help = Help::new(key, desc);
97 self
98 }
99
100 #[must_use]
102 pub fn disabled(mut self) -> Self {
103 self.disabled = true;
104 self
105 }
106
107 pub fn set_keys(&mut self, keys: &[&str]) {
109 self.keys = keys.iter().map(|&s| s.to_string()).collect();
110 }
111
112 #[must_use]
114 pub fn get_keys(&self) -> &[String] {
115 &self.keys
116 }
117
118 pub fn set_help(&mut self, key: impl Into<String>, desc: impl Into<String>) {
120 self.help = Help::new(key, desc);
121 }
122
123 #[must_use]
125 pub fn get_help(&self) -> &Help {
126 &self.help
127 }
128
129 #[must_use]
133 pub fn enabled(&self) -> bool {
134 !self.disabled && !self.keys.is_empty()
135 }
136
137 pub fn enable(&mut self, enabled: bool) {
139 self.disabled = !enabled;
140 }
141
142 #[must_use]
144 pub fn set_enabled(mut self, enabled: bool) -> Self {
145 self.disabled = !enabled;
146 self
147 }
148
149 pub fn unbind(&mut self) {
154 self.keys.clear();
155 self.help = Help::default();
156 }
157}
158
159pub fn matches<K: fmt::Display>(key: K, bindings: &[&Binding]) -> bool {
177 let key_str = key.to_string();
178 for binding in bindings {
179 if binding.enabled() {
180 for k in &binding.keys {
181 if *k == key_str {
182 return true;
183 }
184 }
185 }
186 }
187 false
188}
189
190pub fn matches_one<K: fmt::Display>(key: K, binding: &Binding) -> bool {
194 matches(key, &[binding])
195}
196
197#[cfg(test)]
198mod tests {
199 use super::*;
200
201 #[test]
202 fn test_binding_new() {
203 let binding = Binding::new();
204 assert!(binding.get_keys().is_empty());
205 assert!(!binding.enabled());
206 }
207
208 #[test]
209 fn test_binding_with_keys() {
210 let binding = Binding::new().keys(&["k", "up"]);
211 assert_eq!(binding.get_keys(), &["k", "up"]);
212 assert!(binding.enabled());
213 }
214
215 #[test]
216 fn test_binding_with_help() {
217 let binding = Binding::new()
218 .keys(&["q"])
219 .help("q", "quit the application");
220 assert_eq!(binding.get_help().key, "q");
221 assert_eq!(binding.get_help().desc, "quit the application");
222 }
223
224 #[test]
225 fn test_binding_disabled() {
226 let binding = Binding::new().keys(&["q"]).disabled();
227 assert!(!binding.enabled());
228 }
229
230 #[test]
231 fn test_binding_set_enabled() {
232 let mut binding = Binding::new().keys(&["q"]).disabled();
233 assert!(!binding.enabled());
234 binding.enable(true);
235 assert!(binding.enabled());
236 }
237
238 #[test]
239 fn test_binding_set_enabled_builder() {
240 let binding = Binding::new().keys(&["q"]).set_enabled(false);
241 assert!(!binding.enabled());
242 let binding = binding.set_enabled(true);
243 assert!(binding.enabled());
244 }
245
246 #[test]
247 fn test_binding_unbind() {
248 let mut binding = Binding::new().keys(&["q"]).help("q", "quit");
249 binding.unbind();
250 assert!(binding.get_keys().is_empty());
251 assert!(binding.get_help().key.is_empty());
252 }
253
254 #[test]
255 fn test_matches() {
256 let up = Binding::new().keys(&["k", "up"]);
257 let down = Binding::new().keys(&["j", "down"]);
258
259 assert!(matches("k", &[&up, &down]));
260 assert!(matches("up", &[&up, &down]));
261 assert!(matches("j", &[&up, &down]));
262 assert!(matches("down", &[&up, &down]));
263 assert!(!matches("x", &[&up, &down]));
264 }
265
266 #[test]
267 fn test_matches_disabled() {
268 let binding = Binding::new().keys(&["q"]).disabled();
269 assert!(!matches("q", &[&binding]));
270 }
271
272 #[test]
273 fn test_matches_empty() {
274 let binding = Binding::new();
275 assert!(!matches("q", &[&binding]));
276 }
277
278 #[test]
279 fn test_matches_one() {
280 let quit = Binding::new().keys(&["q", "ctrl+c"]);
281 assert!(matches_one("q", &quit));
282 assert!(matches_one("ctrl+c", &quit));
283 assert!(!matches_one("x", &quit));
284 }
285}