Skip to main content

very_normal/
report.rs

1//! Plain-text explanations shared by the command line and the TUI.
2
3use crate::layout;
4use crate::oryx::Snapshot;
5
6/// The Oryx edits that turn `stage - 1` into `stage`.
7pub fn edits(stage: usize) -> Vec<String> {
8    let mut out = Vec::new();
9    for c in layout::changes(stage) {
10        let hold =
11            layout::modifier(c.index).map_or(String::new(), |m| format!("  (keep the {m} hold)"));
12        out.push(format!("key that types {} → {}{hold}", c.from, c.to));
13    }
14    for c in layout::changes(stage).iter().filter(|c| c.right_hand()) {
15        out.push(format!(
16            "mirror layer: key that types {} → {}",
17            c.from, c.to
18        ));
19    }
20    out
21}
22
23pub fn next_stage(current: usize, url: &str) -> Vec<String> {
24    if current >= layout::last() {
25        return vec!["You're on the last stage. Nothing left to edit.".into()];
26    }
27    let next = current + 1;
28    let mut out = vec![
29        format!("Stage {next}: {}", layout::name(next)),
30        format!("Edit: {url}"),
31    ];
32    out.extend(edits(next).into_iter().map(|e| format!("  {e}")));
33    out.push(format!(
34        "  vim to practise: {}",
35        layout::vim(next).join(" ")
36    ));
37    out
38}
39
40/// The same, squeezed into a few lines for the TUI.
41pub fn next_stage_compact(current: usize, url: &str) -> Vec<String> {
42    if current >= layout::last() {
43        return vec!["You're on the last stage. Nothing left to edit.".into()];
44    }
45    let next = current + 1;
46    let changes = layout::changes(next);
47    let swap = |c: &layout::Change| format!("{}→{}", c.from, c.to);
48    let keys: Vec<String> = changes
49        .iter()
50        .map(|c| match layout::modifier(c.index) {
51            Some(m) => format!("{} (keep {m})", swap(c)),
52            None => swap(c),
53        })
54        .collect();
55    let mirror: Vec<String> = changes
56        .iter()
57        .filter(|c| c.right_hand())
58        .map(swap)
59        .collect();
60    let mut out = vec![
61        format!("Stage {next}: {}", layout::name(next)),
62        format!("keys: {}", keys.join("  ")),
63    ];
64    if !mirror.is_empty() {
65        out.push(format!("mirror: {}", mirror.join("  ")));
66    }
67    out.push(format!("vim: {}", layout::vim(next).join(" ")));
68    out.push(url.to_string());
69    out
70}
71
72/// What Oryx says the board is on, measured against the stage you practise.
73pub fn check(snap: &Snapshot, current: usize) -> Vec<String> {
74    let mut out = vec![format!(
75        "{} · revision {} · {}",
76        snap.title, snap.revision, snap.created
77    )];
78    let findings = snap.compare(current);
79    match snap.stage() {
80        Some(s) if s == current && findings.is_empty() => {
81            out.push(format!(
82                "Matches stage {current}: every key, hold and mirror key."
83            ));
84        }
85        Some(s) if s == current + 1 && snap.compare(s).is_empty() => {
86            out.push(format!(
87                "Stage {s}'s edits are all in. Flash it and climb to stage {s}."
88            ));
89        }
90        Some(s) if snap.compare(s).is_empty() => {
91            out.push(format!(
92                "Matches stage {s}, but you're practising stage {current}."
93            ));
94        }
95        _ => {
96            let target =
97                if current < layout::last() && snap.compare(current + 1).len() < findings.len() {
98                    current + 1
99                } else {
100                    current
101                };
102            let f = snap.compare(target);
103            out.push(format!(
104                "Closest to stage {target}, with {} difference(s):",
105                f.len()
106            ));
107            out.extend(f.iter().take(8).map(|f| format!("  {f}")));
108            if f.len() > 8 {
109                out.push(format!("  and {} more", f.len() - 8));
110            }
111        }
112    }
113    out
114}
115
116#[cfg(test)]
117mod tests {
118    use super::*;
119
120    #[test]
121    fn stage_two_edits_keep_holds_and_list_mirror_keys() {
122        let e = edits(2);
123        assert!(
124            e.contains(&"key that types k → o  (keep the alt hold)".to_string()),
125            "{e:?}"
126        );
127        assert_eq!(e.iter().filter(|l| l.starts_with("mirror")).count(), 3);
128    }
129
130    #[test]
131    fn nothing_is_left_after_the_last_stage() {
132        assert_eq!(next_stage(layout::last(), "u").len(), 1);
133        assert_eq!(next_stage(0, "u")[1], "Edit: u");
134    }
135}