1use std::fmt;
16use raw::CONTROL_SEQUENCE_TIMEOUT;
17use std::io::{self, Write, Read};
18use std::time::{SystemTime, Duration};
19use async::async_stdin;
20use std::env;
21use std::fmt::Debug;
22use numtoa::NumToA;
23
24pub trait Color: Debug {
26 fn write_fg(&self, f: &mut fmt::Formatter) -> fmt::Result;
28 fn write_bg(&self, f: &mut fmt::Formatter) -> fmt::Result;
30}
31
32macro_rules! derive_color {
33 ($doc:expr, $name:ident, $value:expr) => {
34 #[doc = $doc]
35 #[derive(Copy, Clone, Debug)]
36 pub struct $name;
37
38 impl Color for $name {
39 #[inline]
40 fn write_fg(&self, f: &mut fmt::Formatter) -> fmt::Result {
41 f.write_str(self.fg_str())
42 }
43
44 #[inline]
45 fn write_bg(&self, f: &mut fmt::Formatter) -> fmt::Result {
46 f.write_str(self.bg_str())
47 }
48 }
49
50 impl $name {
51 #[inline]
52 pub fn fg_str(&self) -> &'static str { csi!("38;5;", $value, "m") }
54
55 #[inline]
56 pub fn bg_str(&self) -> &'static str { csi!("48;5;", $value, "m") }
58 }
59 };
60}
61
62derive_color!("Black.", Black, "0");
63derive_color!("Red.", Red, "1");
64derive_color!("Green.", Green, "2");
65derive_color!("Yellow.", Yellow, "3");
66derive_color!("Blue.", Blue, "4");
67derive_color!("Magenta.", Magenta, "5");
68derive_color!("Cyan.", Cyan, "6");
69derive_color!("White.", White, "7");
70derive_color!("High-intensity light black.", LightBlack, "8");
71derive_color!("High-intensity light red.", LightRed, "9");
72derive_color!("High-intensity light green.", LightGreen, "10");
73derive_color!("High-intensity light yellow.", LightYellow, "11");
74derive_color!("High-intensity light blue.", LightBlue, "12");
75derive_color!("High-intensity light magenta.", LightMagenta, "13");
76derive_color!("High-intensity light cyan.", LightCyan, "14");
77derive_color!("High-intensity light white.", LightWhite, "15");
78
79impl<'a> Color for &'a Color {
80 #[inline]
81 fn write_fg(&self, f: &mut fmt::Formatter) -> fmt::Result {
82 (*self).write_fg(f)
83 }
84
85 #[inline]
86 fn write_bg(&self, f: &mut fmt::Formatter) -> fmt::Result {
87 (*self).write_bg(f)
88 }
89}
90
91#[derive(Clone, Copy, Debug)]
93pub struct AnsiValue(pub u8);
94
95impl AnsiValue {
96 pub fn rgb(r: u8, g: u8, b: u8) -> AnsiValue {
98 debug_assert!(r <= 5,
99 "Red color fragment (r = {}) is out of bound. Make sure r ≤ 5.",
100 r);
101 debug_assert!(g <= 5,
102 "Green color fragment (g = {}) is out of bound. Make sure g ≤ 5.",
103 g);
104 debug_assert!(b <= 5,
105 "Blue color fragment (b = {}) is out of bound. Make sure b ≤ 5.",
106 b);
107
108 AnsiValue(16 + 36 * r + 6 * g + b)
109 }
110
111 pub fn grayscale(shade: u8) -> AnsiValue {
115 debug_assert!(shade < 24,
117 "Grayscale out of bound (shade = {}). There are only 24 shades of \
118 gray.",
119 shade);
120
121 AnsiValue(0xE8 + shade)
122 }
123}
124
125impl AnsiValue {
126 pub fn fg_string(self) -> String {
128 let mut x = [0u8; 20];
129 let x = self.0.numtoa_str(10, &mut x);
130 [csi!("38;5;"), x, "m"].concat()
131 }
132
133 pub fn bg_string(self) -> String {
135 let mut x = [0u8; 20];
136 let x = self.0.numtoa_str(10, &mut x);
137 [csi!("48;5;"), x, "m"].concat()
138 }
139}
140
141impl Color for AnsiValue {
142 #[inline]
143 fn write_fg(&self, f: &mut fmt::Formatter) -> fmt::Result {
144 f.write_str(&self.fg_string())
145 }
146
147 #[inline]
148 fn write_bg(&self, f: &mut fmt::Formatter) -> fmt::Result {
149 f.write_str(&self.bg_string())
150 }
151}
152
153#[derive(Debug, Clone, Copy, PartialEq)]
155pub struct Rgb(pub u8, pub u8, pub u8);
156
157impl Rgb {
158 pub fn fg_string(self) -> String {
160 let (mut x, mut y, mut z) = ([0u8; 20], [0u8; 20], [0u8; 20]);
161 let (x, y, z) = (
162 self.0.numtoa_str(10, &mut x),
163 self.1.numtoa_str(10, &mut y),
164 self.2.numtoa_str(10, &mut z),
165 );
166
167 [csi!("38;2;"), x, ";", y, ";", z, "m"].concat()
168 }
169
170 pub fn bg_string(self) -> String {
172 let (mut x, mut y, mut z) = ([0u8; 20], [0u8; 20], [0u8; 20]);
173 let (x, y, z) = (
174 self.0.numtoa_str(10, &mut x),
175 self.1.numtoa_str(10, &mut y),
176 self.2.numtoa_str(10, &mut z),
177 );
178
179 [csi!("48;2;"), x, ";", y, ";", z, "m"].concat()
180 }
181}
182
183impl Color for Rgb {
184 #[inline]
185 fn write_fg(&self, f: &mut fmt::Formatter) -> fmt::Result {
186 f.write_str(&self.fg_string())
187 }
188
189 #[inline]
190 fn write_bg(&self, f: &mut fmt::Formatter) -> fmt::Result {
191 f.write_str(&self.bg_string())
192 }
193}
194
195#[derive(Debug, Clone, Copy)]
197pub struct Reset;
198
199const RESET_FG: &str = csi!("39m");
200const RESET_BG: &str = csi!("49m");
201
202impl Reset {
203 pub fn fg_str(self) -> &'static str { RESET_FG }
205 pub fn bg_str(self) -> &'static str { RESET_BG }
207}
208
209impl Color for Reset {
210 #[inline]
211 fn write_fg(&self, f: &mut fmt::Formatter) -> fmt::Result {
212 f.write_str(RESET_FG)
213 }
214
215 #[inline]
216 fn write_bg(&self, f: &mut fmt::Formatter) -> fmt::Result {
217 f.write_str(RESET_BG)
218 }
219}
220
221#[derive(Debug, Clone, Copy)]
223pub struct Fg<C: Color>(pub C);
224
225impl<C: Color> fmt::Display for Fg<C> {
226 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
227 self.0.write_fg(f)
228 }
229}
230
231#[derive(Debug, Clone, Copy)]
233pub struct Bg<C: Color>(pub C);
234
235impl<C: Color> fmt::Display for Bg<C> {
236 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
237 self.0.write_bg(f)
238 }
239}
240
241pub trait DetectColors {
243 fn available_colors(&mut self) -> io::Result<u16>;
248}
249
250impl<W: Write> DetectColors for W {
251 fn available_colors(&mut self) -> io::Result<u16> {
252 let mut stdin = async_stdin();
253
254 if detect_color(self, &mut stdin, 0)? {
255 let mut min = 8;
258 let mut max = 256;
259 let mut i;
260 while min + 1 < max {
261 i = (min + max) / 2;
262 if detect_color(self, &mut stdin, i)? {
263 min = i
264 } else {
265 max = i
266 }
267 }
268 Ok(max)
269 } else {
270 Ok(match env::var_os("TERM") {
272 Some(val) => {
273 if val.to_str().unwrap_or("").contains("256color") {
274 256
275 } else {
276 8
277 }
278 }
279 None => 8,
280 })
281 }
282 }
283}
284
285fn detect_color(stdout: &mut Write, stdin: &mut Read, color: u16) -> io::Result<bool> {
287 write!(stdout, "\x1B]4;{};?\x07", color)?;
290 stdout.flush()?;
291
292 let mut buf: [u8; 1] = [0];
293 let mut total_read = 0;
294
295 let timeout = Duration::from_millis(CONTROL_SEQUENCE_TIMEOUT);
296 let now = SystemTime::now();
297 let bell = 7u8;
298
299 while buf[0] != bell && now.elapsed().unwrap() < timeout {
301 total_read += stdin.read(&mut buf)?;
302 }
303
304 Ok(total_read > 0)
306}