paris/formatter/
color.rs

1use super::concerns::Ansi;
2use super::keys::FromKey;
3use std::str::FromStr;
4
5pub enum Color {
6    Black,
7    Red,
8    Green,
9    Yellow,
10    Blue,
11    Magenta,
12    Cyan,
13    White,
14    BrightBlack,
15    BrightRed,
16    BrightGreen,
17    BrightYellow,
18    BrightBlue,
19    BrightMagenta,
20    BrightCyan,
21    BrightWhite,
22
23    BackgroundReset,
24    ForegroundReset,
25    Reset,
26
27    None, // So we can check if its a color or not
28          // In case there's HTML in the logs
29}
30
31impl Color {
32    pub fn get_fg_value(&self) -> u8 {
33        match *self {
34            Color::Black => 30,
35            Color::Red => 31,
36            Color::Green => 32,
37            Color::Yellow => 33,
38            Color::Blue => 34,
39            Color::Magenta => 35,
40            Color::Cyan => 36,
41            Color::White => 37,
42            Color::BrightBlack => 90,
43            Color::BrightRed => 91,
44            Color::BrightGreen => 92,
45            Color::BrightYellow => 93,
46            Color::BrightBlue => 94,
47            Color::BrightMagenta => 95,
48            Color::BrightCyan => 96,
49            Color::BrightWhite => 97,
50
51            Color::ForegroundReset => 39,
52            Color::BackgroundReset => 49,
53            Color::Reset => 0,
54
55            Color::None => 0,
56        }
57    }
58
59    pub fn get_bg_value(&self) -> u8 {
60        match *self {
61            Color::Black => 40,
62            Color::Red => 41,
63            Color::Green => 42,
64            Color::Yellow => 43,
65            Color::Blue => 44,
66            Color::Magenta => 45,
67            Color::Cyan => 46,
68            Color::White => 47,
69            Color::BrightBlack => 100,
70            Color::BrightRed => 101,
71            Color::BrightGreen => 102,
72            Color::BrightYellow => 103,
73            Color::BrightBlue => 104,
74            Color::BrightMagenta => 105,
75            Color::BrightCyan => 106,
76            Color::BrightWhite => 107,
77
78            Color::ForegroundReset => 39,
79            Color::BackgroundReset => 49,
80            Color::Reset => 0,
81
82            Color::None => 0,
83        }
84    }
85}
86
87impl<'a> From<&'a str> for Color {
88    fn from(s: &str) -> Self {
89        s.parse().unwrap_or(Color::None)
90    }
91}
92
93impl FromStr for Color {
94    type Err = ();
95
96    fn from_str(s: &str) -> Result<Self, Self::Err> {
97        let s = s.to_lowercase();
98
99        match s.as_ref() {
100            "black" => Ok(Color::Black),
101            "red" => Ok(Color::Red),
102            "green" => Ok(Color::Green),
103            "yellow" => Ok(Color::Yellow),
104            "blue" => Ok(Color::Blue),
105            "magenta" => Ok(Color::Magenta),
106            "cyan" => Ok(Color::Cyan),
107            "white" => Ok(Color::White),
108            "bright black" => Ok(Color::BrightBlack),
109            "bright red" => Ok(Color::BrightRed),
110            "bright green" => Ok(Color::BrightGreen),
111            "bright yellow" => Ok(Color::BrightYellow),
112            "bright blue" => Ok(Color::BrightBlue),
113            "bright magenta" => Ok(Color::BrightMagenta),
114            "bright cyan" => Ok(Color::BrightCyan),
115            "bright white" => Ok(Color::BrightWhite),
116
117            // Different resets for each color or everything
118            "///" => Ok(Color::BackgroundReset),
119            "//" => Ok(Color::ForegroundReset),
120            "/" => Ok(Color::Reset),
121
122            _ => Err(()),
123        }
124    }
125}
126
127impl FromKey for Color {
128    /// Convert a str to an Ansi color code based
129    /// on the key standard that the colored lib has.
130    /// With some of my own additions.
131    ///
132    /// # Example
133    /// If "red" is passed, it'll become the red foreground
134    /// color code. If "on red" is passed, it'll become the
135    /// red background color code.
136    fn from_key(key: &str) -> Option<String> {
137        let is_bg = key.starts_with("on");
138
139        let color = Color::from(key.trim_start_matches("on "));
140
141        match color {
142            Color::None => None,
143            _ => {
144                if is_bg {
145                    return Some(Ansi::escape(color.get_bg_value()));
146                }
147
148                Some(Ansi::escape(color.get_fg_value()))
149            }
150        }
151    }
152}
153
154#[cfg(test)]
155mod tests {
156    use super::*;
157
158    macro_rules! color_test {
159        ($name:ident, $key:expr, $value:expr) => {
160            #[test]
161            fn $name() {
162                let v = String::from(format!("\x1B[{}m", $value));
163                let c = Color::from_key($key).unwrap();
164
165                assert_eq!(c, v);
166            }
167        };
168    }
169
170    color_test!(reset, "/", 0);
171    color_test!(background, "on red", 41);
172    color_test!(foreground, "red", 31);
173}