1use serde_json::json;
8
9use crate::layout;
10
11const PHYSICAL: &str = "qwertyuiopasdfghjkl;zxcvbnm,./";
13
14fn remaps(stage: usize) -> Vec<(char, char)> {
16 PHYSICAL
17 .chars()
18 .zip(layout::keys(stage))
19 .filter(|(from, to)| from != to)
20 .collect()
21}
22
23fn header(stage: usize) -> String {
24 format!(
25 "{}, stage {stage} of {}: {}",
26 layout::plan().name,
27 layout::last(),
28 layout::name(stage)
29 )
30}
31
32pub fn kanata(stage: usize) -> String {
33 let spaced = |s: &str| s.chars().map(String::from).collect::<Vec<_>>().join(" ");
34 let keys: String = layout::keys(stage).into_iter().collect();
35 format!(
36 ";; very-normal: {}\n\
37 ;; The keyboard must send QWERTY; don't also remap it in firmware.\n\
38 (defcfg process-unmapped-keys yes)\n\
39 (defsrc {})\n\
40 (deflayer stage{stage} {})\n",
41 header(stage),
42 spaced(PHYSICAL),
43 spaced(&keys),
44 )
45}
46
47fn key_code(c: char) -> String {
48 match c {
49 ';' => "semicolon".into(),
50 ',' => "comma".into(),
51 '.' => "period".into(),
52 '/' => "slash".into(),
53 '\'' => "quote".into(),
54 c => c.to_string(),
55 }
56}
57
58pub fn karabiner(stage: usize) -> String {
60 let manipulators: Vec<_> = remaps(stage)
61 .into_iter()
62 .map(|(from, to)| {
63 json!({
64 "type": "basic",
65 "from": { "key_code": key_code(from), "modifiers": { "optional": ["any"] } },
66 "to": [{ "key_code": key_code(to) }],
67 })
68 })
69 .collect();
70 let doc = json!({
71 "title": format!("very-normal: {}", layout::plan().name),
72 "rules": [{
73 "description": format!("{} (the keyboard must send QWERTY)", header(stage)),
74 "manipulators": manipulators,
75 }],
76 });
77 serde_json::to_string_pretty(&doc).expect("json") + "\n"
78}
79
80#[cfg(test)]
81mod tests {
82 use super::*;
83
84 #[test]
85 fn kanata_maps_every_key_to_the_stage() {
86 let k = kanata(1);
87 assert!(k.contains("(defsrc q w e r t y u i o p a s d f g h j k l ; z x c v b n m , . /)"));
88 assert!(k.contains(
89 "(deflayer stage1 q w l r f y u i o p a s d t g h j k e ; z x c v b n m , . /)"
90 ));
91 }
92
93 #[test]
94 fn karabiner_only_remaps_what_moved() {
95 let v: serde_json::Value = serde_json::from_str(&karabiner(1)).unwrap();
96 let m = v["rules"][0]["manipulators"].as_array().unwrap();
97 assert_eq!(m.len(), 4);
98 assert_eq!(m[0]["from"]["key_code"], "e");
99 assert_eq!(m[0]["to"][0]["key_code"], "l");
100 }
101
102 #[test]
103 fn karabiner_names_punctuation() {
104 let v: serde_json::Value = serde_json::from_str(&karabiner(layout::last())).unwrap();
105 let codes: Vec<&str> = v["rules"][0]["manipulators"]
106 .as_array()
107 .unwrap()
108 .iter()
109 .filter_map(|m| m["to"][0]["key_code"].as_str())
110 .collect();
111 assert!(codes.contains(&"semicolon"));
112 assert!(remaps(0).is_empty());
113 }
114}