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 std::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}