1/// Any ANSI color code scheme
2#[allow(clippy::exhaustive_enums)]
3#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
4pub enum Color {
5 /// Available 4-bit ANSI color palette codes
6 ///
7 /// The user's terminal defines the meaning of the each palette code.
8 Ansi(AnsiColor),
9 /// 256 (8-bit) color support
10 ///
11 /// - `0..16` are [`AnsiColor`] palette codes
12 /// - `0..232` map to [`RgbColor`] color values
13 /// - `232..` map to [`RgbColor`] gray-scale values
14 Ansi256(Ansi256Color),
15 /// 24-bit ANSI RGB color codes
16 Rgb(RgbColor),
17}
18
19impl Color {
20 /// Create a [`Style`][crate::Style] with this as the foreground
21 #[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 /// Create a [`Style`][crate::Style] with this as the foreground
29 #[inline]
30 pub const fn on_default(self) -> crate::Style {
31 crate::Style::new().fg_color(Some(self))
32 }
33
34 /// Render the ANSI code for a foreground color
35 #[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 /// Render the ANSI code for a background color
56 #[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/// Available 4-bit ANSI color palette codes
133///
134/// The user's terminal defines the meaning of the each palette code.
135#[allow(clippy::exhaustive_enums)]
136#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
137#[repr(u8)]
138pub enum AnsiColor {
139 /// Black: #0 (foreground code `30`, background code `40`).
140 Black,
141
142 /// Red: #1 (foreground code `31`, background code `41`).
143 Red,
144
145 /// Green: #2 (foreground code `32`, background code `42`).
146 Green,
147
148 /// Yellow: #3 (foreground code `33`, background code `43`).
149 Yellow,
150
151 /// Blue: #4 (foreground code `34`, background code `44`).
152 Blue,
153
154 /// Magenta: #5 (foreground code `35`, background code `45`).
155 Magenta,
156
157 /// Cyan: #6 (foreground code `36`, background code `46`).
158 Cyan,
159
160 /// White: #7 (foreground code `37`, background code `47`).
161 White,
162
163 /// Bright black: #0 (foreground code `90`, background code `100`).
164 BrightBlack,
165
166 /// Bright red: #1 (foreground code `91`, background code `101`).
167 BrightRed,
168
169 /// Bright green: #2 (foreground code `92`, background code `102`).
170 BrightGreen,
171
172 /// Bright yellow: #3 (foreground code `93`, background code `103`).
173 BrightYellow,
174
175 /// Bright blue: #4 (foreground code `94`, background code `104`).
176 BrightBlue,
177
178 /// Bright magenta: #5 (foreground code `95`, background code `105`).
179 BrightMagenta,
180
181 /// Bright cyan: #6 (foreground code `96`, background code `106`).
182 BrightCyan,
183
184 /// Bright white: #7 (foreground code `97`, background code `107`).
185 BrightWhite,
186}
187
188impl AnsiColor {
189 /// Create a [`Style`][crate::Style] with this as the foreground
190 #[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 /// Create a [`Style`][crate::Style] with this as the foreground
198 #[inline]
199 pub const fn on_default(self) -> crate::Style {
200 crate::Style::new().fg_color(Some(Color::Ansi(self)))
201 }
202
203 /// Render the ANSI code for a foreground color
204 #[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 /// Render the ANSI code for a background color
237 #[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 // No per-color codes; must delegate to `Ansi256Color`
272 Ansi256Color::from(*self).as_underline_buffer()
273 }
274
275 /// Change the color to/from bright
276 #[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 /// Report whether the color is bright
321 #[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/// 256 (8-bit) color support
345///
346/// - `0..16` are [`AnsiColor`] palette codes
347/// - `0..232` map to [`RgbColor`] color values
348/// - `232..` map to [`RgbColor`] gray-scale values
349#[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 /// Create a [`Style`][crate::Style] with this as the foreground
356 #[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 /// Create a [`Style`][crate::Style] with this as the foreground
364 #[inline]
365 pub const fn on_default(self) -> crate::Style {
366 crate::Style::new().fg_color(Some(Color::Ansi256(self)))
367 }
368
369 /// Get the raw value
370 #[inline]
371 pub const fn index(self) -> u8 {
372 self.0
373 }
374
375 /// Convert to [`AnsiColor`] when there is a 1:1 mapping
376 #[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 /// Losslessly convert from [`AnsiColor`]
400 #[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 /// Render the ANSI code for a foreground color
423 #[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 /// Render the ANSI code for a background color
437 #[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(color:inner)
470 }
471}
472
473/// 24-bit ANSI RGB color codes
474#[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 /// Create a [`Style`][crate::Style] with this as the foreground
480 #[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 /// Create a [`Style`][crate::Style] with this as the foreground
488 #[inline]
489 pub const fn on_default(self) -> crate::Style {
490 crate::Style::new().fg_color(Some(Color::Rgb(self)))
491 }
492
493 /// Red
494 #[inline]
495 pub const fn r(self) -> u8 {
496 self.0
497 }
498
499 /// Green
500 #[inline]
501 pub const fn g(self) -> u8 {
502 self.1
503 }
504
505 /// Blue
506 #[inline]
507 pub const fn b(self) -> u8 {
508 self.2
509 }
510
511 /// Render the ANSI code for a foreground color
512 #[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 /// Render the ANSI code for a background color
530 #[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: u8, g: u8, b: u8) = 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 // If we received a zero value we must still print a value.
605 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 // SAFETY: Only `&str` can be written to the buffer
614 #[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}
686

Provided by KDAB

Privacy Policy
Learn Rust with the experts
Find out more