1use std::io::IsTerminal;
4
5pub type RgbColor = (u8, u8, u8);
7
8#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
10pub enum Color {
11 Black,
12 Red,
13 Green,
14 Yellow,
15 Blue,
16 Magenta,
17 Cyan,
18 White,
19 Long(u8),
20 Rgb(RgbColor),
21}
22
23impl From<u8> for Color {
24 fn from(n: u8) -> Self {
25 match n {
26 0 => Self::Black,
27 1 => Self::Red,
28 2 => Self::Green,
29 3 => Self::Yellow,
30 4 => Self::Blue,
31 5 => Self::Magenta,
32 6 => Self::Cyan,
33 7 => Self::White,
34 _ => Self::Long(n),
35 }
36 }
37}
38
39impl From<RgbColor> for Color {
40 fn from(n: RgbColor) -> Self {
41 Self::Rgb(n)
42 }
43}
44
45#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
47pub enum ColorMode {
48 On,
50 Off,
52}
53
54impl Default for ColorMode {
55 fn default() -> Self {
56 if std::io::stdout().is_terminal() {
57 Self::On
58 } else {
59 Self::Off
60 }
61 }
62}
63
64impl From<&str> for ColorMode {
65 fn from(value: &str) -> Self {
67 Self::new(value)
68 }
69}
70
71impl From<String> for ColorMode {
72 fn from(value: String) -> Self {
74 Self::new(&value)
75 }
76}
77
78impl From<&String> for ColorMode {
79 fn from(value: &String) -> Self {
81 Self::new(value)
82 }
83}
84
85impl From<Option<String>> for ColorMode {
86 fn from(value: Option<String>) -> Self {
88 value.map_or(Self::default(), |s| Self::new(&s))
89 }
90}
91
92impl From<Option<&String>> for ColorMode {
93 fn from(value: Option<&String>) -> Self {
95 value.map_or(Self::default(), |s| Self::new(s))
96 }
97}
98
99impl ColorMode {
100 pub fn new(s: &str) -> Self {
101 match s.to_lowercase().trim() {
102 "never" => Self::Off,
103 "always" => Self::On,
104 _ => Self::default(),
105 }
106 }
107
108 pub fn black(&self) -> String {
109 self.color_8(0)
110 }
111
112 pub fn red(&self) -> String {
113 self.color_8(1)
114 }
115
116 pub fn green(&self) -> String {
117 self.color_8(2)
118 }
119
120 pub fn yellow(&self) -> String {
121 self.color_8(3)
122 }
123
124 pub fn blue(&self) -> String {
125 self.color_8(4)
126 }
127
128 pub fn magenta(&self) -> String {
129 self.color_8(5)
130 }
131
132 pub fn cyan(&self) -> String {
133 self.color_8(6)
134 }
135
136 pub fn white(&self) -> String {
137 self.color_8(7)
138 }
139
140 pub fn bg_black(&self) -> String {
141 self.bg_color_8(0)
142 }
143
144 pub fn bg_red(&self) -> String {
145 self.bg_color_8(1)
146 }
147
148 pub fn bg_green(&self) -> String {
149 self.bg_color_8(2)
150 }
151
152 pub fn bg_yellow(&self) -> String {
153 self.bg_color_8(3)
154 }
155
156 pub fn bg_blue(&self) -> String {
157 self.bg_color_8(4)
158 }
159
160 pub fn bg_magenta(&self) -> String {
161 self.bg_color_8(5)
162 }
163
164 pub fn bg_cyan(&self) -> String {
165 self.bg_color_8(6)
166 }
167
168 pub fn bg_white(&self) -> String {
169 self.bg_color_8(7)
170 }
171
172 pub fn color(&self, c: Color) -> String {
173 match c {
174 Color::Black => self.color_8(0),
175 Color::Red => self.color_8(1),
176 Color::Green => self.color_8(2),
177 Color::Yellow => self.color_8(3),
178 Color::Blue => self.color_8(4),
179 Color::Magenta => self.color_8(5),
180 Color::Cyan => self.color_8(6),
181 Color::White => self.color_8(7),
182 Color::Long(value) => self.color_256(value),
183 Color::Rgb(value) => self.color_rgb(value),
184 }
185 }
186
187 pub fn bg_color(&self, c: Color) -> String {
188 match c {
189 Color::Black => self.bg_color_8(0),
190 Color::Red => self.bg_color_8(1),
191 Color::Green => self.bg_color_8(2),
192 Color::Yellow => self.bg_color_8(3),
193 Color::Blue => self.bg_color_8(4),
194 Color::Magenta => self.bg_color_8(5),
195 Color::Cyan => self.bg_color_8(6),
196 Color::White => self.bg_color_8(7),
197 Color::Long(value) => self.bg_color_256(value),
198 Color::Rgb(value) => self.bg_color_rgb(value),
199 }
200 }
201
202 pub fn color_8(&self, c: u8) -> String {
203 match self {
204 ColorMode::On => format!("\u{1b}[{}m", 30 + c.clamp(0, 7)),
205 _ => "".to_string(),
206 }
207 }
208
209 pub fn bg_color_8(&self, c: u8) -> String {
210 match self {
211 ColorMode::On => format!("\u{1b}[{}m", 40 + c.clamp(0, 7)),
212 _ => "".to_string(),
213 }
214 }
215
216 pub fn color_256(&self, c: u8) -> String {
217 match self {
218 ColorMode::On => format!("\u{1b}[38;5;{}m", c),
219 _ => "".to_string(),
220 }
221 }
222
223 pub fn bg_color_256(&self, c: u8) -> String {
224 match self {
225 ColorMode::On => format!("\u{1b}[48;5;{}m", c),
226 _ => "".to_string(),
227 }
228 }
229
230 pub fn color_rgb(&self, c: RgbColor) -> String {
231 match self {
232 ColorMode::On => format!("\u{1b}[38;2;{};{};{}m", c.0, c.1, c.2),
233 _ => "".to_string(),
234 }
235 }
236
237 pub fn bg_color_rgb(&self, c: RgbColor) -> String {
238 match self {
239 ColorMode::On => format!("\u{1b}[48;2;{};{};{}m", c.0, c.1, c.2),
240 _ => "".to_string(),
241 }
242 }
243
244 pub fn bold(&self) -> &str {
245 match self {
246 ColorMode::On => "\u{1b}[1m",
247 _ => "",
248 }
249 }
250
251 pub fn italic(&self) -> &str {
252 match self {
253 ColorMode::On => "\u{1b}[3m",
254 _ => "",
255 }
256 }
257
258 pub fn underline(&self) -> &str {
259 match self {
260 ColorMode::On => "\u{1b}[4m",
261 _ => "",
262 }
263 }
264
265 pub fn clear(&self) -> &str {
266 match self {
267 ColorMode::On => "\u{1b}[0m",
268 _ => "",
269 }
270 }
271}