Skip to main content

git_stk/
style.rs

1//! Semantic terminal styles. Styled lines must be printed through
2//! `anstream::println!`/`eprintln!`, which strip color for pipes, NO_COLOR,
3//! and consoles that cannot render it.
4
5use anstyle::{AnsiColor, Style};
6
7use crate::providers::ReviewState;
8
9/// Branch names.
10pub const BRANCH: Style = AnsiColor::Cyan.on_default();
11/// The branch you are standing on.
12pub const CURRENT: Style = AnsiColor::Green.on_default().bold();
13/// Secondary detail: URLs, the trunk tag, counts.
14pub const DIM: Style = Style::new().dimmed();
15/// The `hint:` prefix.
16pub const HINT: Style = AnsiColor::Cyan.on_default();
17/// The `warning:` prefix.
18pub const WARN: Style = AnsiColor::Yellow.on_default();
19
20/// Review states, matching the ledger emoji: green open, purple merged,
21/// red closed.
22pub const OPEN: Style = AnsiColor::Green.on_default();
23pub const MERGED: Style = AnsiColor::Magenta.on_default();
24pub const CLOSED: Style = AnsiColor::Red.on_default();
25
26pub fn paint(style: Style, text: &str) -> String {
27    format!("{style}{text}{style:#}")
28}
29
30/// A review state in its color.
31pub fn state(state: &ReviewState) -> String {
32    let style = match state {
33        ReviewState::Open => OPEN,
34        ReviewState::Merged => MERGED,
35        ReviewState::Closed => CLOSED,
36        ReviewState::Unknown(_) => DIM,
37    };
38    paint(style, &state.to_string())
39}
40
41#[cfg(test)]
42mod tests {
43    use super::*;
44
45    #[test]
46    fn paint_wraps_text_in_escape_codes() {
47        let painted = paint(BRANCH, "feature/a");
48        assert!(painted.contains("feature/a"));
49        assert!(painted.starts_with('\u{1b}'));
50        assert!(painted.ends_with('m'));
51    }
52
53    #[test]
54    fn state_uses_the_ledger_palette() {
55        assert!(state(&ReviewState::Open).contains("open"));
56        assert!(state(&ReviewState::Merged).contains("merged"));
57        assert!(state(&ReviewState::Closed).contains("closed"));
58        assert_ne!(
59            state(&ReviewState::Open),
60            state(&ReviewState::Merged),
61            "states should not share a style"
62        );
63    }
64}