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) => color.as_fg_buffer(),
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) => color.as_fg_buffer(),
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) => color.as_bg_buffer(),
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) => color.as_bg_buffer(),
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 NullFormatter(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 #[inline]
221 fn as_fg_buffer(&self) -> DisplayBuffer {
222 DisplayBuffer::default().write_str(self.as_fg_str())
223 }
224
225 /// Render the ANSI code for a background color
226 #[inline]
227 pub fn render_bg(self) -> impl core::fmt::Display + Copy + Clone {
228 NullFormatter(self.as_bg_str())
229 }
230
231 #[inline]
232 fn as_bg_str(&self) -> &'static str {
233 match self {
234 Self::Black => escape!("4", "0"),
235 Self::Red => escape!("4", "1"),
236 Self::Green => escape!("4", "2"),
237 Self::Yellow => escape!("4", "3"),
238 Self::Blue => escape!("4", "4"),
239 Self::Magenta => escape!("4", "5"),
240 Self::Cyan => escape!("4", "6"),
241 Self::White => escape!("4", "7"),
242 Self::BrightBlack => escape!("10", "0"),
243 Self::BrightRed => escape!("10", "1"),
244 Self::BrightGreen => escape!("10", "2"),
245 Self::BrightYellow => escape!("10", "3"),
246 Self::BrightBlue => escape!("10", "4"),
247 Self::BrightMagenta => escape!("10", "5"),
248 Self::BrightCyan => escape!("10", "6"),
249 Self::BrightWhite => escape!("10", "7"),
250 }
251 }
252
253 #[inline]
254 fn as_bg_buffer(&self) -> DisplayBuffer {
255 DisplayBuffer::default().write_str(self.as_bg_str())
256 }
257
258 #[inline]
259 fn as_underline_buffer(&self) -> DisplayBuffer {
260 // No per-color codes; must delegate to `Ansi256Color`
261 Ansi256Color::from(*self).as_underline_buffer()
262 }
263
264 /// Change the color to/from bright
265 #[must_use]
266 #[inline]
267 pub fn bright(self, yes: bool) -> Self {
268 if yes {
269 match self {
270 Self::Black => Self::BrightBlack,
271 Self::Red => Self::BrightRed,
272 Self::Green => Self::BrightGreen,
273 Self::Yellow => Self::BrightYellow,
274 Self::Blue => Self::BrightBlue,
275 Self::Magenta => Self::BrightMagenta,
276 Self::Cyan => Self::BrightCyan,
277 Self::White => Self::BrightWhite,
278 Self::BrightBlack => self,
279 Self::BrightRed => self,
280 Self::BrightGreen => self,
281 Self::BrightYellow => self,
282 Self::BrightBlue => self,
283 Self::BrightMagenta => self,
284 Self::BrightCyan => self,
285 Self::BrightWhite => self,
286 }
287 } else {
288 match self {
289 Self::Black => self,
290 Self::Red => self,
291 Self::Green => self,
292 Self::Yellow => self,
293 Self::Blue => self,
294 Self::Magenta => self,
295 Self::Cyan => self,
296 Self::White => self,
297 Self::BrightBlack => Self::Black,
298 Self::BrightRed => Self::Red,
299 Self::BrightGreen => Self::Green,
300 Self::BrightYellow => Self::Yellow,
301 Self::BrightBlue => Self::Blue,
302 Self::BrightMagenta => Self::Magenta,
303 Self::BrightCyan => Self::Cyan,
304 Self::BrightWhite => Self::White,
305 }
306 }
307 }
308
309 /// Report whether the color is bright
310 #[inline]
311 pub fn is_bright(self) -> bool {
312 match self {
313 Self::Black => false,
314 Self::Red => false,
315 Self::Green => false,
316 Self::Yellow => false,
317 Self::Blue => false,
318 Self::Magenta => false,
319 Self::Cyan => false,
320 Self::White => false,
321 Self::BrightBlack => true,
322 Self::BrightRed => true,
323 Self::BrightGreen => true,
324 Self::BrightYellow => true,
325 Self::BrightBlue => true,
326 Self::BrightMagenta => true,
327 Self::BrightCyan => true,
328 Self::BrightWhite => true,
329 }
330 }
331}
332
333/// 256 (8-bit) color support
334///
335/// - `0..16` are [`AnsiColor`] palette codes
336/// - `0..232` map to [`RgbColor`] color values
337/// - `232..` map to [`RgbColor`] gray-scale values
338#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
339#[repr(transparent)]
340pub struct Ansi256Color(pub u8);
341
342impl Ansi256Color {
343 /// Create a [`Style`][crate::Style] with this as the foreground
344 #[inline]
345 pub fn on(self, background: impl Into<Color>) -> crate::Style {
346 crate::Style::new()
347 .fg_color(Some(self.into()))
348 .bg_color(Some(background.into()))
349 }
350
351 /// Create a [`Style`][crate::Style] with this as the foreground
352 #[inline]
353 pub const fn on_default(self) -> crate::Style {
354 crate::Style::new().fg_color(Some(Color::Ansi256(self)))
355 }
356
357 #[inline]
358 pub const fn index(self) -> u8 {
359 self.0
360 }
361
362 #[inline]
363 pub const fn into_ansi(self) -> Option<AnsiColor> {
364 match self.index() {
365 0 => Some(AnsiColor::Black),
366 1 => Some(AnsiColor::Red),
367 2 => Some(AnsiColor::Green),
368 3 => Some(AnsiColor::Yellow),
369 4 => Some(AnsiColor::Blue),
370 5 => Some(AnsiColor::Magenta),
371 6 => Some(AnsiColor::Cyan),
372 7 => Some(AnsiColor::White),
373 8 => Some(AnsiColor::BrightBlack),
374 9 => Some(AnsiColor::BrightRed),
375 10 => Some(AnsiColor::BrightGreen),
376 11 => Some(AnsiColor::BrightYellow),
377 12 => Some(AnsiColor::BrightBlue),
378 13 => Some(AnsiColor::BrightMagenta),
379 14 => Some(AnsiColor::BrightCyan),
380 15 => Some(AnsiColor::BrightWhite),
381 _ => None,
382 }
383 }
384
385 #[inline]
386 pub const fn from_ansi(color: AnsiColor) -> Self {
387 match color {
388 AnsiColor::Black => Self(0),
389 AnsiColor::Red => Self(1),
390 AnsiColor::Green => Self(2),
391 AnsiColor::Yellow => Self(3),
392 AnsiColor::Blue => Self(4),
393 AnsiColor::Magenta => Self(5),
394 AnsiColor::Cyan => Self(6),
395 AnsiColor::White => Self(7),
396 AnsiColor::BrightBlack => Self(8),
397 AnsiColor::BrightRed => Self(9),
398 AnsiColor::BrightGreen => Self(10),
399 AnsiColor::BrightYellow => Self(11),
400 AnsiColor::BrightBlue => Self(12),
401 AnsiColor::BrightMagenta => Self(13),
402 AnsiColor::BrightCyan => Self(14),
403 AnsiColor::BrightWhite => Self(15),
404 }
405 }
406
407 /// Render the ANSI code for a foreground color
408 #[inline]
409 pub fn render_fg(self) -> impl core::fmt::Display + Copy + Clone {
410 self.as_fg_buffer()
411 }
412
413 #[inline]
414 fn as_fg_buffer(&self) -> DisplayBuffer {
415 DisplayBuffer::default()
416 .write_str("\x1B[38;5;")
417 .write_code(self.index())
418 .write_str("m")
419 }
420
421 /// Render the ANSI code for a background color
422 #[inline]
423 pub fn render_bg(self) -> impl core::fmt::Display + Copy + Clone {
424 self.as_bg_buffer()
425 }
426
427 #[inline]
428 fn as_bg_buffer(&self) -> DisplayBuffer {
429 DisplayBuffer::default()
430 .write_str("\x1B[48;5;")
431 .write_code(self.index())
432 .write_str("m")
433 }
434
435 #[inline]
436 fn as_underline_buffer(&self) -> DisplayBuffer {
437 DisplayBuffer::default()
438 .write_str("\x1B[58;5;")
439 .write_code(self.index())
440 .write_str("m")
441 }
442}
443
444impl From<u8> for Ansi256Color {
445 #[inline]
446 fn from(inner: u8) -> Self {
447 Self(inner)
448 }
449}
450
451impl From<AnsiColor> for Ansi256Color {
452 #[inline]
453 fn from(inner: AnsiColor) -> Self {
454 Self::from_ansi(color:inner)
455 }
456}
457
458/// 24-bit ANSI RGB color codes
459#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
460pub struct RgbColor(pub u8, pub u8, pub u8);
461
462impl RgbColor {
463 /// Create a [`Style`][crate::Style] with this as the foreground
464 #[inline]
465 pub fn on(self, background: impl Into<Color>) -> crate::Style {
466 crate::Style::new()
467 .fg_color(Some(self.into()))
468 .bg_color(Some(background.into()))
469 }
470
471 /// Create a [`Style`][crate::Style] with this as the foreground
472 #[inline]
473 pub const fn on_default(self) -> crate::Style {
474 crate::Style::new().fg_color(Some(Color::Rgb(self)))
475 }
476
477 #[inline]
478 pub const fn r(self) -> u8 {
479 self.0
480 }
481
482 #[inline]
483 pub const fn g(self) -> u8 {
484 self.1
485 }
486
487 #[inline]
488 pub const fn b(self) -> u8 {
489 self.2
490 }
491
492 /// Render the ANSI code for a foreground color
493 #[inline]
494 pub fn render_fg(self) -> impl core::fmt::Display + Copy + Clone {
495 self.as_fg_buffer()
496 }
497
498 #[inline]
499 fn as_fg_buffer(&self) -> DisplayBuffer {
500 DisplayBuffer::default()
501 .write_str("\x1B[38;2;")
502 .write_code(self.r())
503 .write_str(";")
504 .write_code(self.g())
505 .write_str(";")
506 .write_code(self.b())
507 .write_str("m")
508 }
509
510 /// Render the ANSI code for a background color
511 #[inline]
512 pub fn render_bg(self) -> impl core::fmt::Display + Copy + Clone {
513 self.as_bg_buffer()
514 }
515
516 #[inline]
517 fn as_bg_buffer(&self) -> DisplayBuffer {
518 DisplayBuffer::default()
519 .write_str("\x1B[48;2;")
520 .write_code(self.r())
521 .write_str(";")
522 .write_code(self.g())
523 .write_str(";")
524 .write_code(self.b())
525 .write_str("m")
526 }
527
528 #[inline]
529 fn as_underline_buffer(&self) -> DisplayBuffer {
530 DisplayBuffer::default()
531 .write_str("\x1B[58;2;")
532 .write_code(self.r())
533 .write_str(";")
534 .write_code(self.g())
535 .write_str(";")
536 .write_code(self.b())
537 .write_str("m")
538 }
539}
540
541impl From<(u8, u8, u8)> for RgbColor {
542 #[inline]
543 fn from(inner: (u8, u8, u8)) -> Self {
544 let (r: u8, g: u8, b: u8) = inner;
545 Self(r, g, b)
546 }
547}
548
549const DISPLAY_BUFFER_CAPACITY: usize = 19;
550
551#[derive(Copy, Clone, Default, Debug)]
552struct DisplayBuffer {
553 buffer: [u8; DISPLAY_BUFFER_CAPACITY],
554 len: usize,
555}
556
557impl DisplayBuffer {
558 #[must_use]
559 #[inline(never)]
560 fn write_str(mut self, part: &'static str) -> Self {
561 for (i, b) in part.as_bytes().iter().enumerate() {
562 self.buffer[self.len + i] = *b;
563 }
564 self.len += part.len();
565 self
566 }
567
568 #[must_use]
569 #[inline(never)]
570 fn write_code(mut self, code: u8) -> Self {
571 let c1: u8 = (code / 100) % 10;
572 let c2: u8 = (code / 10) % 10;
573 let c3: u8 = code % 10;
574
575 let mut printed = true;
576 if c1 != 0 {
577 printed = true;
578 self.buffer[self.len] = b'0' + c1;
579 self.len += 1;
580 }
581 if c2 != 0 || printed {
582 self.buffer[self.len] = b'0' + c2;
583 self.len += 1;
584 }
585 // If we received a zero value we must still print a value.
586 self.buffer[self.len] = b'0' + c3;
587 self.len += 1;
588
589 self
590 }
591
592 #[inline]
593 fn as_str(&self) -> &str {
594 // SAFETY: Only `&str` can be written to the buffer
595 unsafe { core::str::from_utf8_unchecked(&self.buffer[0..self.len]) }
596 }
597
598 #[inline]
599 #[cfg(feature = "std")]
600 fn write_to(self, write: &mut dyn std::io::Write) -> std::io::Result<()> {
601 write.write_all(self.as_str().as_bytes())
602 }
603}
604
605impl core::fmt::Display for DisplayBuffer {
606 #[inline]
607 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
608 let s: &str = self.as_str();
609 write!(f, "{s}")
610 }
611}
612
613#[derive(Copy, Clone, Default, Debug)]
614struct NullFormatter<D: core::fmt::Display>(D);
615
616impl<D: core::fmt::Display> core::fmt::Display for NullFormatter<D> {
617 #[inline]
618 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
619 let d: &D = &self.0;
620 write!(f, "{d}")
621 }
622}
623
624#[cfg(test)]
625#[cfg(feature = "std")]
626mod test {
627 use super::*;
628
629 #[test]
630 fn max_display_buffer() {
631 let c = RgbColor(255, 255, 255);
632 let actual = c.render_fg().to_string();
633 assert_eq!(actual, "\u{1b}[38;2;255;255;255m");
634 assert_eq!(actual.len(), DISPLAY_BUFFER_CAPACITY);
635 }
636
637 #[test]
638 fn print_size_of() {
639 use std::mem::size_of;
640 dbg!(size_of::<Color>());
641 dbg!(size_of::<AnsiColor>());
642 dbg!(size_of::<Ansi256Color>());
643 dbg!(size_of::<RgbColor>());
644 dbg!(size_of::<DisplayBuffer>());
645 }
646
647 #[test]
648 fn no_align() {
649 #[track_caller]
650 fn assert_no_align(d: impl core::fmt::Display) {
651 let expected = format!("{d}");
652 let actual = format!("{d:<10}");
653 assert_eq!(expected, actual);
654 }
655
656 assert_no_align(AnsiColor::White.render_fg());
657 assert_no_align(AnsiColor::White.render_bg());
658 assert_no_align(Ansi256Color(0).render_fg());
659 assert_no_align(Ansi256Color(0).render_bg());
660 assert_no_align(RgbColor(0, 0, 0).render_fg());
661 assert_no_align(RgbColor(0, 0, 0).render_bg());
662 assert_no_align(Color::Ansi(AnsiColor::White).render_fg());
663 assert_no_align(Color::Ansi(AnsiColor::White).render_bg());
664 }
665}
666