pixelactions_core/
chord.rs1#[derive(Debug, thiserror::Error, PartialEq, Eq)]
11pub enum ChordError {
12 #[error("chord {0:?} names no key — expected something like \"cmd+s\" or \"enter\"")]
13 Empty(String),
14}
15
16pub fn split(chord: &str) -> Result<(Vec<&str>, &str), ChordError> {
24 let mut parts: Vec<&str> = chord
25 .split('+')
26 .map(str::trim)
27 .filter(|part| !part.is_empty())
28 .collect();
29 let key = parts
30 .pop()
31 .ok_or_else(|| ChordError::Empty(chord.to_string()))?;
32 Ok((parts, key))
33}
34
35#[cfg(test)]
36mod tests {
37 use super::*;
38
39 #[test]
40 fn chords_split_into_modifiers_and_a_key() {
41 let (modifiers, key) = split("cmd+shift+s").expect("valid");
42 assert_eq!(modifiers, vec!["cmd", "shift"]);
43 assert_eq!(key, "s");
44 }
45
46 #[test]
47 fn a_bare_key_has_no_modifiers() {
48 let (modifiers, key) = split("enter").expect("valid");
49 assert!(modifiers.is_empty());
50 assert_eq!(key, "enter");
51 }
52
53 #[test]
54 fn whitespace_around_chord_parts_is_tolerated() {
55 let (modifiers, key) = split("cmd + s").expect("valid");
56 assert_eq!(modifiers, vec!["cmd"]);
57 assert_eq!(key, "s");
58 }
59
60 #[test]
61 fn an_empty_chord_is_an_error() {
62 assert!(split("").is_err());
63 assert!(split("+").is_err());
64 assert!(split(" ").is_err());
65 }
66
67 #[test]
68 fn the_error_quotes_the_chord_it_could_not_read() {
69 let error = split("+").expect_err("empty");
70 assert!(error.to_string().contains("\"+\""), "{error}");
71 }
72}