1use async::async_stdin;
16use numtoa::NumToA;
17use raw::CONTROL_SEQUENCE_TIMEOUT;
18use std::env;
19use std::fmt;
20use std::fmt::Debug;
21use std::io::{self, Read, Write};
22use std::sync::atomic::AtomicBool;
23use std::sync::atomic::Ordering;
24use std::sync::Once;
25use std::time::{Duration, SystemTime};
26
27static NO_COLOR: AtomicBool = AtomicBool::new(false);
28static INITIALIZER: Once = Once::new();
29
30fn is_no_color_set() -> bool {
34 !std::env::var("NO_COLOR")
35 .unwrap_or("".to_string())
36 .is_empty()
37}
38
39fn ansi_color_disabled() -> bool {
44 INITIALIZER.call_once(|| {
45 NO_COLOR.store(is_no_color_set(), Ordering::SeqCst);
46 });
47 NO_COLOR.load(Ordering::SeqCst)
48}
49
50pub trait Color: Debug {
52 fn write_fg(&self, f: &mut fmt::Formatter) -> fmt::Result;
54 fn write_bg(&self, f: &mut fmt::Formatter) -> fmt::Result;
56}
57
58macro_rules! derive_color {
59 ($doc:expr, $name:ident, $value:expr) => {
60 #[doc = $doc]
61 #[derive(Copy, Clone, Debug)]
62 pub struct $name;
63
64 impl Color for $name {
65 #[inline]
66 fn write_fg(&self, f: &mut fmt::Formatter) -> fmt::Result {
67 if ansi_color_disabled() {
68 return Ok(());
69 }
70 f.write_str(self.fg_str())
71 }
72
73 #[inline]
74 fn write_bg(&self, f: &mut fmt::Formatter) -> fmt::Result {
75 if ansi_color_disabled() {
76 return Ok(());
77 }
78 f.write_str(self.bg_str())
79 }
80 }
81
82 impl $name {
83 #[inline]
84 pub fn fg_str(&self) -> &'static str {
86 csi!("38;5;", $value, "m")
87 }
88
89 #[inline]
90 pub fn bg_str(&self) -> &'static str {
92 csi!("48;5;", $value, "m")
93 }
94 }
95 };
96}
97
98derive_color!("Black.", Black, "0");
99derive_color!("Red.", Red, "1");
100derive_color!("Green.", Green, "2");
101derive_color!("Yellow.", Yellow, "3");
102derive_color!("Blue.", Blue, "4");
103derive_color!("Magenta.", Magenta, "5");
104derive_color!("Cyan.", Cyan, "6");
105derive_color!("White.", White, "7");
106derive_color!("High-intensity light black.", LightBlack, "8");
107derive_color!("High-intensity light red.", LightRed, "9");
108derive_color!("High-intensity light green.", LightGreen, "10");
109derive_color!("High-intensity light yellow.", LightYellow, "11");
110derive_color!("High-intensity light blue.", LightBlue, "12");
111derive_color!("High-intensity light magenta.", LightMagenta, "13");
112derive_color!("High-intensity light cyan.", LightCyan, "14");
113derive_color!("High-intensity light white.", LightWhite, "15");
114
115impl<'a> Color for &'a dyn Color {
116 #[inline]
117 fn write_fg(&self, f: &mut fmt::Formatter) -> fmt::Result {
118 (*self).write_fg(f)
119 }
120
121 #[inline]
122 fn write_bg(&self, f: &mut fmt::Formatter) -> fmt::Result {
123 (*self).write_bg(f)
124 }
125}
126
127#[derive(Clone, Copy, Debug)]
129pub struct AnsiValue(pub u8);
130
131impl AnsiValue {
132 pub fn rgb(r: u8, g: u8, b: u8) -> AnsiValue {
134 debug_assert!(
135 r <= 5,
136 "Red color fragment (r = {}) is out of bound. Make sure r ≤ 5.",
137 r
138 );
139 debug_assert!(
140 g <= 5,
141 "Green color fragment (g = {}) is out of bound. Make sure g ≤ 5.",
142 g
143 );
144 debug_assert!(
145 b <= 5,
146 "Blue color fragment (b = {}) is out of bound. Make sure b ≤ 5.",
147 b
148 );
149
150 AnsiValue(16 + 36 * r + 6 * g + b)
151 }
152
153 pub fn grayscale(shade: u8) -> AnsiValue {
157 debug_assert!(
159 shade < 24,
160 "Grayscale out of bound (shade = {}). There are only 24 shades of \
161 gray.",
162 shade
163 );
164
165 AnsiValue(0xE8 + shade)
166 }
167}
168
169impl AnsiValue {
170 pub fn fg_string(self) -> String {
172 let mut x = [0u8; 20];
173 let x = self.0.numtoa_str(10, &mut x);
174 [csi!("38;5;"), x, "m"].concat()
175 }
176
177 pub fn bg_string(self) -> String {
179 let mut x = [0u8; 20];
180 let x = self.0.numtoa_str(10, &mut x);
181 [csi!("48;5;"), x, "m"].concat()
182 }
183}
184
185impl Color for AnsiValue {
186 #[inline]
187 fn write_fg(&self, f: &mut fmt::Formatter) -> fmt::Result {
188 if ansi_color_disabled() {
189 return Ok(());
190 }
191 f.write_str(&self.fg_string())
192 }
193
194 #[inline]
195 fn write_bg(&self, f: &mut fmt::Formatter) -> fmt::Result {
196 if ansi_color_disabled() {
197 return Ok(());
198 }
199 f.write_str(&self.bg_string())
200 }
201}
202
203#[derive(Debug, Clone, Copy, PartialEq)]
205pub struct Rgb(pub u8, pub u8, pub u8);
206
207impl Rgb {
208 pub fn fg_string(self) -> String {
210 let (mut x, mut y, mut z) = ([0u8; 20], [0u8; 20], [0u8; 20]);
211 let (x, y, z) = (
212 self.0.numtoa_str(10, &mut x),
213 self.1.numtoa_str(10, &mut y),
214 self.2.numtoa_str(10, &mut z),
215 );
216
217 [csi!("38;2;"), x, ";", y, ";", z, "m"].concat()
218 }
219
220 pub fn bg_string(self) -> String {
222 let (mut x, mut y, mut z) = ([0u8; 20], [0u8; 20], [0u8; 20]);
223 let (x, y, z) = (
224 self.0.numtoa_str(10, &mut x),
225 self.1.numtoa_str(10, &mut y),
226 self.2.numtoa_str(10, &mut z),
227 );
228
229 [csi!("48;2;"), x, ";", y, ";", z, "m"].concat()
230 }
231}
232
233impl Color for Rgb {
234 #[inline]
235 fn write_fg(&self, f: &mut fmt::Formatter) -> fmt::Result {
236 if ansi_color_disabled() {
237 return Ok(());
238 }
239 f.write_str(&self.fg_string())
240 }
241
242 #[inline]
243 fn write_bg(&self, f: &mut fmt::Formatter) -> fmt::Result {
244 if ansi_color_disabled() {
245 return Ok(());
246 }
247 f.write_str(&self.bg_string())
248 }
249}
250
251#[derive(Debug, Clone, Copy)]
253pub struct Reset;
254
255const RESET_FG: &str = csi!("39m");
256const RESET_BG: &str = csi!("49m");
257
258impl Reset {
259 pub fn fg_str(self) -> &'static str {
261 RESET_FG
262 }
263 pub fn bg_str(self) -> &'static str {
265 RESET_BG
266 }
267}
268
269impl Color for Reset {
270 #[inline]
271 fn write_fg(&self, f: &mut fmt::Formatter) -> fmt::Result {
272 f.write_str(RESET_FG)
273 }
274
275 #[inline]
276 fn write_bg(&self, f: &mut fmt::Formatter) -> fmt::Result {
277 f.write_str(RESET_BG)
278 }
279}
280
281#[derive(Debug, Clone, Copy)]
283pub struct Fg<C: Color>(pub C);
284
285impl<C: Color> fmt::Display for Fg<C> {
286 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
287 self.0.write_fg(f)
288 }
289}
290
291#[derive(Debug, Clone, Copy)]
293pub struct Bg<C: Color>(pub C);
294
295impl<C: Color> fmt::Display for Bg<C> {
296 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
297 self.0.write_bg(f)
298 }
299}
300
301pub trait DetectColors {
303 fn available_colors(&mut self) -> io::Result<u16>;
308}
309
310impl<W: Write> DetectColors for W {
311 fn available_colors(&mut self) -> io::Result<u16> {
312 let mut stdin = async_stdin();
313
314 if detect_color(self, &mut stdin, 0)? {
315 let mut min = 8;
318 let mut max = 256;
319 let mut i;
320 while min + 1 < max {
321 i = (min + max) / 2;
322 if detect_color(self, &mut stdin, i)? {
323 min = i
324 } else {
325 max = i
326 }
327 }
328 Ok(max)
329 } else {
330 Ok(match env::var_os("TERM") {
332 Some(val) => {
333 if val.to_str().unwrap_or("").contains("256color") {
334 256
335 } else {
336 8
337 }
338 }
339 None => 8,
340 })
341 }
342 }
343}
344
345fn detect_color(stdout: &mut dyn Write, stdin: &mut dyn Read, color: u16) -> io::Result<bool> {
347 write!(stdout, "\x1B]4;{};?\x07", color)?;
350 stdout.flush()?;
351
352 let mut buf: [u8; 1] = [0];
353 let mut total_read = 0;
354
355 let timeout = Duration::from_millis(CONTROL_SEQUENCE_TIMEOUT);
356 let now = SystemTime::now();
357 let bell = 7u8;
358
359 while buf[0] != bell && now.elapsed().unwrap() < timeout {
361 total_read += stdin.read(&mut buf)?;
362 }
363
364 Ok(total_read > 0)
366}