1use std::fmt;
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
40pub struct ColorTriplet {
41 pub red: u8,
42 pub green: u8,
43 pub blue: u8,
44}
45
46impl ColorTriplet {
47 pub const fn new(red: u8, green: u8, blue: u8) -> Self {
49 Self { red, green, blue }
50 }
51}
52
53impl fmt::Display for ColorTriplet {
54 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55 write!(f, "#{:02x}{:02x}{:02x}", self.red, self.green, self.blue)
56 }
57}
58use std::hash::Hash;
59
60#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
66pub enum ColorSystem {
67 Standard = 1,
69 EightBit = 2,
71 TrueColor = 3,
73}
74
75impl fmt::Display for ColorSystem {
76 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
77 match self {
78 Self::Standard => write!(f, "standard"),
79 Self::EightBit => write!(f, "256"),
80 Self::TrueColor => write!(f, "truecolor"),
81 }
82 }
83}
84
85#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
91pub enum ColorType {
92 Default,
94 Standard,
96 EightBit,
98 TrueColor,
100}
101
102#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
116pub struct Color {
117 pub(crate) color_type: ColorType,
118 pub(crate) number: Option<u8>,
121 pub(crate) triplet: Option<(u8, u8, u8)>,
123 pub(crate) name: Option<&'static str>,
125}
126
127impl Color {
128 pub const fn default() -> Self {
132 Self {
133 color_type: ColorType::Default,
134 number: None,
135 triplet: None,
136 name: None,
137 }
138 }
139
140 pub fn from_ansi_name(name: &str) -> Option<Self> {
142 let n = ANSI_NAME_MAP.get(name).copied()?;
143 Some(Self {
144 color_type: if n < 16 {
145 ColorType::Standard
146 } else {
147 ColorType::EightBit
148 },
149 number: Some(n),
150 triplet: None,
151 name: None,
152 })
153 }
154
155 pub fn from_8bit(n: u8) -> Self {
157 Self {
158 color_type: ColorType::EightBit,
159 number: Some(n),
160 triplet: None,
161 name: None,
162 }
163 }
164
165 pub fn from_rgb(r: u8, g: u8, b: u8) -> Self {
167 Self {
168 color_type: ColorType::TrueColor,
169 number: None,
170 triplet: Some((r, g, b)),
171 name: None,
172 }
173 }
174
175 pub fn from_hex(hex: &str) -> Result<Self, ColorParseError> {
177 let hex = hex.trim_start_matches('#');
178 if hex.len() != 6 {
179 return Err(ColorParseError::InvalidHex(hex.to_string()));
180 }
181 let r = u8::from_str_radix(&hex[0..2], 16)
182 .map_err(|_| ColorParseError::InvalidHex(hex.to_string()))?;
183 let g = u8::from_str_radix(&hex[2..4], 16)
184 .map_err(|_| ColorParseError::InvalidHex(hex.to_string()))?;
185 let b = u8::from_str_radix(&hex[4..6], 16)
186 .map_err(|_| ColorParseError::InvalidHex(hex.to_string()))?;
187 Ok(Self::from_rgb(r, g, b))
188 }
189
190 pub fn parse(s: &str) -> Result<Self, ColorParseError> {
192 let lower = s.to_lowercase();
193 if lower == "default" || lower.is_empty() {
194 return Ok(Self::default());
195 }
196 if let Some(c) = Self::from_ansi_name(&lower) {
197 return Ok(c);
198 }
199 if let Some((r, g, b)) = lookup_css_color(&lower) {
201 return Ok(Self::from_rgb(r, g, b));
202 }
203 if lower.starts_with('#') {
204 return Self::from_hex(&lower);
205 }
206 if lower.len() == 6 && lower.chars().all(|c| c.is_ascii_hexdigit()) {
209 return Self::from_hex(&lower);
210 }
211 if let Some(num_str) = lower.strip_prefix("color") {
213 if let Ok(n) = num_str.parse::<u8>() {
214 return Ok(Self::from_8bit(n));
215 }
216 }
217 Err(ColorParseError::UnknownName(lower))
218 }
219
220 pub fn is_default(&self) -> bool {
224 matches!(self.color_type, ColorType::Default)
225 }
226
227 pub fn from_triplet(triplet: &ColorTriplet) -> Self {
239 Self::from_rgb(triplet.red, triplet.green, triplet.blue)
240 }
241
242 pub fn is_system_defined(&self) -> bool {
247 matches!(self.color_type, ColorType::Standard | ColorType::EightBit)
248 }
249
250 pub fn get_ansi_codes(&self, background: bool) -> (Option<String>, Option<String>) {
265 if self.is_default() {
266 return (None, None);
267 }
268 let code = match self.color_type {
269 ColorType::Standard => {
270 let base: u8 = if background { 40 } else { 30 };
271 if let Some(n) = self.number {
272 let bright_offset: u8 = if n >= 8 { 60 } else { 0 };
273 Some((base + n + bright_offset).to_string())
274 } else {
275 None
276 }
277 }
278 ColorType::EightBit => {
279 let prefix = if background { "48;5;" } else { "38;5;" };
280 self.number.map(|n| format!("{prefix}{n}"))
281 }
282 ColorType::TrueColor => {
283 let prefix = if background { "48;2;" } else { "38;2;" };
284 self.triplet.map(|(r, g, b)| format!("{prefix}{r};{g};{b}"))
285 }
286 ColorType::Default => None,
287 };
288 if background {
289 (None, code)
290 } else {
291 (code, None)
292 }
293 }
294
295 pub fn name(&self) -> Option<&'static str> {
301 self.name
302 }
303
304 pub fn number(&self) -> Option<u8> {
309 self.number
310 }
311
312 pub fn triplet(&self) -> Option<(u8, u8, u8)> {
318 self.triplet
319 }
320
321 pub fn get_truecolor(&self, theme: &TerminalTheme) -> (u8, u8, u8) {
324 match self.color_type {
325 ColorType::TrueColor => self.triplet.unwrap(),
326 ColorType::Default => theme.foreground_color,
327 _ => {
328 if let Some(n) = self.number {
329 if let Some(&[r, g, b]) = EIGHT_BIT_PALETTE.get(n as usize) {
330 return (r, g, b);
331 }
332 }
333 theme.foreground_color
334 }
335 }
336 }
337
338 pub fn downgrade(&self, system: ColorSystem) -> Self {
340 match system {
341 ColorSystem::TrueColor => *self,
342 ColorSystem::EightBit => {
343 if matches!(self.color_type, ColorType::TrueColor) {
344 let (r, g, b) = self.triplet.unwrap();
345 let idx = rgb_to_8bit(r, g, b);
346 Self::from_8bit(idx)
347 } else {
348 *self
349 }
350 }
351 ColorSystem::Standard => {
352 if matches!(self.color_type, ColorType::TrueColor) {
353 let (r, g, b) = self.triplet.unwrap();
354 let idx = rgb_to_standard(r, g, b);
355 Self {
356 color_type: ColorType::Standard,
357 number: Some(idx),
358 triplet: None,
359 name: None,
360 }
361 } else if let Some(n) = self.number {
362 if n >= 16 {
363 let idx = n % 16;
364 Self {
365 color_type: ColorType::Standard,
366 number: Some(idx),
367 triplet: None,
368 name: None,
369 }
370 } else {
371 *self
372 }
373 } else {
374 *self
375 }
376 }
377 }
378 }
379}
380
381static CSS_COLORS: &[(&str, (u8, u8, u8))] = &[
390 ("aliceblue", (240, 248, 255)),
391 ("antiquewhite", (250, 235, 215)),
392 ("aqua", (0, 255, 255)),
393 ("aquamarine", (127, 255, 212)),
394 ("azure", (240, 255, 255)),
395 ("beige", (245, 245, 220)),
396 ("bisque", (255, 228, 196)),
397 ("black", (0, 0, 0)),
398 ("blanchedalmond", (255, 235, 205)),
399 ("blue", (0, 0, 255)),
400 ("blueviolet", (138, 43, 226)),
401 ("brown", (165, 42, 42)),
402 ("burlywood", (222, 184, 135)),
403 ("cadetblue", (95, 158, 160)),
404 ("chartreuse", (127, 255, 0)),
405 ("chocolate", (210, 105, 30)),
406 ("coral", (255, 127, 80)),
407 ("cornflowerblue", (100, 149, 237)),
408 ("cornsilk", (255, 248, 220)),
409 ("crimson", (220, 20, 60)),
410 ("cyan", (0, 255, 255)),
411 ("darkblue", (0, 0, 139)),
412 ("darkcyan", (0, 139, 139)),
413 ("darkgoldenrod", (184, 134, 11)),
414 ("darkgray", (169, 169, 169)),
415 ("darkgreen", (0, 100, 0)),
416 ("darkgrey", (169, 169, 169)),
417 ("darkkhaki", (189, 183, 107)),
418 ("darkmagenta", (139, 0, 139)),
419 ("darkolivegreen", (85, 107, 47)),
420 ("darkorange", (255, 140, 0)),
421 ("darkorchid", (153, 50, 204)),
422 ("darkred", (139, 0, 0)),
423 ("darksalmon", (233, 150, 122)),
424 ("darkseagreen", (143, 188, 143)),
425 ("darkslateblue", (72, 61, 139)),
426 ("darkslategray", (47, 79, 79)),
427 ("darkslategrey", (47, 79, 79)),
428 ("darkturquoise", (0, 206, 209)),
429 ("darkviolet", (148, 0, 211)),
430 ("deeppink", (255, 20, 147)),
431 ("deepskyblue", (0, 191, 255)),
432 ("dimgray", (105, 105, 105)),
433 ("dimgrey", (105, 105, 105)),
434 ("dodgerblue", (30, 144, 255)),
435 ("firebrick", (178, 34, 34)),
436 ("floralwhite", (255, 250, 240)),
437 ("forestgreen", (34, 139, 34)),
438 ("fuchsia", (255, 0, 255)),
439 ("gainsboro", (220, 220, 220)),
440 ("ghostwhite", (248, 248, 255)),
441 ("gold", (255, 215, 0)),
442 ("goldenrod", (218, 165, 32)),
443 ("gray", (128, 128, 128)),
444 ("green", (0, 128, 0)),
445 ("greenyellow", (173, 255, 47)),
446 ("grey", (128, 128, 128)),
447 ("honeydew", (240, 255, 240)),
448 ("hotpink", (255, 105, 180)),
449 ("indianred", (205, 92, 92)),
450 ("indigo", (75, 0, 130)),
451 ("ivory", (255, 255, 240)),
452 ("khaki", (240, 230, 140)),
453 ("lavender", (230, 230, 250)),
454 ("lavenderblush", (255, 240, 245)),
455 ("lawngreen", (124, 252, 0)),
456 ("lemonchiffon", (255, 250, 205)),
457 ("lightblue", (173, 216, 230)),
458 ("lightcoral", (240, 128, 128)),
459 ("lightcyan", (224, 255, 255)),
460 ("lightgoldenrodyellow", (250, 250, 210)),
461 ("lightgray", (211, 211, 211)),
462 ("lightgreen", (144, 238, 144)),
463 ("lightgrey", (211, 211, 211)),
464 ("lightpink", (255, 182, 193)),
465 ("lightsalmon", (255, 160, 122)),
466 ("lightseagreen", (32, 178, 170)),
467 ("lightskyblue", (135, 206, 250)),
468 ("lightslategray", (119, 136, 153)),
469 ("lightslategrey", (119, 136, 153)),
470 ("lightsteelblue", (176, 196, 222)),
471 ("lightyellow", (255, 255, 224)),
472 ("lime", (0, 255, 0)),
473 ("limegreen", (50, 205, 50)),
474 ("linen", (250, 240, 230)),
475 ("magenta", (255, 0, 255)),
476 ("maroon", (128, 0, 0)),
477 ("mediumaquamarine", (102, 205, 170)),
478 ("mediumblue", (0, 0, 205)),
479 ("mediumorchid", (186, 85, 211)),
480 ("mediumpurple", (147, 112, 219)),
481 ("mediumseagreen", (60, 179, 113)),
482 ("mediumslateblue", (123, 104, 238)),
483 ("mediumspringgreen", (0, 250, 154)),
484 ("mediumturquoise", (72, 209, 204)),
485 ("mediumvioletred", (199, 21, 133)),
486 ("midnightblue", (25, 25, 112)),
487 ("mintcream", (245, 255, 250)),
488 ("mistyrose", (255, 228, 225)),
489 ("moccasin", (255, 228, 181)),
490 ("navajowhite", (255, 222, 173)),
491 ("navy", (0, 0, 128)),
492 ("oldlace", (253, 245, 230)),
493 ("olive", (128, 128, 0)),
494 ("olivedrab", (107, 142, 35)),
495 ("orange", (255, 165, 0)),
496 ("orangered", (255, 69, 0)),
497 ("orchid", (218, 112, 214)),
498 ("palegoldenrod", (238, 232, 170)),
499 ("palegreen", (152, 251, 152)),
500 ("paleturquoise", (175, 238, 238)),
501 ("palevioletred", (219, 112, 147)),
502 ("papayawhip", (255, 239, 213)),
503 ("peachpuff", (255, 218, 185)),
504 ("peru", (205, 133, 63)),
505 ("pink", (255, 192, 203)),
506 ("plum", (221, 160, 221)),
507 ("powderblue", (176, 224, 230)),
508 ("purple", (128, 0, 128)),
509 ("rebeccapurple", (102, 51, 153)),
510 ("red", (255, 0, 0)),
511 ("rosybrown", (188, 143, 143)),
512 ("royalblue", (65, 105, 225)),
513 ("saddlebrown", (139, 69, 19)),
514 ("salmon", (250, 128, 114)),
515 ("sandybrown", (244, 164, 96)),
516 ("seagreen", (46, 139, 87)),
517 ("seashell", (255, 245, 238)),
518 ("sienna", (160, 82, 45)),
519 ("silver", (192, 192, 192)),
520 ("skyblue", (135, 206, 235)),
521 ("slateblue", (106, 90, 205)),
522 ("slategray", (112, 128, 144)),
523 ("slategrey", (112, 128, 144)),
524 ("snow", (255, 250, 250)),
525 ("springgreen", (0, 255, 127)),
526 ("steelblue", (70, 130, 180)),
527 ("tan", (210, 180, 140)),
528 ("teal", (0, 128, 128)),
529 ("thistle", (216, 191, 216)),
530 ("tomato", (255, 99, 71)),
531 ("turquoise", (64, 224, 208)),
532 ("violet", (238, 130, 238)),
533 ("wheat", (245, 222, 179)),
534 ("white", (255, 255, 255)),
535 ("whitesmoke", (245, 245, 245)),
536 ("yellow", (255, 255, 0)),
537 ("yellowgreen", (154, 205, 50)),
538];
539
540fn lookup_css_color(name: &str) -> Option<(u8, u8, u8)> {
542 CSS_COLORS
543 .binary_search_by_key(&name, |(n, _)| n)
544 .ok()
545 .map(|idx| CSS_COLORS[idx].1)
546}
547
548impl fmt::Display for Color {
549 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
550 match self.color_type {
551 ColorType::Default => write!(f, "default"),
552 ColorType::Standard => {
553 write!(f, "{}", STANDARD_COLOR_NAMES[self.number.unwrap() as usize])
554 }
555 ColorType::EightBit => write!(f, "color({})", self.number.unwrap()),
556 ColorType::TrueColor => {
557 let (r, g, b) = self.triplet.unwrap();
558 write!(f, "#{:02x}{:02x}{:02x}", r, g, b)
559 }
560 }
561 }
562}
563
564#[derive(Debug, Clone)]
570pub enum ColorParseError {
571 UnknownName(String),
573 InvalidHex(String),
575}
576
577impl fmt::Display for ColorParseError {
578 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
579 match self {
580 Self::UnknownName(n) => write!(f, "unknown color name: {n}"),
581 Self::InvalidHex(h) => write!(f, "invalid hex color: {h}"),
582 }
583 }
584}
585
586impl std::error::Error for ColorParseError {}
587
588#[derive(Debug, Clone, Copy)]
594pub struct TerminalTheme {
595 pub foreground_color: (u8, u8, u8),
596 pub background_color: (u8, u8, u8),
597}
598
599impl Default for TerminalTheme {
600 fn default() -> Self {
601 Self {
602 foreground_color: (255, 255, 255),
603 background_color: (0, 0, 0),
604 }
605 }
606}
607
608pub static STANDARD_PALETTE: &[(u8, u8, u8)] = &[
614 (0, 0, 0), (128, 0, 0), (0, 128, 0), (128, 128, 0), (0, 0, 128), (128, 0, 128), (0, 128, 128), (192, 192, 192), (128, 128, 128), (255, 0, 0), (0, 255, 0), (255, 255, 0), (0, 0, 255), (255, 0, 255), (0, 255, 255), (255, 255, 255), ];
631
632pub static STANDARD_COLOR_NAMES: &[&str] = &[
634 "black",
635 "red",
636 "green",
637 "yellow",
638 "blue",
639 "magenta",
640 "cyan",
641 "white",
642 "bright_black",
643 "bright_red",
644 "bright_green",
645 "bright_yellow",
646 "bright_blue",
647 "bright_magenta",
648 "bright_cyan",
649 "bright_white",
650];
651
652pub static EIGHT_BIT_PALETTE: LazyLock<[[u8; 3]; 256]> = LazyLock::new(|| {
654 let mut table = [[0u8; 3]; 256];
655 let std: [[u8; 3]; 16] = [
656 [0, 0, 0],
657 [128, 0, 0],
658 [0, 128, 0],
659 [128, 128, 0],
660 [0, 0, 128],
661 [128, 0, 128],
662 [0, 128, 128],
663 [192, 192, 192],
664 [128, 128, 128],
665 [255, 0, 0],
666 [0, 255, 0],
667 [255, 255, 0],
668 [0, 0, 255],
669 [255, 0, 255],
670 [0, 255, 255],
671 [255, 255, 255],
672 ];
673 table[0..16].copy_from_slice(&std);
674 let levels = [0u8, 95, 135, 175, 215, 255];
675 for r in 0..6 {
676 for g in 0..6 {
677 for b in 0..6 {
678 let idx = 16 + 36 * r + 6 * g + b;
679 table[idx] = [levels[r], levels[g], levels[b]];
680 }
681 }
682 }
683 for gr in 0..24 {
684 let val = (gr * 10 + 8) as u8;
685 table[232 + gr] = [val, val, val];
686 }
687 table
688});
689
690pub fn rgb_to_8bit(r: u8, g: u8, b: u8) -> u8 {
696 let grey = ((r as u32 + g as u32 + b as u32) / 3) as u8;
698 if r == g && g == b {
699 if r == 0 && g == 0 && b == 0 {
701 return 0;
702 }
703 if grey < 8 {
704 return 16;
705 }
706 if grey > 248 {
707 return 231; }
709 return 232 + (grey - 8) / 10;
710 }
711
712 let r6 = ((r as f64 / 255.0) * 5.0).round() as u8;
714 let g6 = ((g as f64 / 255.0) * 5.0).round() as u8;
715 let b6 = ((b as f64 / 255.0) * 5.0).round() as u8;
716 16 + 36 * r6 + 6 * g6 + b6
717}
718
719pub fn rgb_to_standard(_r: u8, _g: u8, _b: u8) -> u8 {
721 let mut best_idx = 0u8;
723 let mut best_dist = u32::MAX;
724 for (i, &(pr, pg, pb)) in STANDARD_PALETTE.iter().enumerate() {
725 let dr = _r as i32 - pr as i32;
726 let dg = _g as i32 - pg as i32;
727 let db = _b as i32 - pb as i32;
728 let dist = (dr * dr + dg * dg + db * db) as u32;
729 if dist < best_dist {
730 best_dist = dist;
731 best_idx = i as u8;
732 }
733 }
734 best_idx
735}
736
737pub fn blend_rgb(color1: (u8, u8, u8), color2: (u8, u8, u8), cross_fade: f64) -> (u8, u8, u8) {
739 let r = (color1.0 as f64 + (color2.0 as f64 - color1.0 as f64) * cross_fade) as u8;
740 let g = (color1.1 as f64 + (color2.1 as f64 - color1.1 as f64) * cross_fade) as u8;
741 let b = (color1.2 as f64 + (color2.2 as f64 - color1.2 as f64) * cross_fade) as u8;
742 (r, g, b)
743}
744
745pub fn blend_colors(
747 color1: &Color,
748 color2: &Color,
749 cross_fade: f64,
750 theme: &TerminalTheme,
751) -> Color {
752 let rgb1 = color1.get_truecolor(theme);
753 let rgb2 = color2.get_truecolor(theme);
754 let blended = blend_rgb(rgb1, rgb2, cross_fade);
755 Color::from_rgb(blended.0, blended.1, blended.2)
756}
757
758use std::collections::HashMap;
766use std::sync::LazyLock;
767
768static ANSI_NAME_MAP: LazyLock<HashMap<&'static str, u8>> = LazyLock::new(|| {
769 let mut m = HashMap::new();
770 m.insert("black", 0u8);
772 m.insert("red", 1u8);
773 m.insert("green", 2u8);
774 m.insert("yellow", 3u8);
775 m.insert("blue", 4u8);
776 m.insert("magenta", 5u8);
777 m.insert("cyan", 6u8);
778 m.insert("white", 7u8);
779 m.insert("bright_black", 8u8);
780 m.insert("grey", 8u8);
781 m.insert("gray", 8u8);
782 m.insert("bright_red", 9u8);
783 m.insert("bright_green", 10u8);
784 m.insert("bright_yellow", 11u8);
785 m.insert("bright_blue", 12u8);
786 m.insert("bright_magenta", 13u8);
787 m.insert("bright_cyan", 14u8);
788 m.insert("bright_white", 15u8);
789 m.insert("grey0", 16u8);
791 m.insert("gray0", 16u8);
792 m.insert("navy_blue", 17u8);
793 m.insert("dark_blue", 18u8);
794 m.insert("blue3", 20u8);
795 m.insert("blue1", 21u8);
796 m.insert("dark_green", 22u8);
797 m.insert("deep_sky_blue4", 25u8);
798 m.insert("dodger_blue3", 26u8);
799 m.insert("dodger_blue2", 27u8);
800 m.insert("green4", 28u8);
801 m.insert("spring_green4", 29u8);
802 m.insert("turquoise4", 30u8);
803 m.insert("deep_sky_blue3", 32u8);
804 m.insert("dodger_blue1", 33u8);
805 m.insert("dark_cyan", 36u8);
806 m.insert("light_sea_green", 37u8);
807 m.insert("deep_sky_blue2", 38u8);
808 m.insert("deep_sky_blue1", 39u8);
809 m.insert("green3", 40u8);
810 m.insert("spring_green3", 41u8);
811 m.insert("cyan3", 43u8);
812 m.insert("dark_turquoise", 44u8);
813 m.insert("turquoise2", 45u8);
814 m.insert("green1", 46u8);
815 m.insert("spring_green2", 47u8);
816 m.insert("spring_green1", 48u8);
817 m.insert("medium_spring_green", 49u8);
818 m.insert("cyan2", 50u8);
819 m.insert("cyan1", 51u8);
820 m.insert("purple4", 55u8);
821 m.insert("purple3", 56u8);
822 m.insert("blue_violet", 57u8);
823 m.insert("grey37", 59u8);
824 m.insert("gray37", 59u8);
825 m.insert("medium_purple4", 60u8);
826 m.insert("slate_blue3", 62u8);
827 m.insert("royal_blue1", 63u8);
828 m.insert("chartreuse4", 64u8);
829 m.insert("pale_turquoise4", 66u8);
830 m.insert("steel_blue", 67u8);
831 m.insert("steel_blue3", 68u8);
832 m.insert("cornflower_blue", 69u8);
833 m.insert("dark_sea_green4", 71u8);
834 m.insert("dark_sea_green", 71u8);
835 m.insert("cadet_blue", 73u8);
836 m.insert("sky_blue3", 74u8);
837 m.insert("chartreuse3", 76u8);
838 m.insert("sea_green3", 78u8);
839 m.insert("aquamarine3", 79u8);
840 m.insert("medium_turquoise", 80u8);
841 m.insert("steel_blue1", 81u8);
842 m.insert("sea_green2", 83u8);
843 m.insert("sea_green1", 85u8);
844 m.insert("dark_slate_gray2", 87u8);
845 m.insert("dark_red", 88u8);
846 m.insert("dark_magenta", 91u8);
847 m.insert("orange4", 94u8);
848 m.insert("light_pink4", 95u8);
849 m.insert("plum4", 96u8);
850 m.insert("medium_purple3", 98u8);
851 m.insert("slate_blue1", 99u8);
852 m.insert("wheat4", 101u8);
853 m.insert("grey53", 102u8);
854 m.insert("gray53", 102u8);
855 m.insert("light_slate_grey", 103u8);
856 m.insert("light_slate_gray", 103u8);
857 m.insert("medium_purple", 104u8);
858 m.insert("light_slate_blue", 105u8);
859 m.insert("yellow4", 106u8);
860 m.insert("dark_olive_green3", 110u8); m.insert("light_sky_blue3", 110u8);
862 m.insert("sky_blue2", 111u8);
863 m.insert("chartreuse2", 112u8);
864 m.insert("pale_green3", 114u8);
865 m.insert("dark_slate_gray3", 116u8);
866 m.insert("sky_blue1", 117u8);
867 m.insert("chartreuse1", 118u8);
868 m.insert("light_green", 120u8);
869 m.insert("aquamarine1", 122u8);
870 m.insert("dark_slate_gray1", 123u8);
871 m.insert("deep_pink4", 125u8);
872 m.insert("medium_violet_red", 126u8);
873 m.insert("dark_violet", 128u8);
874 m.insert("purple", 129u8);
875 m.insert("medium_orchid3", 133u8);
876 m.insert("medium_orchid", 134u8);
877 m.insert("dark_goldenrod", 136u8);
878 m.insert("rosy_brown", 138u8);
879 m.insert("grey63", 139u8);
880 m.insert("gray63", 139u8);
881 m.insert("medium_purple2", 140u8);
882 m.insert("medium_purple1", 141u8);
883 m.insert("dark_khaki", 143u8);
884 m.insert("navajo_white3", 144u8);
885 m.insert("grey69", 145u8);
886 m.insert("gray69", 145u8);
887 m.insert("light_steel_blue3", 146u8);
888 m.insert("light_steel_blue", 147u8);
889 m.insert("dark_olive_green2", 155u8);
890 m.insert("pale_green1", 156u8);
891 m.insert("dark_sea_green2", 157u8);
892 m.insert("pale_turquoise1", 159u8);
893 m.insert("red3", 160u8);
894 m.insert("deep_pink3", 162u8);
895 m.insert("magenta3", 164u8);
896 m.insert("dark_orange3", 166u8);
897 m.insert("indian_red", 167u8);
898 m.insert("hot_pink3", 168u8);
899 m.insert("hot_pink2", 169u8);
900 m.insert("orchid", 170u8);
901 m.insert("orange3", 172u8);
902 m.insert("light_salmon3", 173u8);
903 m.insert("light_pink3", 174u8);
904 m.insert("pink3", 175u8);
905 m.insert("plum3", 176u8);
906 m.insert("violet", 177u8);
907 m.insert("gold3", 178u8);
908 m.insert("light_goldenrod3", 179u8);
909 m.insert("tan", 180u8);
910 m.insert("misty_rose3", 181u8);
911 m.insert("thistle3", 182u8);
912 m.insert("plum2", 183u8);
913 m.insert("yellow3", 184u8);
914 m.insert("khaki3", 185u8);
915 m.insert("light_yellow3", 187u8);
916 m.insert("grey84", 188u8);
917 m.insert("gray84", 188u8);
918 m.insert("light_steel_blue1", 189u8);
919 m.insert("yellow2", 190u8);
920 m.insert("dark_olive_green1", 192u8);
921 m.insert("dark_sea_green1", 193u8);
922 m.insert("honeydew2", 194u8);
923 m.insert("light_cyan1", 195u8);
924 m.insert("red1", 196u8);
925 m.insert("deep_pink2", 197u8);
926 m.insert("deep_pink1", 199u8);
927 m.insert("magenta2", 200u8);
928 m.insert("magenta1", 201u8);
929 m.insert("orange_red1", 202u8);
930 m.insert("indian_red1", 204u8);
931 m.insert("hot_pink", 206u8);
932 m.insert("medium_orchid1", 207u8);
933 m.insert("dark_orange", 208u8);
934 m.insert("salmon1", 209u8);
935 m.insert("light_coral", 210u8);
936 m.insert("pale_violet_red1", 211u8);
937 m.insert("orchid2", 212u8);
938 m.insert("orchid1", 213u8);
939 m.insert("orange1", 214u8);
940 m.insert("sandy_brown", 215u8);
941 m.insert("light_salmon1", 216u8);
942 m.insert("light_pink1", 217u8);
943 m.insert("pink1", 218u8);
944 m.insert("plum1", 219u8);
945 m.insert("gold1", 220u8);
946 m.insert("light_goldenrod2", 222u8);
947 m.insert("navajo_white1", 223u8);
948 m.insert("misty_rose1", 224u8);
949 m.insert("thistle1", 225u8);
950 m.insert("yellow1", 226u8);
951 m.insert("light_goldenrod1", 227u8);
952 m.insert("khaki1", 228u8);
953 m.insert("wheat1", 229u8);
954 m.insert("cornsilk1", 230u8);
955 m.insert("grey100", 231u8);
956 m.insert("gray100", 231u8);
957 m.insert("grey3", 232u8);
959 m.insert("gray3", 232u8);
960 m.insert("grey7", 233u8);
961 m.insert("gray7", 233u8);
962 m.insert("grey11", 234u8);
963 m.insert("gray11", 234u8);
964 m.insert("grey15", 235u8);
965 m.insert("gray15", 235u8);
966 m.insert("grey19", 236u8);
967 m.insert("gray19", 236u8);
968 m.insert("grey23", 237u8);
969 m.insert("gray23", 237u8);
970 m.insert("grey27", 238u8);
971 m.insert("gray27", 238u8);
972 m.insert("grey30", 239u8);
973 m.insert("gray30", 239u8);
974 m.insert("grey35", 240u8);
975 m.insert("gray35", 240u8);
976 m.insert("grey39", 241u8);
977 m.insert("gray39", 241u8);
978 m.insert("grey42", 242u8);
979 m.insert("gray42", 242u8);
980 m.insert("grey46", 243u8);
981 m.insert("gray46", 243u8);
982 m.insert("grey50", 244u8);
983 m.insert("gray50", 244u8);
984 m.insert("grey54", 245u8);
985 m.insert("gray54", 245u8);
986 m.insert("grey58", 246u8);
987 m.insert("gray58", 246u8);
988 m.insert("grey62", 247u8);
989 m.insert("gray62", 247u8);
990 m.insert("grey66", 248u8);
991 m.insert("gray66", 248u8);
992 m.insert("dark_grey", 248u8);
993 m.insert("dark_gray", 248u8);
994 m.insert("grey70", 249u8);
995 m.insert("gray70", 249u8);
996 m.insert("grey74", 250u8);
997 m.insert("gray74", 250u8);
998 m.insert("light_grey", 250u8);
999 m.insert("light_gray", 250u8);
1000 m.insert("grey78", 251u8);
1001 m.insert("gray78", 251u8);
1002 m.insert("grey82", 252u8);
1003 m.insert("gray82", 252u8);
1004 m.insert("grey85", 253u8);
1005 m.insert("gray85", 253u8);
1006 m.insert("grey89", 254u8);
1007 m.insert("gray89", 254u8);
1008 m.insert("grey93", 255u8);
1009 m.insert("gray93", 255u8);
1010 m
1011});
1012
1013impl Color {
1014 pub fn name_to_index(name: &str) -> Option<u8> {
1016 ANSI_NAME_MAP.get(name).copied()
1017 }
1018}
1019
1020#[cfg(test)]
1021mod tests {
1022 use super::*;
1023
1024 #[test]
1025 fn test_default_color() {
1026 let c = Color::default();
1027 assert!(c.is_default());
1028 }
1029
1030 #[test]
1031 fn test_parse_red() {
1032 let c = Color::parse("red").unwrap();
1033 assert_eq!(c.number, Some(1));
1034 }
1035
1036 #[test]
1037 fn test_parse_hex() {
1038 let c = Color::parse("#ff0000").unwrap();
1039 assert_eq!(c.triplet, Some((255, 0, 0)));
1040 }
1041
1042 #[test]
1043 fn test_rgb_to_8bit_black() {
1044 assert_eq!(rgb_to_8bit(0, 0, 0), 0);
1046 }
1047
1048 #[test]
1049 fn test_from_triplet() {
1050 let triplet = ColorTriplet::new(255, 128, 0);
1051 let color = Color::from_triplet(&triplet);
1052 assert_eq!(color.triplet(), Some((255, 128, 0)));
1053 }
1054
1055 #[test]
1056 fn test_is_system_defined() {
1057 let default = Color::default();
1058 assert!(!default.is_system_defined());
1059
1060 let red = Color::parse("red").unwrap();
1061 assert!(red.is_system_defined());
1062
1063 let custom = Color::from_rgb(100, 200, 50);
1064 assert!(!custom.is_system_defined());
1065 }
1066
1067 #[test]
1068 fn test_get_ansi_codes_standard() {
1069 let red = Color::parse("red").unwrap();
1070 let (fg, _bg) = red.get_ansi_codes(false);
1071 assert!(fg.is_some());
1072 assert!(fg.unwrap().contains("31"));
1073 }
1074
1075 #[test]
1076 fn test_get_ansi_codes_background() {
1077 let blue = Color::parse("blue").unwrap();
1078 let (_fg, bg) = blue.get_ansi_codes(true);
1079 assert!(bg.is_some());
1080 assert!(bg.unwrap().contains("44"));
1081 }
1082
1083 #[test]
1084 fn test_get_ansi_codes_truecolor() {
1085 let c = Color::from_rgb(255, 128, 0);
1086 let (fg, _bg) = c.get_ansi_codes(false);
1087 assert!(fg.is_some());
1088 assert!(fg.unwrap().contains("38;2;255;128;0"));
1089 }
1090
1091 #[test]
1092 fn test_get_ansi_codes_default() {
1093 let c = Color::default();
1094 let (fg, bg) = c.get_ansi_codes(false);
1095 assert!(fg.is_none());
1096 assert!(bg.is_none());
1097 }
1098
1099 #[test]
1100 fn test_color_name() {
1101 let red = Color::parse("red").unwrap();
1102 assert_eq!(red.number(), Some(1));
1105 }
1106
1107 #[test]
1108 fn test_color_number() {
1109 let c = Color::from_8bit(42);
1110 assert_eq!(c.number(), Some(42));
1111 }
1112
1113 #[test]
1114 fn test_color_triplet() {
1115 let c = Color::from_rgb(10, 20, 30);
1116 assert_eq!(c.triplet(), Some((10, 20, 30)));
1117 let d = Color::default();
1118 assert_eq!(d.triplet(), None);
1119 }
1120}