1/// Any ANSI color code scheme
2#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
3pub enum Color {
4 Ansi(AnsiColor),
5 Ansi256(Ansi256Color),
6 Rgb(RgbColor),
7}
8
9impl Color {
10 /// Create a [`Style`][crate::Style] with this as the foreground
11 #[inline]
12 pub fn on(self, background: impl Into<Color>) -> crate::Style {
13 crate::Style::new()
14 .fg_color(Some(self))
15 .bg_color(Some(background.into()))
16 }
17
18 /// Create a [`Style`][crate::Style] with this as the foreground
19 #[inline]
20 pub const fn on_default(self) -> crate::Style {
21 crate::Style::new().fg_color(Some(self))
22 }
23
24 /// Render the ANSI code for a foreground color
25 #[inline]
26 pub fn render_fg(self) -> impl core::fmt::Display + Copy + Clone {
27 match self {
28 Self::Ansi(color) => DisplayBuffer::default().write_str(color.as_fg_str()),
29 Self::Ansi256(color) => color.as_fg_buffer(),
30 Self::Rgb(color) => color.as_fg_buffer(),
31 }
32 }
33
34 #[inline]
35 #[cfg(feature = "std")]
36 pub(crate) fn write_fg_to(self, write: &mut dyn std::io::Write) -> std::io::Result<()> {
37 let buffer = match self {
38 Self::Ansi(color) => DisplayBuffer::default().write_str(color.as_fg_str()),
39 Self::Ansi256(color) => color.as_fg_buffer(),
40 Self::Rgb(color) => color.as_fg_buffer(),
41 };
42 buffer.write_to(write)
43 }
44
45 /// Render the ANSI code for a background color
46 #[inline]
47 pub fn render_bg(self) -> impl core::fmt::Display + Copy + Clone {
48 match self {
49 Self::Ansi(color) => DisplayBuffer::default().write_str(color.as_bg_str()),
50 Self::Ansi256(color) => color.as_bg_buffer(),
51 Self::Rgb(color) => color.as_bg_buffer(),
52 }
53 }
54
55 #[inline]
56 #[cfg(feature = "std")]
57 pub(crate) fn write_bg_to(self, write: &mut dyn std::io::Write) -> std::io::Result<()> {
58 let buffer = match self {
59 Self::Ansi(color) => DisplayBuffer::default().write_str(color.as_bg_str()),
60 Self::Ansi256(color) => color.as_bg_buffer(),
61 Self::Rgb(color) => color.as_bg_buffer(),
62 };
63 buffer.write_to(write)
64 }
65
66 #[inline]
67 pub(crate) fn render_underline(self) -> impl core::fmt::Display + Copy + Clone {
68 match self {
69 Self::Ansi(color) => color.as_underline_buffer(),
70 Self::Ansi256(color) => color.as_underline_buffer(),
71 Self::Rgb(color) => color.as_underline_buffer(),
72 }
73 }
74
75 #[inline]
76 #[cfg(feature = "std")]
77 pub(crate) fn write_underline_to(self, write: &mut dyn std::io::Write) -> std::io::Result<()> {
78 let buffer = match self {
79 Self::Ansi(color) => color.as_underline_buffer(),
80 Self::Ansi256(color) => color.as_underline_buffer(),
81 Self::Rgb(color) => color.as_underline_buffer(),
82 };
83 buffer.write_to(write)
84 }
85}
86
87impl From<AnsiColor> for Color {
88 #[inline]
89 fn from(inner: AnsiColor) -> Self {
90 Self::Ansi(inner)
91 }
92}
93
94impl From<Ansi256Color> for Color {
95 #[inline]
96 fn from(inner: Ansi256Color) -> Self {
97 Self::Ansi256(inner)
98 }
99}
100
101impl From<RgbColor> for Color {
102 #[inline]
103 fn from(inner: RgbColor) -> Self {
104 Self::Rgb(inner)
105 }
106}
107
108impl From<u8> for Color {
109 #[inline]
110 fn from(inner: u8) -> Self {
111 Self::Ansi256(inner.into())
112 }
113}
114
115impl From<(u8, u8, u8)> for Color {
116 #[inline]
117 fn from(inner: (u8, u8, u8)) -> Self {
118 Self::Rgb(inner.into())
119 }
120}
121
122/// Available 4-bit ANSI color palette codes
123///
124/// The user's terminal defines the meaning of the each palette code.
125#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
126#[repr(u8)]
127pub enum AnsiColor {
128 /// Black: #0 (foreground code `30`, background code `40`).
129 Black,
130
131 /// Red: #1 (foreground code `31`, background code `41`).
132 Red,
133
134 /// Green: #2 (foreground code `32`, background code `42`).
135 Green,
136
137 /// Yellow: #3 (foreground code `33`, background code `43`).
138 Yellow,
139
140 /// Blue: #4 (foreground code `34`, background code `44`).
141 Blue,
142
143 /// Magenta: #5 (foreground code `35`, background code `45`).
144 Magenta,
145
146 /// Cyan: #6 (foreground code `36`, background code `46`).
147 Cyan,
148
149 /// White: #7 (foreground code `37`, background code `47`).
150 White,
151
152 /// Bright black: #0 (foreground code `90`, background code `100`).
153 BrightBlack,
154
155 /// Bright red: #1 (foreground code `91`, background code `101`).
156 BrightRed,
157
158 /// Bright green: #2 (foreground code `92`, background code `102`).
159 BrightGreen,
160
161 /// Bright yellow: #3 (foreground code `93`, background code `103`).
162 BrightYellow,
163
164 /// Bright blue: #4 (foreground code `94`, background code `104`).
165 BrightBlue,
166
167 /// Bright magenta: #5 (foreground code `95`, background code `105`).
168 BrightMagenta,
169
170 /// Bright cyan: #6 (foreground code `96`, background code `106`).
171 BrightCyan,
172
173 /// Bright white: #7 (foreground code `97`, background code `107`).
174 BrightWhite,
175}
176
177impl AnsiColor {
178 /// Create a [`Style`][crate::Style] with this as the foreground
179 #[inline]
180 pub fn on(self, background: impl Into<Color>) -> crate::Style {
181 crate::Style::new()
182 .fg_color(Some(self.into()))
183 .bg_color(Some(background.into()))
184 }
185
186 /// Create a [`Style`][crate::Style] with this as the foreground
187 #[inline]
188 pub const fn on_default(self) -> crate::Style {
189 crate::Style::new().fg_color(Some(Color::Ansi(self)))
190 }
191
192 /// Render the ANSI code for a foreground color
193 #[inline]
194 pub fn render_fg(self) -> impl core::fmt::Display + Copy + Clone {
195 self.as_fg_str()
196 }
197
198 #[inline]
199 fn as_fg_str(&self) -> &'static str {
200 match self {
201 Self::Black => escape!("3", "0"),
202 Self::Red => escape!("3", "1"),
203 Self::Green => escape!("3", "2"),
204 Self::Yellow => escape!("3", "3"),
205 Self::Blue => escape!("3", "4"),
206 Self::Magenta => escape!("3", "5"),
207 Self::Cyan => escape!("3", "6"),
208 Self::White => escape!("3", "7"),
209 Self::BrightBlack => escape!("9", "0"),
210 Self::BrightRed => escape!("9", "1"),
211 Self::BrightGreen => escape!("9", "2"),
212 Self::BrightYellow => escape!("9", "3"),
213 Self::BrightBlue => escape!("9", "4"),
214 Self::BrightMagenta => escape!("9", "5"),
215 Self::BrightCyan => escape!("9", "6"),
216 Self::BrightWhite => escape!("9", "7"),
217 }
218 }
219
220 /// Render the ANSI code for a background color
221 #[inline]
222 pub fn render_bg(self) -> impl core::fmt::Display + Copy + Clone {
223 self.as_bg_str()
224 }
225
226 #[inline]
227 fn as_bg_str(&self) -> &'static str {
228 match self {
229 Self::Black => escape!("4", "0"),
230 Self::Red => escape!("4", "1"),
231 Self::Green => escape!("4", "2"),
232 Self::Yellow => escape!("4", "3"),
233 Self::Blue => escape!("4", "4"),
234 Self::Magenta => escape!("4", "5"),
235 Self::Cyan => escape!("4", "6"),
236 Self::White => escape!("4", "7"),
237 Self::BrightBlack => escape!("10", "0"),
238 Self::BrightRed => escape!("10", "1"),
239 Self::BrightGreen => escape!("10", "2"),
240 Self::BrightYellow => escape!("10", "3"),
241 Self::BrightBlue => escape!("10", "4"),
242 Self::BrightMagenta => escape!("10", "5"),
243 Self::BrightCyan => escape!("10", "6"),
244 Self::BrightWhite => escape!("10", "7"),
245 }
246 }
247
248 #[inline]
249 fn as_underline_buffer(&self) -> DisplayBuffer {
250 // No per-color codes; must delegate to `Ansi256Color`
251 Ansi256Color::from(*self).as_underline_buffer()
252 }
253
254 /// Change the color to/from bright
255 #[must_use]
256 #[inline]
257 pub fn bright(self, yes: bool) -> Self {
258 if yes {
259 match self {
260 Self::Black => Self::BrightBlack,
261 Self::Red => Self::BrightRed,
262 Self::Green => Self::BrightGreen,
263 Self::Yellow => Self::BrightYellow,
264 Self::Blue => Self::BrightBlue,
265 Self::Magenta => Self::BrightMagenta,
266 Self::Cyan => Self::BrightCyan,
267 Self::White => Self::BrightWhite,
268 Self::BrightBlack => self,
269 Self::BrightRed => self,
270 Self::BrightGreen => self,
271 Self::BrightYellow => self,
272 Self::BrightBlue => self,
273 Self::BrightMagenta => self,
274 Self::BrightCyan => self,
275 Self::BrightWhite => self,
276 }
277 } else {
278 match self {
279 Self::Black => self,
280 Self::Red => self,
281 Self::Green => self,
282 Self::Yellow => self,
283 Self::Blue => self,
284 Self::Magenta => self,
285 Self::Cyan => self,
286 Self::White => self,
287 Self::BrightBlack => Self::Black,
288 Self::BrightRed => Self::Red,
289 Self::BrightGreen => Self::Green,
290 Self::BrightYellow => Self::Yellow,
291 Self::BrightBlue => Self::Blue,
292 Self::BrightMagenta => Self::Magenta,
293 Self::BrightCyan => Self::Cyan,
294 Self::BrightWhite => Self::White,
295 }
296 }
297 }
298
299 /// Report whether the color is bright
300 #[inline]
301 pub fn is_bright(self) -> bool {
302 match self {
303 Self::Black => false,
304 Self::Red => false,
305 Self::Green => false,
306 Self::Yellow => false,
307 Self::Blue => false,
308 Self::Magenta => false,
309 Self::Cyan => false,
310 Self::White => false,
311 Self::BrightBlack => true,
312 Self::BrightRed => true,
313 Self::BrightGreen => true,
314 Self::BrightYellow => true,
315 Self::BrightBlue => true,
316 Self::BrightMagenta => true,
317 Self::BrightCyan => true,
318 Self::BrightWhite => true,
319 }
320 }
321}
322
323/// 256 (8-bit) color support
324///
325/// - `0..16` are [`AnsiColor`] palette codes
326/// - `0..232` map to [`RgbColor`] color values
327/// - `232..` map to [`RgbColor`] gray-scale values
328#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
329#[repr(transparent)]
330pub struct Ansi256Color(pub u8);
331
332impl Ansi256Color {
333 /// Create a [`Style`][crate::Style] with this as the foreground
334 #[inline]
335 pub fn on(self, background: impl Into<Color>) -> crate::Style {
336 crate::Style::new()
337 .fg_color(Some(self.into()))
338 .bg_color(Some(background.into()))
339 }
340
341 /// Create a [`Style`][crate::Style] with this as the foreground
342 #[inline]
343 pub const fn on_default(self) -> crate::Style {
344 crate::Style::new().fg_color(Some(Color::Ansi256(self)))
345 }
346
347 #[inline]
348 pub const fn index(self) -> u8 {
349 self.0
350 }
351
352 #[inline]
353 pub const fn into_ansi(self) -> Option<AnsiColor> {
354 match self.index() {
355 0 => Some(AnsiColor::Black),
356 1 => Some(AnsiColor::Red),
357 2 => Some(AnsiColor::Green),
358 3 => Some(AnsiColor::Yellow),
359 4 => Some(AnsiColor::Blue),
360 5 => Some(AnsiColor::Magenta),
361 6 => Some(AnsiColor::Cyan),
362 7 => Some(AnsiColor::White),
363 8 => Some(AnsiColor::BrightBlack),
364 9 => Some(AnsiColor::BrightRed),
365 10 => Some(AnsiColor::BrightGreen),
366 11 => Some(AnsiColor::BrightYellow),
367 12 => Some(AnsiColor::BrightBlue),
368 13 => Some(AnsiColor::BrightMagenta),
369 14 => Some(AnsiColor::BrightCyan),
370 15 => Some(AnsiColor::BrightWhite),
371 _ => None,
372 }
373 }
374
375 #[inline]
376 pub const fn from_ansi(color: AnsiColor) -> Self {
377 match color {
378 AnsiColor::Black => Self(0),
379 AnsiColor::Red => Self(1),
380 AnsiColor::Green => Self(2),
381 AnsiColor::Yellow => Self(3),
382 AnsiColor::Blue => Self(4),
383 AnsiColor::Magenta => Self(5),
384 AnsiColor::Cyan => Self(6),
385 AnsiColor::White => Self(7),
386 AnsiColor::BrightBlack => Self(8),
387 AnsiColor::BrightRed => Self(9),
388 AnsiColor::BrightGreen => Self(10),
389 AnsiColor::BrightYellow => Self(11),
390 AnsiColor::BrightBlue => Self(12),
391 AnsiColor::BrightMagenta => Self(13),
392 AnsiColor::BrightCyan => Self(14),
393 AnsiColor::BrightWhite => Self(15),
394 }
395 }
396
397 /// Render the ANSI code for a foreground color
398 #[inline]
399 pub fn render_fg(self) -> impl core::fmt::Display + Copy + Clone {
400 self.as_fg_buffer()
401 }
402
403 #[inline]
404 fn as_fg_buffer(&self) -> DisplayBuffer {
405 DisplayBuffer::default()
406 .write_str("\x1B[38;5;")
407 .write_code(self.index())
408 .write_str("m")
409 }
410
411 /// Render the ANSI code for a background color
412 #[inline]
413 pub fn render_bg(self) -> impl core::fmt::Display + Copy + Clone {
414 self.as_bg_buffer()
415 }
416
417 #[inline]
418 fn as_bg_buffer(&self) -> DisplayBuffer {
419 DisplayBuffer::default()
420 .write_str("\x1B[48;5;")
421 .write_code(self.index())
422 .write_str("m")
423 }
424
425 #[inline]
426 fn as_underline_buffer(&self) -> DisplayBuffer {
427 DisplayBuffer::default()
428 .write_str("\x1B[58;5;")
429 .write_code(self.index())
430 .write_str("m")
431 }
432}
433
434impl From<u8> for Ansi256Color {
435 #[inline]
436 fn from(inner: u8) -> Self {
437 Self(inner)
438 }
439}
440
441impl From<AnsiColor> for Ansi256Color {
442 #[inline]
443 fn from(inner: AnsiColor) -> Self {
444 Self::from_ansi(color:inner)
445 }
446}
447
448/// 24-bit ANSI RGB color codes
449#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
450pub struct RgbColor(pub u8, pub u8, pub u8);
451
452impl RgbColor {
453 /// Create a [`Style`][crate::Style] with this as the foreground
454 #[inline]
455 pub fn on(self, background: impl Into<Color>) -> crate::Style {
456 crate::Style::new()
457 .fg_color(Some(self.into()))
458 .bg_color(Some(background.into()))
459 }
460
461 /// Create a [`Style`][crate::Style] with this as the foreground
462 #[inline]
463 pub const fn on_default(self) -> crate::Style {
464 crate::Style::new().fg_color(Some(Color::Rgb(self)))
465 }
466
467 #[inline]
468 pub const fn r(self) -> u8 {
469 self.0
470 }
471
472 #[inline]
473 pub const fn g(self) -> u8 {
474 self.1
475 }
476
477 #[inline]
478 pub const fn b(self) -> u8 {
479 self.2
480 }
481
482 /// Render the ANSI code for a foreground color
483 #[inline]
484 pub fn render_fg(self) -> impl core::fmt::Display + Copy + Clone {
485 self.as_fg_buffer()
486 }
487
488 #[inline]
489 fn as_fg_buffer(&self) -> DisplayBuffer {
490 DisplayBuffer::default()
491 .write_str("\x1B[38;2;")
492 .write_code(self.r())
493 .write_str(";")
494 .write_code(self.g())
495 .write_str(";")
496 .write_code(self.b())
497 .write_str("m")
498 }
499
500 /// Render the ANSI code for a background color
501 #[inline]
502 pub fn render_bg(self) -> impl core::fmt::Display + Copy + Clone {
503 self.as_bg_buffer()
504 }
505
506 #[inline]
507 fn as_bg_buffer(&self) -> DisplayBuffer {
508 DisplayBuffer::default()
509 .write_str("\x1B[48;2;")
510 .write_code(self.r())
511 .write_str(";")
512 .write_code(self.g())
513 .write_str(";")
514 .write_code(self.b())
515 .write_str("m")
516 }
517
518 #[inline]
519 fn as_underline_buffer(&self) -> DisplayBuffer {
520 DisplayBuffer::default()
521 .write_str("\x1B[58;2;")
522 .write_code(self.r())
523 .write_str(";")
524 .write_code(self.g())
525 .write_str(";")
526 .write_code(self.b())
527 .write_str("m")
528 }
529}
530
531impl From<(u8, u8, u8)> for RgbColor {
532 #[inline]
533 fn from(inner: (u8, u8, u8)) -> Self {
534 let (r: u8, g: u8, b: u8) = inner;
535 Self(r, g, b)
536 }
537}
538
539#[derive(Copy, Clone, Default, Debug)]
540struct DisplayBuffer {
541 buffer: [u8; 19],
542 len: usize,
543}
544
545impl DisplayBuffer {
546 #[must_use]
547 #[inline(never)]
548 fn write_str(mut self, part: &'static str) -> Self {
549 for (i, b) in part.as_bytes().iter().enumerate() {
550 self.buffer[self.len + i] = *b;
551 }
552 self.len += part.len();
553 self
554 }
555
556 #[must_use]
557 #[inline(never)]
558 fn write_code(mut self, code: u8) -> Self {
559 let c1: u8 = (code / 100) % 10;
560 let c2: u8 = (code / 10) % 10;
561 let c3: u8 = code % 10;
562
563 let mut printed = true;
564 if c1 != 0 {
565 printed = true;
566 self.buffer[self.len] = b'0' + c1;
567 self.len += 1;
568 }
569 if c2 != 0 || printed {
570 self.buffer[self.len] = b'0' + c2;
571 self.len += 1;
572 }
573 // If we received a zero value we must still print a value.
574 self.buffer[self.len] = b'0' + c3;
575 self.len += 1;
576
577 self
578 }
579
580 #[inline]
581 fn as_str(&self) -> &str {
582 // SAFETY: Only `&str` can be written to the buffer
583 unsafe { core::str::from_utf8_unchecked(&self.buffer[0..self.len]) }
584 }
585
586 #[inline]
587 #[cfg(feature = "std")]
588 fn write_to(self, write: &mut dyn std::io::Write) -> std::io::Result<()> {
589 write.write_all(self.as_str().as_bytes())
590 }
591}
592
593impl core::fmt::Display for DisplayBuffer {
594 #[inline]
595 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
596 self.as_str().fmt(f)
597 }
598}
599
600#[cfg(test)]
601#[cfg(feature = "std")]
602mod test {
603 use super::*;
604
605 #[test]
606 fn max_display_buffer() {
607 let c = RgbColor(255, 255, 255);
608 let actual = c.render_fg().to_string();
609 assert_eq!(actual, "\u{1b}[38;2;255;255;255m");
610 }
611}
612