Skip to main content

zero_tui/app/
mode.rs

1//! The five modes (ADR-015). `Calibration`, `Scars`, `Risk`, and
2//! `Network` are overlays dispatched by `zero-commands`, not modes.
3
4use std::fmt;
5
6#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
7pub enum Mode {
8    #[default]
9    Conversation,
10    Positions,
11    Decisions,
12    Heat,
13    Cockpit,
14}
15
16impl Mode {
17    /// Short label for the status bar: `CONV`, `POS`, `DEC`, `HEAT`, `LIVE`.
18    #[must_use]
19    pub const fn short(self) -> &'static str {
20        match self {
21            Self::Conversation => "CONV",
22            Self::Positions => "POS",
23            Self::Decisions => "DEC",
24            Self::Heat => "HEAT",
25            Self::Cockpit => "LIVE",
26        }
27    }
28
29    /// Full label used in overlays and docs.
30    #[must_use]
31    pub const fn full(self) -> &'static str {
32        match self {
33            Self::Conversation => "conversation",
34            Self::Positions => "positions",
35            Self::Decisions => "decisions",
36            Self::Heat => "heat",
37            Self::Cockpit => "cockpit",
38        }
39    }
40
41    /// Decode a `Ctrl+N` digit (1..=5). `Ctrl+0` returns
42    /// [`Mode::Conversation`]. Returns `None` for Ctrl+6..9 —
43    /// those bindings are deliberately unbound (ADR-015).
44    #[must_use]
45    pub const fn from_digit(d: u8) -> Option<Self> {
46        match d {
47            0 | 1 => Some(Self::Conversation),
48            2 => Some(Self::Positions),
49            3 => Some(Self::Decisions),
50            4 => Some(Self::Heat),
51            5 => Some(Self::Cockpit),
52            _ => None,
53        }
54    }
55}
56
57impl fmt::Display for Mode {
58    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59        f.write_str(self.full())
60    }
61}
62
63#[cfg(test)]
64mod tests {
65    use super::Mode;
66
67    #[test]
68    fn digit_mapping() {
69        assert_eq!(Mode::from_digit(0), Some(Mode::Conversation));
70        assert_eq!(Mode::from_digit(1), Some(Mode::Conversation));
71        assert_eq!(Mode::from_digit(2), Some(Mode::Positions));
72        assert_eq!(Mode::from_digit(3), Some(Mode::Decisions));
73        assert_eq!(Mode::from_digit(4), Some(Mode::Heat));
74        assert_eq!(Mode::from_digit(5), Some(Mode::Cockpit));
75        for d in 6..=9u8 {
76            assert_eq!(Mode::from_digit(d), None, "Ctrl+{d} must be unbound");
77        }
78    }
79}