color.rs

  1use comfy_table::{Attribute, Cell, Color};
  2use std::io::IsTerminal;
  3
  4use crate::model::{Effort, Priority, Status};
  5
  6pub struct Theme {
  7    pub red: &'static str,
  8    pub green: &'static str,
  9    pub yellow: &'static str,
 10    pub blue: &'static str,
 11    pub cyan: &'static str,
 12    pub bold: &'static str,
 13    pub bold_red: &'static str,
 14    pub bold_yellow: &'static str,
 15    pub reset: &'static str,
 16}
 17
 18const ON: Theme = Theme {
 19    red: "\x1b[31m",
 20    green: "\x1b[32m",
 21    yellow: "\x1b[33m",
 22    blue: "\x1b[34m",
 23    cyan: "\x1b[36m",
 24    bold: "\x1b[1m",
 25    bold_red: "\x1b[1;31m",
 26    bold_yellow: "\x1b[1;33m",
 27    reset: "\x1b[0m",
 28};
 29
 30const OFF: Theme = Theme {
 31    red: "",
 32    green: "",
 33    yellow: "",
 34    blue: "",
 35    cyan: "",
 36    bold: "",
 37    bold_red: "",
 38    bold_yellow: "",
 39    reset: "",
 40};
 41
 42fn use_color(is_tty: bool) -> bool {
 43    std::env::var_os("NO_COLOR").is_none() && is_tty
 44}
 45
 46/// Colour theme for stdout.
 47pub fn stdout_theme() -> &'static Theme {
 48    if use_color(std::io::stdout().is_terminal()) {
 49        &ON
 50    } else {
 51        &OFF
 52    }
 53}
 54
 55/// Colour theme for stderr.
 56pub fn stderr_theme() -> &'static Theme {
 57    if use_color(std::io::stderr().is_terminal()) {
 58        &ON
 59    } else {
 60        &OFF
 61    }
 62}
 63
 64/// Whether stdout should use colour.
 65pub fn stdout_use_color() -> bool {
 66    use_color(std::io::stdout().is_terminal())
 67}
 68
 69/// A table cell with bold text.
 70pub fn cell_bold(text: impl ToString, use_color: bool) -> Cell {
 71    let cell = Cell::new(text);
 72    if use_color {
 73        cell.add_attribute(Attribute::Bold)
 74    } else {
 75        cell
 76    }
 77}
 78
 79/// A table cell with a coloured foreground.
 80pub fn cell_fg(text: impl ToString, color: Color, use_color: bool) -> Cell {
 81    let cell = Cell::new(text);
 82    if use_color {
 83        cell.fg(color)
 84    } else {
 85        cell
 86    }
 87}
 88
 89impl Theme {
 90    /// ANSI escape for a task status.
 91    pub fn status(&self, s: Status) -> &str {
 92        match s {
 93            Status::Open => self.green,
 94            Status::InProgress => self.bold_yellow,
 95            Status::Closed => "",
 96        }
 97    }
 98
 99    /// ANSI escape for a priority level.
100    pub fn priority(&self, p: Priority) -> &str {
101        match p {
102            Priority::High => self.bold_red,
103            Priority::Medium => "",
104            Priority::Low => self.cyan,
105        }
106    }
107
108    /// ANSI escape for an effort level.
109    pub fn effort(&self, e: Effort) -> &str {
110        match e {
111            Effort::High => self.bold_red,
112            Effort::Medium => "",
113            Effort::Low => self.cyan,
114        }
115    }
116}
117
118/// A table cell styled for a task status.
119pub fn cell_status(text: impl ToString, s: Status, use_color: bool) -> Cell {
120    let cell = Cell::new(text);
121    if !use_color {
122        return cell;
123    }
124    match s {
125        Status::Open => cell.fg(Color::Green),
126        Status::InProgress => cell.fg(Color::Yellow).add_attribute(Attribute::Bold),
127        Status::Closed => cell,
128    }
129}
130
131/// A table cell styled for a priority level.
132pub fn cell_priority(text: impl ToString, p: Priority, use_color: bool) -> Cell {
133    let cell = Cell::new(text);
134    if !use_color {
135        return cell;
136    }
137    match p {
138        Priority::High => cell.fg(Color::Red).add_attribute(Attribute::Bold),
139        Priority::Medium => cell,
140        Priority::Low => cell.fg(Color::Cyan),
141    }
142}
143
144/// A table cell styled for an effort level.
145pub fn cell_effort(text: impl ToString, e: Effort, use_color: bool) -> Cell {
146    let cell = Cell::new(text);
147    if !use_color {
148        return cell;
149    }
150    match e {
151        Effort::High => cell.fg(Color::Red).add_attribute(Attribute::Bold),
152        Effort::Medium => cell,
153        Effort::Low => cell.fg(Color::Cyan),
154    }
155}