1#[allow(clippy::exhaustive_enums)]
3#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
4pub enum Color {
5    Ansi(AnsiColor),
9    Ansi256(Ansi256Color),
15    Rgb(RgbColor),
17}
18
19impl Color {
20    #[inline]
22    pub fn on(self, background: impl Into<Color>) -> crate::Style {
23        crate::Style::new()
24            .fg_color(Some(self))
25            .bg_color(Some(background.into()))
26    }
27
28    #[inline]
30    pub const fn on_default(self) -> crate::Style {
31        crate::Style::new().fg_color(Some(self))
32    }
33
34    #[inline]
36    pub fn render_fg(self) -> impl core::fmt::Display + Copy {
37        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    }
43
44    #[inline]
45    #[cfg(feature = "std")]
46    pub(crate) fn write_fg_to(self, write: &mut dyn std::io::Write) -> std::io::Result<()> {
47        let buffer = match self {
48            Self::Ansi(color) => color.as_fg_buffer(),
49            Self::Ansi256(color) => color.as_fg_buffer(),
50            Self::Rgb(color) => color.as_fg_buffer(),
51        };
52        buffer.write_to(write)
53    }
54
55    #[inline]
57    pub fn render_bg(self) -> impl core::fmt::Display + Copy {
58        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    }
64
65    #[inline]
66    #[cfg(feature = "std")]
67    pub(crate) fn write_bg_to(self, write: &mut dyn std::io::Write) -> std::io::Result<()> {
68        let buffer = match self {
69            Self::Ansi(color) => color.as_bg_buffer(),
70            Self::Ansi256(color) => color.as_bg_buffer(),
71            Self::Rgb(color) => color.as_bg_buffer(),
72        };
73        buffer.write_to(write)
74    }
75
76    #[inline]
77    pub(crate) fn render_underline(self) -> impl core::fmt::Display + Copy {
78        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    }
84
85    #[inline]
86    #[cfg(feature = "std")]
87    pub(crate) fn write_underline_to(self, write: &mut dyn std::io::Write) -> std::io::Result<()> {
88        let buffer = match self {
89            Self::Ansi(color) => color.as_underline_buffer(),
90            Self::Ansi256(color) => color.as_underline_buffer(),
91            Self::Rgb(color) => color.as_underline_buffer(),
92        };
93        buffer.write_to(write)
94    }
95}
96
97impl From<AnsiColor> for Color {
98    #[inline]
99    fn from(inner: AnsiColor) -> Self {
100        Self::Ansi(inner)
101    }
102}
103
104impl From<Ansi256Color> for Color {
105    #[inline]
106    fn from(inner: Ansi256Color) -> Self {
107        Self::Ansi256(inner)
108    }
109}
110
111impl From<RgbColor> for Color {
112    #[inline]
113    fn from(inner: RgbColor) -> Self {
114        Self::Rgb(inner)
115    }
116}
117
118impl From<u8> for Color {
119    #[inline]
120    fn from(inner: u8) -> Self {
121        Self::Ansi256(inner.into())
122    }
123}
124
125impl From<(u8, u8, u8)> for Color {
126    #[inline]
127    fn from(inner: (u8, u8, u8)) -> Self {
128        Self::Rgb(inner.into())
129    }
130}
131
132#[allow(clippy::exhaustive_enums)]
136#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
137#[repr(u8)]
138pub enum AnsiColor {
139    Black,
141
142    Red,
144
145    Green,
147
148    Yellow,
150
151    Blue,
153
154    Magenta,
156
157    Cyan,
159
160    White,
162
163    BrightBlack,
165
166    BrightRed,
168
169    BrightGreen,
171
172    BrightYellow,
174
175    BrightBlue,
177
178    BrightMagenta,
180
181    BrightCyan,
183
184    BrightWhite,
186}
187
188impl AnsiColor {
189    #[inline]
191    pub fn on(self, background: impl Into<Color>) -> crate::Style {
192        crate::Style::new()
193            .fg_color(Some(self.into()))
194            .bg_color(Some(background.into()))
195    }
196
197    #[inline]
199    pub const fn on_default(self) -> crate::Style {
200        crate::Style::new().fg_color(Some(Color::Ansi(self)))
201    }
202
203    #[inline]
205    pub fn render_fg(self) -> impl core::fmt::Display + Copy {
206        NullFormatter(self.as_fg_str())
207    }
208
209    #[inline]
210    fn as_fg_str(&self) -> &'static str {
211        match self {
212            Self::Black => escape!("3", "0"),
213            Self::Red => escape!("3", "1"),
214            Self::Green => escape!("3", "2"),
215            Self::Yellow => escape!("3", "3"),
216            Self::Blue => escape!("3", "4"),
217            Self::Magenta => escape!("3", "5"),
218            Self::Cyan => escape!("3", "6"),
219            Self::White => escape!("3", "7"),
220            Self::BrightBlack => escape!("9", "0"),
221            Self::BrightRed => escape!("9", "1"),
222            Self::BrightGreen => escape!("9", "2"),
223            Self::BrightYellow => escape!("9", "3"),
224            Self::BrightBlue => escape!("9", "4"),
225            Self::BrightMagenta => escape!("9", "5"),
226            Self::BrightCyan => escape!("9", "6"),
227            Self::BrightWhite => escape!("9", "7"),
228        }
229    }
230
231    #[inline]
232    fn as_fg_buffer(&self) -> DisplayBuffer {
233        DisplayBuffer::default().write_str(self.as_fg_str())
234    }
235
236    #[inline]
238    pub fn render_bg(self) -> impl core::fmt::Display + Copy {
239        NullFormatter(self.as_bg_str())
240    }
241
242    #[inline]
243    fn as_bg_str(&self) -> &'static str {
244        match self {
245            Self::Black => escape!("4", "0"),
246            Self::Red => escape!("4", "1"),
247            Self::Green => escape!("4", "2"),
248            Self::Yellow => escape!("4", "3"),
249            Self::Blue => escape!("4", "4"),
250            Self::Magenta => escape!("4", "5"),
251            Self::Cyan => escape!("4", "6"),
252            Self::White => escape!("4", "7"),
253            Self::BrightBlack => escape!("10", "0"),
254            Self::BrightRed => escape!("10", "1"),
255            Self::BrightGreen => escape!("10", "2"),
256            Self::BrightYellow => escape!("10", "3"),
257            Self::BrightBlue => escape!("10", "4"),
258            Self::BrightMagenta => escape!("10", "5"),
259            Self::BrightCyan => escape!("10", "6"),
260            Self::BrightWhite => escape!("10", "7"),
261        }
262    }
263
264    #[inline]
265    fn as_bg_buffer(&self) -> DisplayBuffer {
266        DisplayBuffer::default().write_str(self.as_bg_str())
267    }
268
269    #[inline]
270    fn as_underline_buffer(&self) -> DisplayBuffer {
271        Ansi256Color::from(*self).as_underline_buffer()
273    }
274
275    #[must_use]
277    #[inline]
278    pub fn bright(self, yes: bool) -> Self {
279        if yes {
280            match self {
281                Self::Black => Self::BrightBlack,
282                Self::Red => Self::BrightRed,
283                Self::Green => Self::BrightGreen,
284                Self::Yellow => Self::BrightYellow,
285                Self::Blue => Self::BrightBlue,
286                Self::Magenta => Self::BrightMagenta,
287                Self::Cyan => Self::BrightCyan,
288                Self::White => Self::BrightWhite,
289                Self::BrightBlack => self,
290                Self::BrightRed => self,
291                Self::BrightGreen => self,
292                Self::BrightYellow => self,
293                Self::BrightBlue => self,
294                Self::BrightMagenta => self,
295                Self::BrightCyan => self,
296                Self::BrightWhite => self,
297            }
298        } else {
299            match self {
300                Self::Black => self,
301                Self::Red => self,
302                Self::Green => self,
303                Self::Yellow => self,
304                Self::Blue => self,
305                Self::Magenta => self,
306                Self::Cyan => self,
307                Self::White => self,
308                Self::BrightBlack => Self::Black,
309                Self::BrightRed => Self::Red,
310                Self::BrightGreen => Self::Green,
311                Self::BrightYellow => Self::Yellow,
312                Self::BrightBlue => Self::Blue,
313                Self::BrightMagenta => Self::Magenta,
314                Self::BrightCyan => Self::Cyan,
315                Self::BrightWhite => Self::White,
316            }
317        }
318    }
319
320    #[inline]
322    pub fn is_bright(self) -> bool {
323        match self {
324            Self::Black => false,
325            Self::Red => false,
326            Self::Green => false,
327            Self::Yellow => false,
328            Self::Blue => false,
329            Self::Magenta => false,
330            Self::Cyan => false,
331            Self::White => false,
332            Self::BrightBlack => true,
333            Self::BrightRed => true,
334            Self::BrightGreen => true,
335            Self::BrightYellow => true,
336            Self::BrightBlue => true,
337            Self::BrightMagenta => true,
338            Self::BrightCyan => true,
339            Self::BrightWhite => true,
340        }
341    }
342}
343
344#[allow(clippy::exhaustive_structs)]
350#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
351#[repr(transparent)]
352pub struct Ansi256Color(pub u8);
353
354impl Ansi256Color {
355    #[inline]
357    pub fn on(self, background: impl Into<Color>) -> crate::Style {
358        crate::Style::new()
359            .fg_color(Some(self.into()))
360            .bg_color(Some(background.into()))
361    }
362
363    #[inline]
365    pub const fn on_default(self) -> crate::Style {
366        crate::Style::new().fg_color(Some(Color::Ansi256(self)))
367    }
368
369    #[inline]
371    pub const fn index(self) -> u8 {
372        self.0
373    }
374
375    #[inline]
377    pub const fn into_ansi(self) -> Option<AnsiColor> {
378        match self.index() {
379            0 => Some(AnsiColor::Black),
380            1 => Some(AnsiColor::Red),
381            2 => Some(AnsiColor::Green),
382            3 => Some(AnsiColor::Yellow),
383            4 => Some(AnsiColor::Blue),
384            5 => Some(AnsiColor::Magenta),
385            6 => Some(AnsiColor::Cyan),
386            7 => Some(AnsiColor::White),
387            8 => Some(AnsiColor::BrightBlack),
388            9 => Some(AnsiColor::BrightRed),
389            10 => Some(AnsiColor::BrightGreen),
390            11 => Some(AnsiColor::BrightYellow),
391            12 => Some(AnsiColor::BrightBlue),
392            13 => Some(AnsiColor::BrightMagenta),
393            14 => Some(AnsiColor::BrightCyan),
394            15 => Some(AnsiColor::BrightWhite),
395            _ => None,
396        }
397    }
398
399    #[inline]
401    pub const fn from_ansi(color: AnsiColor) -> Self {
402        match color {
403            AnsiColor::Black => Self(0),
404            AnsiColor::Red => Self(1),
405            AnsiColor::Green => Self(2),
406            AnsiColor::Yellow => Self(3),
407            AnsiColor::Blue => Self(4),
408            AnsiColor::Magenta => Self(5),
409            AnsiColor::Cyan => Self(6),
410            AnsiColor::White => Self(7),
411            AnsiColor::BrightBlack => Self(8),
412            AnsiColor::BrightRed => Self(9),
413            AnsiColor::BrightGreen => Self(10),
414            AnsiColor::BrightYellow => Self(11),
415            AnsiColor::BrightBlue => Self(12),
416            AnsiColor::BrightMagenta => Self(13),
417            AnsiColor::BrightCyan => Self(14),
418            AnsiColor::BrightWhite => Self(15),
419        }
420    }
421
422    #[inline]
424    pub fn render_fg(self) -> impl core::fmt::Display + Copy {
425        self.as_fg_buffer()
426    }
427
428    #[inline]
429    fn as_fg_buffer(&self) -> DisplayBuffer {
430        DisplayBuffer::default()
431            .write_str("\x1B[38;5;")
432            .write_code(self.index())
433            .write_str("m")
434    }
435
436    #[inline]
438    pub fn render_bg(self) -> impl core::fmt::Display + Copy {
439        self.as_bg_buffer()
440    }
441
442    #[inline]
443    fn as_bg_buffer(&self) -> DisplayBuffer {
444        DisplayBuffer::default()
445            .write_str("\x1B[48;5;")
446            .write_code(self.index())
447            .write_str("m")
448    }
449
450    #[inline]
451    fn as_underline_buffer(&self) -> DisplayBuffer {
452        DisplayBuffer::default()
453            .write_str("\x1B[58;5;")
454            .write_code(self.index())
455            .write_str("m")
456    }
457}
458
459impl From<u8> for Ansi256Color {
460    #[inline]
461    fn from(inner: u8) -> Self {
462        Self(inner)
463    }
464}
465
466impl From<AnsiColor> for Ansi256Color {
467    #[inline]
468    fn from(inner: AnsiColor) -> Self {
469        Self::from_ansi(inner)
470    }
471}
472
473#[allow(clippy::exhaustive_structs)]
475#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
476pub struct RgbColor(pub u8, pub u8, pub u8);
477
478impl RgbColor {
479    #[inline]
481    pub fn on(self, background: impl Into<Color>) -> crate::Style {
482        crate::Style::new()
483            .fg_color(Some(self.into()))
484            .bg_color(Some(background.into()))
485    }
486
487    #[inline]
489    pub const fn on_default(self) -> crate::Style {
490        crate::Style::new().fg_color(Some(Color::Rgb(self)))
491    }
492
493    #[inline]
495    pub const fn r(self) -> u8 {
496        self.0
497    }
498
499    #[inline]
501    pub const fn g(self) -> u8 {
502        self.1
503    }
504
505    #[inline]
507    pub const fn b(self) -> u8 {
508        self.2
509    }
510
511    #[inline]
513    pub fn render_fg(self) -> impl core::fmt::Display + Copy {
514        self.as_fg_buffer()
515    }
516
517    #[inline]
518    fn as_fg_buffer(&self) -> DisplayBuffer {
519        DisplayBuffer::default()
520            .write_str("\x1B[38;2;")
521            .write_code(self.r())
522            .write_str(";")
523            .write_code(self.g())
524            .write_str(";")
525            .write_code(self.b())
526            .write_str("m")
527    }
528
529    #[inline]
531    pub fn render_bg(self) -> impl core::fmt::Display + Copy {
532        self.as_bg_buffer()
533    }
534
535    #[inline]
536    fn as_bg_buffer(&self) -> DisplayBuffer {
537        DisplayBuffer::default()
538            .write_str("\x1B[48;2;")
539            .write_code(self.r())
540            .write_str(";")
541            .write_code(self.g())
542            .write_str(";")
543            .write_code(self.b())
544            .write_str("m")
545    }
546
547    #[inline]
548    fn as_underline_buffer(&self) -> DisplayBuffer {
549        DisplayBuffer::default()
550            .write_str("\x1B[58;2;")
551            .write_code(self.r())
552            .write_str(";")
553            .write_code(self.g())
554            .write_str(";")
555            .write_code(self.b())
556            .write_str("m")
557    }
558}
559
560impl From<(u8, u8, u8)> for RgbColor {
561    #[inline]
562    fn from(inner: (u8, u8, u8)) -> Self {
563        let (r, g, b) = inner;
564        Self(r, g, b)
565    }
566}
567
568const DISPLAY_BUFFER_CAPACITY: usize = 19;
569
570#[derive(Copy, Clone, Default, Debug)]
571struct DisplayBuffer {
572    buffer: [u8; DISPLAY_BUFFER_CAPACITY],
573    len: usize,
574}
575
576impl DisplayBuffer {
577    #[must_use]
578    #[inline(never)]
579    fn write_str(mut self, part: &'static str) -> Self {
580        for (i, b) in part.as_bytes().iter().enumerate() {
581            self.buffer[self.len + i] = *b;
582        }
583        self.len += part.len();
584        self
585    }
586
587    #[must_use]
588    #[inline(never)]
589    fn write_code(mut self, code: u8) -> Self {
590        let c1: u8 = (code / 100) % 10;
591        let c2: u8 = (code / 10) % 10;
592        let c3: u8 = code % 10;
593
594        let mut printed = true;
595        if c1 != 0 {
596            printed = true;
597            self.buffer[self.len] = b'0' + c1;
598            self.len += 1;
599        }
600        if c2 != 0 || printed {
601            self.buffer[self.len] = b'0' + c2;
602            self.len += 1;
603        }
604        self.buffer[self.len] = b'0' + c3;
606        self.len += 1;
607
608        self
609    }
610
611    #[inline]
612    fn as_str(&self) -> &str {
613        #[allow(unsafe_code)]
615        unsafe {
616            core::str::from_utf8_unchecked(&self.buffer[0..self.len])
617        }
618    }
619
620    #[inline]
621    #[cfg(feature = "std")]
622    fn write_to(self, write: &mut dyn std::io::Write) -> std::io::Result<()> {
623        write.write_all(self.as_str().as_bytes())
624    }
625}
626
627impl core::fmt::Display for DisplayBuffer {
628    #[inline]
629    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
630        f.write_str(self.as_str())
631    }
632}
633
634#[derive(Copy, Clone, Default, Debug)]
635struct NullFormatter(&'static str);
636
637impl core::fmt::Display for NullFormatter {
638    #[inline]
639    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
640        f.write_str(self.0)
641    }
642}
643
644#[cfg(test)]
645#[cfg(feature = "std")]
646mod test {
647    use super::*;
648
649    #[test]
650    fn max_display_buffer() {
651        let c = RgbColor(255, 255, 255);
652        let actual = c.render_fg().to_string();
653        assert_eq!(actual, "\u{1b}[38;2;255;255;255m");
654        assert_eq!(actual.len(), DISPLAY_BUFFER_CAPACITY);
655    }
656
657    #[test]
658    fn print_size_of() {
659        use core::mem::size_of;
660        dbg!(size_of::<Color>());
661        dbg!(size_of::<AnsiColor>());
662        dbg!(size_of::<Ansi256Color>());
663        dbg!(size_of::<RgbColor>());
664        dbg!(size_of::<DisplayBuffer>());
665    }
666
667    #[test]
668    fn no_align() {
669        #[track_caller]
670        fn assert_no_align(d: impl core::fmt::Display) {
671            let expected = format!("{d}");
672            let actual = format!("{d:<10}");
673            assert_eq!(expected, actual);
674        }
675
676        assert_no_align(AnsiColor::White.render_fg());
677        assert_no_align(AnsiColor::White.render_bg());
678        assert_no_align(Ansi256Color(0).render_fg());
679        assert_no_align(Ansi256Color(0).render_bg());
680        assert_no_align(RgbColor(0, 0, 0).render_fg());
681        assert_no_align(RgbColor(0, 0, 0).render_bg());
682        assert_no_align(Color::Ansi(AnsiColor::White).render_fg());
683        assert_no_align(Color::Ansi(AnsiColor::White).render_bg());
684    }
685}