1use std::fmt;
17
18#[derive(Debug, Clone, PartialEq)]
20pub struct Color {
21 r: u8,
22 g: u8,
23 b: u8,
24 a: f32, }
26
27#[derive(Debug, Clone, PartialEq)]
29pub struct Rgb {
30 pub r: u8,
31 pub g: u8,
32 pub b: u8,
33}
34
35#[derive(Debug, Clone, PartialEq)]
37pub struct Hsl {
38 pub h: f32, pub s: f32, pub l: f32, }
42
43#[derive(Debug, Clone, PartialEq)]
45pub struct Hsv {
46 pub h: f32, pub s: f32, pub v: f32, }
50
51#[derive(Debug, Clone, PartialEq)]
53pub struct Cmyk {
54 pub c: f32, pub m: f32, pub y: f32, pub k: f32, }
59
60#[derive(Debug, PartialEq)]
62pub enum ColorError {
63 InvalidHexFormat,
64 InvalidHexLength,
65 InvalidRgbValue,
66 InvalidHslValue,
67 InvalidHsvValue,
68 InvalidCmykValue,
69 InvalidMinecraftCode,
70}
71
72impl fmt::Display for ColorError {
73 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74 match self {
75 ColorError::InvalidHexFormat => write!(f, "Invalid hex color format"),
76 ColorError::InvalidHexLength => write!(f, "Invalid hex color length"),
77 ColorError::InvalidRgbValue => write!(f, "RGB values must be between 0 and 255"),
78 ColorError::InvalidHslValue => write!(f, "Invalid HSL values"),
79 ColorError::InvalidHsvValue => write!(f, "Invalid HSV values"),
80 ColorError::InvalidCmykValue => write!(f, "CMYK values must be between 0 and 100"),
81 ColorError::InvalidMinecraftCode => write!(f, "Invalid Minecraft color code"),
82 }
83 }
84}
85
86impl std::error::Error for ColorError {}
87
88impl Color {
89 pub fn from_rgb(r: u8, g: u8, b: u8) -> Self {
91 Color { r, g, b, a: 1.0 }
92 }
93
94 pub fn from_rgba(r: u8, g: u8, b: u8, a: f32) -> Self {
96 Color {
97 r,
98 g,
99 b,
100 a: a.clamp(0.0, 1.0),
101 }
102 }
103
104 pub fn from_hex(hex: &str) -> Result<Self, ColorError> {
106 let hex = hex.trim_start_matches('#');
107
108 match hex.len() {
109 3 => {
110 let chars: Vec<char> = hex.chars().collect();
112 let r = u8::from_str_radix(&format!("{}{}", chars[0], chars[0]), 16)
113 .map_err(|_| ColorError::InvalidHexFormat)?;
114 let g = u8::from_str_radix(&format!("{}{}", chars[1], chars[1]), 16)
115 .map_err(|_| ColorError::InvalidHexFormat)?;
116 let b = u8::from_str_radix(&format!("{}{}", chars[2], chars[2]), 16)
117 .map_err(|_| ColorError::InvalidHexFormat)?;
118 Ok(Color::from_rgb(r, g, b))
119 }
120 6 => {
121 let r =
123 u8::from_str_radix(&hex[0..2], 16).map_err(|_| ColorError::InvalidHexFormat)?;
124 let g =
125 u8::from_str_radix(&hex[2..4], 16).map_err(|_| ColorError::InvalidHexFormat)?;
126 let b =
127 u8::from_str_radix(&hex[4..6], 16).map_err(|_| ColorError::InvalidHexFormat)?;
128 Ok(Color::from_rgb(r, g, b))
129 }
130 8 => {
131 let r =
133 u8::from_str_radix(&hex[0..2], 16).map_err(|_| ColorError::InvalidHexFormat)?;
134 let g =
135 u8::from_str_radix(&hex[2..4], 16).map_err(|_| ColorError::InvalidHexFormat)?;
136 let b =
137 u8::from_str_radix(&hex[4..6], 16).map_err(|_| ColorError::InvalidHexFormat)?;
138 let a = u8::from_str_radix(&hex[6..8], 16)
139 .map_err(|_| ColorError::InvalidHexFormat)? as f32
140 / 255.0;
141 Ok(Color::from_rgba(r, g, b, a))
142 }
143 _ => Err(ColorError::InvalidHexLength),
144 }
145 }
146
147 pub fn from_hsl(h: f32, s: f32, l: f32) -> Result<Self, ColorError> {
149 if s < 0.0 || s > 100.0 || l < 0.0 || l > 100.0 {
150 return Err(ColorError::InvalidHslValue);
151 }
152
153 let h = h % 360.0;
154 let s = s / 100.0;
155 let l = l / 100.0;
156
157 let c = (1.0 - (2.0 * l - 1.0).abs()) * s;
158 let x = c * (1.0 - ((h / 60.0) % 2.0 - 1.0).abs());
159 let m = l - c / 2.0;
160
161 let (r_prime, g_prime, b_prime) = match h {
162 h if h < 60.0 => (c, x, 0.0),
163 h if h < 120.0 => (x, c, 0.0),
164 h if h < 180.0 => (0.0, c, x),
165 h if h < 240.0 => (0.0, x, c),
166 h if h < 300.0 => (x, 0.0, c),
167 _ => (c, 0.0, x),
168 };
169
170 let r = ((r_prime + m) * 255.0).round() as u8;
171 let g = ((g_prime + m) * 255.0).round() as u8;
172 let b = ((b_prime + m) * 255.0).round() as u8;
173
174 Ok(Color::from_rgb(r, g, b))
175 }
176
177 pub fn from_minecraft_code(code: &str) -> Result<Self, ColorError> {
179 if code.is_empty() {
180 return Err(ColorError::InvalidMinecraftCode);
181 }
182
183 if code.starts_with("&#") && code.len() == 8 {
185 return Color::from_hex(&code[2..]);
186 }
187
188 if code.starts_with("&x") && code.len() == 14 {
190 let hex_chars: String = code
191 .chars()
192 .skip(2)
193 .enumerate()
194 .filter_map(|(i, c)| if i % 2 == 1 { Some(c) } else { None })
195 .collect();
196 return Color::from_hex(&hex_chars);
197 }
198
199 let color_char = if code.starts_with('§') || code.starts_with('&') {
201 code.chars()
202 .nth(1)
203 .ok_or(ColorError::InvalidMinecraftCode)?
204 } else if code.len() == 1 {
205 code.chars().next().unwrap()
206 } else {
207 return Err(ColorError::InvalidMinecraftCode);
208 };
209
210 match color_char {
211 '0' => Ok(Color::from_rgb(0, 0, 0)), '1' => Ok(Color::from_rgb(0, 0, 170)), '2' => Ok(Color::from_rgb(0, 170, 0)), '3' => Ok(Color::from_rgb(0, 170, 170)), '4' => Ok(Color::from_rgb(170, 0, 0)), '5' => Ok(Color::from_rgb(170, 0, 170)), '6' => Ok(Color::from_rgb(255, 170, 0)), '7' => Ok(Color::from_rgb(170, 170, 170)), '8' => Ok(Color::from_rgb(85, 85, 85)), '9' => Ok(Color::from_rgb(85, 85, 255)), 'a' | 'A' => Ok(Color::from_rgb(85, 255, 85)), 'b' | 'B' => Ok(Color::from_rgb(85, 255, 255)), 'c' | 'C' => Ok(Color::from_rgb(255, 85, 85)), 'd' | 'D' => Ok(Color::from_rgb(255, 85, 255)), 'e' | 'E' => Ok(Color::from_rgb(255, 255, 85)), 'f' | 'F' => Ok(Color::from_rgb(255, 255, 255)), _ => Err(ColorError::InvalidMinecraftCode),
228 }
229 }
230
231 pub fn from_hsv(h: f32, s: f32, v: f32) -> Result<Self, ColorError> {
233 if s < 0.0 || s > 100.0 || v < 0.0 || v > 100.0 {
234 return Err(ColorError::InvalidHsvValue);
235 }
236
237 let h = h % 360.0;
238 let s = s / 100.0;
239 let v = v / 100.0;
240
241 let c = v * s;
242 let x = c * (1.0 - ((h / 60.0) % 2.0 - 1.0).abs());
243 let m = v - c;
244
245 let (r_prime, g_prime, b_prime) = match h {
246 h if h < 60.0 => (c, x, 0.0),
247 h if h < 120.0 => (x, c, 0.0),
248 h if h < 180.0 => (0.0, c, x),
249 h if h < 240.0 => (0.0, x, c),
250 h if h < 300.0 => (x, 0.0, c),
251 _ => (c, 0.0, x),
252 };
253
254 let r = ((r_prime + m) * 255.0).round() as u8;
255 let g = ((g_prime + m) * 255.0).round() as u8;
256 let b = ((b_prime + m) * 255.0).round() as u8;
257
258 Ok(Color::from_rgb(r, g, b))
259 }
260
261 pub fn from_cmyk(c: f32, m: f32, y: f32, k: f32) -> Result<Self, ColorError> {
263 if c < 0.0
264 || c > 100.0
265 || m < 0.0
266 || m > 100.0
267 || y < 0.0
268 || y > 100.0
269 || k < 0.0
270 || k > 100.0
271 {
272 return Err(ColorError::InvalidCmykValue);
273 }
274
275 let c = c / 100.0;
276 let m = m / 100.0;
277 let y = y / 100.0;
278 let k = k / 100.0;
279
280 let r = (255.0 * (1.0 - c) * (1.0 - k)).round() as u8;
281 let g = (255.0 * (1.0 - m) * (1.0 - k)).round() as u8;
282 let b = (255.0 * (1.0 - y) * (1.0 - k)).round() as u8;
283
284 Ok(Color::from_rgb(r, g, b))
285 }
286
287 pub fn to_rgb(&self) -> Rgb {
289 Rgb {
290 r: self.r,
291 g: self.g,
292 b: self.b,
293 }
294 }
295
296 pub fn to_hex(&self) -> String {
298 format!("#{:02X}{:02X}{:02X}", self.r, self.g, self.b)
299 }
300
301 pub fn to_hex_alpha(&self) -> String {
303 let alpha = (self.a * 255.0).round() as u8;
304 format!("#{:02X}{:02X}{:02X}{:02X}", self.r, self.g, self.b, alpha)
305 }
306
307 pub fn to_hsl(&self) -> Hsl {
309 let r = self.r as f32 / 255.0;
310 let g = self.g as f32 / 255.0;
311 let b = self.b as f32 / 255.0;
312
313 let max = r.max(g.max(b));
314 let min = r.min(g.min(b));
315 let delta = max - min;
316
317 let l = (max + min) / 2.0;
318
319 let (h, s) = if delta == 0.0 {
320 (0.0, 0.0)
321 } else {
322 let s = if l < 0.5 {
323 delta / (max + min)
324 } else {
325 delta / (2.0 - max - min)
326 };
327
328 let h = match max {
329 x if x == r => ((g - b) / delta + if g < b { 6.0 } else { 0.0 }) * 60.0,
330 x if x == g => ((b - r) / delta + 2.0) * 60.0,
331 _ => ((r - g) / delta + 4.0) * 60.0,
332 };
333
334 (h, s * 100.0)
335 };
336
337 Hsl { h, s, l: l * 100.0 }
338 }
339
340 pub fn to_hsv(&self) -> Hsv {
342 let r = self.r as f32 / 255.0;
343 let g = self.g as f32 / 255.0;
344 let b = self.b as f32 / 255.0;
345
346 let max = r.max(g.max(b));
347 let min = r.min(g.min(b));
348 let delta = max - min;
349
350 let v = max;
351
352 let (h, s) = if delta == 0.0 {
353 (0.0, 0.0)
354 } else {
355 let s = delta / max;
356
357 let h = match max {
358 x if x == r => ((g - b) / delta + if g < b { 6.0 } else { 0.0 }) * 60.0,
359 x if x == g => ((b - r) / delta + 2.0) * 60.0,
360 _ => ((r - g) / delta + 4.0) * 60.0,
361 };
362
363 (h, s * 100.0)
364 };
365
366 Hsv { h, s, v: v * 100.0 }
367 }
368
369 pub fn to_cmyk(&self) -> Cmyk {
371 let r = self.r as f32 / 255.0;
372 let g = self.g as f32 / 255.0;
373 let b = self.b as f32 / 255.0;
374
375 let k = 1.0 - r.max(g.max(b));
376
377 if k == 1.0 {
378 Cmyk {
379 c: 0.0,
380 m: 0.0,
381 y: 0.0,
382 k: 100.0,
383 }
384 } else {
385 let c = (1.0 - r - k) / (1.0 - k) * 100.0;
386 let m = (1.0 - g - k) / (1.0 - k) * 100.0;
387 let y = (1.0 - b - k) / (1.0 - k) * 100.0;
388
389 Cmyk {
390 c,
391 m,
392 y,
393 k: k * 100.0,
394 }
395 }
396 }
397
398 pub fn luminance(&self) -> f32 {
400 let r = self.r as f32 / 255.0;
401 let g = self.g as f32 / 255.0;
402 let b = self.b as f32 / 255.0;
403
404 0.299 * r + 0.587 * g + 0.114 * b
405 }
406
407 pub fn is_dark(&self) -> bool {
409 self.luminance() < 0.5
410 }
411
412 pub fn is_light(&self) -> bool {
414 !self.is_dark()
415 }
416
417 pub fn contrasting_text_color(&self) -> Color {
419 if self.is_dark() {
420 Color::from_rgb(255, 255, 255) } else {
422 Color::from_rgb(0, 0, 0) }
424 }
425
426 pub fn blend(&self, other: &Color, ratio: f32) -> Color {
428 let ratio = ratio.clamp(0.0, 1.0);
429 let inv_ratio = 1.0 - ratio;
430
431 let r = (self.r as f32 * inv_ratio + other.r as f32 * ratio).round() as u8;
432 let g = (self.g as f32 * inv_ratio + other.g as f32 * ratio).round() as u8;
433 let b = (self.b as f32 * inv_ratio + other.b as f32 * ratio).round() as u8;
434 let a = self.a * inv_ratio + other.a * ratio;
435
436 Color::from_rgba(r, g, b, a)
437 }
438
439 pub fn to_minecraft_code(&self) -> String {
441 let distances: Vec<(f32, char)> = vec![
442 (self.color_distance(&Color::from_rgb(0, 0, 0)), '0'), (self.color_distance(&Color::from_rgb(0, 0, 170)), '1'), (self.color_distance(&Color::from_rgb(0, 170, 0)), '2'), (self.color_distance(&Color::from_rgb(0, 170, 170)), '3'), (self.color_distance(&Color::from_rgb(170, 0, 0)), '4'), (self.color_distance(&Color::from_rgb(170, 0, 170)), '5'), (self.color_distance(&Color::from_rgb(255, 170, 0)), '6'), (self.color_distance(&Color::from_rgb(170, 170, 170)), '7'), (self.color_distance(&Color::from_rgb(85, 85, 85)), '8'), (self.color_distance(&Color::from_rgb(85, 85, 255)), '9'), (self.color_distance(&Color::from_rgb(85, 255, 85)), 'a'), (self.color_distance(&Color::from_rgb(85, 255, 255)), 'b'), (self.color_distance(&Color::from_rgb(255, 85, 85)), 'c'), (self.color_distance(&Color::from_rgb(255, 85, 255)), 'd'), (self.color_distance(&Color::from_rgb(255, 255, 85)), 'e'), (self.color_distance(&Color::from_rgb(255, 255, 255)), 'f'), ];
459
460 let closest = distances
461 .iter()
462 .min_by(|a, b| a.0.partial_cmp(&b.0).unwrap())
463 .unwrap();
464 format!("§{}", closest.1)
465 }
466
467 pub fn to_minecraft_hex(&self) -> String {
469 format!("&#{:02X}{:02X}{:02X}", self.r, self.g, self.b)
470 }
471
472 pub fn to_minecraft_hex_alt(&self) -> String {
474 let hex = format!("{:02X}{:02X}{:02X}", self.r, self.g, self.b);
475 let mut result = String::from("&x");
476 for ch in hex.chars() {
477 result.push('&');
478 result.push(ch);
479 }
480 result
481 }
482
483 fn color_distance(&self, other: &Color) -> f32 {
485 let dr = self.r as f32 - other.r as f32;
486 let dg = self.g as f32 - other.g as f32;
487 let db = self.b as f32 - other.b as f32;
488 (dr * dr + dg * dg + db * db).sqrt()
489 }
490
491 pub fn darken(&self, percentage: f32) -> Color {
493 let factor = 1.0 - (percentage / 100.0).clamp(0.0, 1.0);
494 let r = (self.r as f32 * factor).round() as u8;
495 let g = (self.g as f32 * factor).round() as u8;
496 let b = (self.b as f32 * factor).round() as u8;
497
498 Color::from_rgba(r, g, b, self.a)
499 }
500
501 pub fn lighten(&self, percentage: f32) -> Color {
503 let factor = (percentage / 100.0).clamp(0.0, 1.0);
504 let r = (self.r as f32 + (255.0 - self.r as f32) * factor).round() as u8;
505 let g = (self.g as f32 + (255.0 - self.g as f32) * factor).round() as u8;
506 let b = (self.b as f32 + (255.0 - self.b as f32) * factor).round() as u8;
507
508 Color::from_rgba(r, g, b, self.a)
509 }
510}
511
512impl fmt::Display for Rgb {
514 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
515 write!(f, "rgb({}, {}, {})", self.r, self.g, self.b)
516 }
517}
518
519impl fmt::Display for Hsl {
520 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
521 write!(f, "hsl({:.1}°, {:.1}%, {:.1}%)", self.h, self.s, self.l)
522 }
523}
524
525impl fmt::Display for Hsv {
526 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
527 write!(f, "hsv({:.1}°, {:.1}%, {:.1}%)", self.h, self.s, self.v)
528 }
529}
530
531impl fmt::Display for Cmyk {
532 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
533 write!(
534 f,
535 "cmyk({:.1}%, {:.1}%, {:.1}%, {:.1}%)",
536 self.c, self.m, self.y, self.k
537 )
538 }
539}
540
541impl fmt::Display for Color {
542 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
543 write!(f, "{}", self.to_hex())
544 }
545}
546
547impl Color {
549 pub const BLACK: Color = Color {
550 r: 0,
551 g: 0,
552 b: 0,
553 a: 1.0,
554 };
555 pub const WHITE: Color = Color {
556 r: 255,
557 g: 255,
558 b: 255,
559 a: 1.0,
560 };
561 pub const RED: Color = Color {
562 r: 255,
563 g: 0,
564 b: 0,
565 a: 1.0,
566 };
567 pub const GREEN: Color = Color {
568 r: 0,
569 g: 255,
570 b: 0,
571 a: 1.0,
572 };
573 pub const BLUE: Color = Color {
574 r: 0,
575 g: 0,
576 b: 255,
577 a: 1.0,
578 };
579 pub const YELLOW: Color = Color {
580 r: 255,
581 g: 255,
582 b: 0,
583 a: 1.0,
584 };
585 pub const CYAN: Color = Color {
586 r: 0,
587 g: 255,
588 b: 255,
589 a: 1.0,
590 };
591 pub const MAGENTA: Color = Color {
592 r: 255,
593 g: 0,
594 b: 255,
595 a: 1.0,
596 };
597
598 pub const MC_BLACK: Color = Color {
600 r: 0,
601 g: 0,
602 b: 0,
603 a: 1.0,
604 };
605 pub const MC_DARK_BLUE: Color = Color {
606 r: 0,
607 g: 0,
608 b: 170,
609 a: 1.0,
610 };
611 pub const MC_DARK_GREEN: Color = Color {
612 r: 0,
613 g: 170,
614 b: 0,
615 a: 1.0,
616 };
617 pub const MC_DARK_AQUA: Color = Color {
618 r: 0,
619 g: 170,
620 b: 170,
621 a: 1.0,
622 };
623 pub const MC_DARK_RED: Color = Color {
624 r: 170,
625 g: 0,
626 b: 0,
627 a: 1.0,
628 };
629 pub const MC_DARK_PURPLE: Color = Color {
630 r: 170,
631 g: 0,
632 b: 170,
633 a: 1.0,
634 };
635 pub const MC_GOLD: Color = Color {
636 r: 255,
637 g: 170,
638 b: 0,
639 a: 1.0,
640 };
641 pub const MC_GRAY: Color = Color {
642 r: 170,
643 g: 170,
644 b: 170,
645 a: 1.0,
646 };
647 pub const MC_DARK_GRAY: Color = Color {
648 r: 85,
649 g: 85,
650 b: 85,
651 a: 1.0,
652 };
653 pub const MC_BLUE: Color = Color {
654 r: 85,
655 g: 85,
656 b: 255,
657 a: 1.0,
658 };
659 pub const MC_GREEN: Color = Color {
660 r: 85,
661 g: 255,
662 b: 85,
663 a: 1.0,
664 };
665 pub const MC_AQUA: Color = Color {
666 r: 85,
667 g: 255,
668 b: 255,
669 a: 1.0,
670 };
671 pub const MC_RED: Color = Color {
672 r: 255,
673 g: 85,
674 b: 85,
675 a: 1.0,
676 };
677 pub const MC_LIGHT_PURPLE: Color = Color {
678 r: 255,
679 g: 85,
680 b: 255,
681 a: 1.0,
682 };
683 pub const MC_YELLOW: Color = Color {
684 r: 255,
685 g: 255,
686 b: 85,
687 a: 1.0,
688 };
689 pub const MC_WHITE: Color = Color {
690 r: 255,
691 g: 255,
692 b: 255,
693 a: 1.0,
694 };
695}
696
697#[cfg(test)]
698mod tests {
699 use super::*;
700
701 #[test]
702 fn test_hex_to_rgb() {
703 let color = Color::from_hex("#FF5733").unwrap();
704 assert_eq!(color.r, 255);
705 assert_eq!(color.g, 87);
706 assert_eq!(color.b, 51);
707 }
708
709 #[test]
710 fn test_rgb_to_hex() {
711 let color = Color::from_rgb(255, 87, 51);
712 assert_eq!(color.to_hex(), "#FF5733");
713 }
714
715 #[test]
716 fn test_short_hex() {
717 let color = Color::from_hex("#F53").unwrap();
718 assert_eq!(color.r, 255);
719 assert_eq!(color.g, 85);
720 assert_eq!(color.b, 51);
721 }
722
723 #[test]
724 fn test_hsl_conversion() {
725 let color = Color::from_hsl(10.0, 100.0, 60.0).unwrap();
726 let hsl = color.to_hsl();
727 assert!((hsl.h - 10.0).abs() < 1.0);
728 assert!((hsl.s - 100.0).abs() < 1.0);
729 assert!((hsl.l - 60.0).abs() < 1.0);
730 }
731
732 #[test]
733 fn test_color_blending() {
734 let red = Color::RED;
735 let blue = Color::BLUE;
736 let purple = red.blend(&blue, 0.5);
737
738 assert_eq!(purple.r, 127);
739 assert_eq!(purple.g, 0);
740 assert_eq!(purple.b, 127);
741 }
742
743 #[test]
744 fn test_luminance() {
745 assert!(Color::WHITE.is_light());
746 assert!(Color::BLACK.is_dark());
747 }
748
749 #[test]
750 fn test_darken_lighten() {
751 let color = Color::from_rgb(100, 100, 100);
752 let darker = color.darken(50.0);
753 let lighter = color.lighten(50.0);
754
755 assert!(darker.r < color.r);
756 assert!(lighter.r > color.r);
757 }
758
759 #[test]
760 fn test_cmyk_conversion() {
761 let color = Color::from_cmyk(0.0, 100.0, 100.0, 0.0).unwrap(); assert_eq!(color.r, 255);
763 assert_eq!(color.g, 0);
764 assert_eq!(color.b, 0);
765 }
766
767 #[test]
768 fn test_minecraft_legacy_codes() {
769 let red = Color::from_minecraft_code("§c").unwrap();
771 assert_eq!(red.r, 255);
772 assert_eq!(red.g, 85);
773 assert_eq!(red.b, 85);
774
775 let blue = Color::from_minecraft_code("&1").unwrap();
776 assert_eq!(blue.r, 0);
777 assert_eq!(blue.g, 0);
778 assert_eq!(blue.b, 170);
779
780 let yellow = Color::from_minecraft_code("e").unwrap();
781 assert_eq!(yellow.r, 255);
782 assert_eq!(yellow.g, 255);
783 assert_eq!(yellow.b, 85);
784 }
785
786 #[test]
787 fn test_minecraft_hex_codes() {
788 let color = Color::from_minecraft_code("&#FF5733").unwrap();
790 assert_eq!(color.r, 255);
791 assert_eq!(color.g, 87);
792 assert_eq!(color.b, 51);
793
794 let color2 = Color::from_minecraft_code("&x&F&F&5&7&3&3").unwrap();
796 assert_eq!(color2.r, 255);
797 assert_eq!(color2.g, 87);
798 assert_eq!(color2.b, 51);
799 }
800
801 #[test]
802 fn test_minecraft_code_conversion() {
803 let color = Color::from_rgb(255, 85, 85);
804 let mc_code = color.to_minecraft_code();
805 assert_eq!(mc_code, "§c");
806
807 let hex_code = color.to_minecraft_hex();
808 assert_eq!(hex_code, "&#FF5555");
809
810 let alt_hex = color.to_minecraft_hex_alt();
811 assert_eq!(alt_hex, "&x&F&F&5&5&5&5");
812 }
813
814 #[test]
815 fn test_minecraft_constants() {
816 assert_eq!(Color::MC_RED.r, 255);
817 assert_eq!(Color::MC_RED.g, 85);
818 assert_eq!(Color::MC_RED.b, 85);
819
820 assert_eq!(Color::MC_GOLD.r, 255);
821 assert_eq!(Color::MC_GOLD.g, 170);
822 assert_eq!(Color::MC_GOLD.b, 0);
823 }
824}