1use anstyle::{AnsiColor, Style};
6
7use crate::providers::ReviewState;
8
9pub const BRANCH: Style = AnsiColor::Cyan.on_default();
11pub const CURRENT: Style = AnsiColor::Green.on_default().bold();
13pub const DIM: Style = Style::new().dimmed();
15pub const HINT: Style = AnsiColor::Cyan.on_default();
17pub const WARN: Style = AnsiColor::Yellow.on_default();
19pub const ERROR: Style = AnsiColor::Red.on_default().bold();
21
22pub const OPEN: Style = AnsiColor::Green.on_default();
25pub const MERGED: Style = AnsiColor::Magenta.on_default();
26pub const CLOSED: Style = AnsiColor::Red.on_default();
27
28pub const ADDED: Style = AnsiColor::Green.on_default().dimmed();
30pub const REMOVED: Style = AnsiColor::Red.on_default().dimmed();
31
32pub fn paint(style: Style, text: &str) -> String {
33 format!("{style}{text}{style:#}")
34}
35
36pub fn branch(name: &str) -> String {
38 paint(BRANCH, name)
39}
40
41pub fn dim(text: &str) -> String {
43 paint(DIM, text)
44}
45
46pub fn success(text: &str) -> String {
48 paint(OPEN, text)
49}
50
51pub fn warn(text: &str) -> String {
53 paint(WARN, text)
54}
55
56pub fn hint_prefix() -> String {
58 paint(HINT, "hint:")
59}
60
61pub fn error_prefix() -> String {
63 paint(ERROR, "error:")
64}
65
66pub fn state(state: &ReviewState) -> String {
68 let style = match state {
69 ReviewState::Open => OPEN,
70 ReviewState::Merged => MERGED,
71 ReviewState::Closed => CLOSED,
72 ReviewState::Unknown(_) => DIM,
73 };
74 paint(style, &state.to_string())
75}
76
77#[cfg(test)]
78mod tests {
79 use super::*;
80
81 #[test]
82 fn paint_wraps_text_in_escape_codes() {
83 let painted = paint(BRANCH, "feature/a");
84 assert!(painted.contains("feature/a"));
85 assert!(painted.starts_with('\u{1b}'));
86 assert!(painted.ends_with('m'));
87 }
88
89 #[test]
90 fn state_uses_the_ledger_palette() {
91 assert!(state(&ReviewState::Open).contains("open"));
92 assert!(state(&ReviewState::Merged).contains("merged"));
93 assert!(state(&ReviewState::Closed).contains("closed"));
94 assert_ne!(
95 state(&ReviewState::Open),
96 state(&ReviewState::Merged),
97 "states should not share a style"
98 );
99 }
100}