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();
19
20pub 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
30pub fn branch(name: &str) -> String {
32 paint(BRANCH, name)
33}
34
35pub fn dim(text: &str) -> String {
37 paint(DIM, text)
38}
39
40pub fn success(text: &str) -> String {
42 paint(OPEN, text)
43}
44
45pub fn warn(text: &str) -> String {
47 paint(WARN, text)
48}
49
50pub fn hint_prefix() -> String {
52 paint(HINT, "hint:")
53}
54
55pub fn state(state: &ReviewState) -> String {
57 let style = match state {
58 ReviewState::Open => OPEN,
59 ReviewState::Merged => MERGED,
60 ReviewState::Closed => CLOSED,
61 ReviewState::Unknown(_) => DIM,
62 };
63 paint(style, &state.to_string())
64}
65
66#[cfg(test)]
67mod tests {
68 use super::*;
69
70 #[test]
71 fn paint_wraps_text_in_escape_codes() {
72 let painted = paint(BRANCH, "feature/a");
73 assert!(painted.contains("feature/a"));
74 assert!(painted.starts_with('\u{1b}'));
75 assert!(painted.ends_with('m'));
76 }
77
78 #[test]
79 fn state_uses_the_ledger_palette() {
80 assert!(state(&ReviewState::Open).contains("open"));
81 assert!(state(&ReviewState::Merged).contains("merged"));
82 assert!(state(&ReviewState::Closed).contains("closed"));
83 assert_ne!(
84 state(&ReviewState::Open),
85 state(&ReviewState::Merged),
86 "states should not share a style"
87 );
88 }
89}