1use std::borrow::Cow;
2use std::cmp::Ordering;
3use std::fmt::{Display, Formatter};
4
5type HexColorInner<'a> = either::Either<(u8, u8, u8), Cow<'a, str>>;
6
7#[derive(Debug, Clone, Copy, PartialEq)]
8pub enum Decoration {
9 Random,
10 Bold,
11 Strikethrough,
12 Underlined,
13 Italic,
14}
15
16#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
17#[serde(try_from = "String", into = "String")]
18pub struct HexColor<'a>(HexColorInner<'a>);
19
20#[derive(Debug, Clone, Copy, PartialEq, serde::Serialize, serde::Deserialize)]
21#[serde(rename_all = "snake_case")]
22pub enum DefaultColor {
23 Black,
24 DarkBlue,
25 DarkGreen,
26 DarkCyan,
27 DarkRed,
28 Purple,
29 Gold,
30 Gray,
31 DarkGray,
32 Blue,
33 BrightGreen,
34 Cyan,
35 Red,
36 Pink,
37 Yellow,
38 White,
39}
40
41#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
42#[serde(untagged)]
43pub enum Color<'a> {
44 Default(DefaultColor),
45 Hex(HexColor<'a>),
46}
47
48#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
49pub enum HexColorError {
50 #[error("Hex value contains bad characters")]
51 HexValueContainsBadCharacters,
52 #[error("Hex value too long")]
53 HexValueTooLong,
54 #[error("Hex value too small")]
55 HexValueTooSmall,
56}
57
58impl<'a> HexColor<'a> {
59 const fn new(inner: HexColorInner<'a>) -> Self {
60 Self(inner)
61 }
62
63 const fn get(&self) -> &HexColorInner<'a> {
64 &self.0
65 }
66
67 pub const fn new_rgb(r: u8, g: u8, b: u8) -> Self {
68 Self::new(either::Either::Left((r, g, b)))
69 }
70
71 pub fn new_hex(hex: impl Into<Cow<'a, str>>) -> Result<Self, HexColorError> {
72 let hex = hex.into();
73 match hex.len().cmp(&7) {
74 Ordering::Less => Err(HexColorError::HexValueTooLong),
75 Ordering::Greater => Err(HexColorError::HexValueTooLong),
76 Ordering::Equal => match unsafe { hex.chars().next().unwrap_unchecked() } == '#' &&
78 hex[1..=7].contains(|c: char| {
79 match c {
80 '0'..='9' | 'a'..='f' | 'A'..='F' => false,
81 _ => true
82 }
83 }) {
84 true => Err(HexColorError::HexValueContainsBadCharacters),
85 false => Ok(Self::new(HexColorInner::Right(hex)))
86 }
87 }
88 }
89
90 pub fn get_rgb(&self) -> (u8, u8, u8) {
91 match self.get() {
92 HexColorInner::Left((r, g, b)) => (*r, *g, *b),
93 HexColorInner::Right(str) => unsafe {
95 (
96 u8::from_str_radix(&str[1..3], 16).unwrap_unchecked(),
97 u8::from_str_radix(&str[3..5], 16).unwrap_unchecked(),
98 u8::from_str_radix(&str[5..7], 16).unwrap_unchecked(),
99 )
100 }
101 }
102 }
103
104 pub fn get_hex(&'a self) -> Cow<'a, str> {
105 match self.get() {
106 HexColorInner::Left((r, g, b)) =>
107 Cow::Owned(format!("#{:02x}{:02x}{:02x}", r, g, b)),
108 HexColorInner::Right(str) => Cow::Borrowed(&str)
109 }
110 }
111}
112
113impl Display for HexColor<'_> {
114 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
115 write!(f, "{}", self.get_hex())
116 }
117}
118
119impl TryFrom<String> for HexColor<'_> {
120 type Error = HexColorError;
121
122 fn try_from(value: String) -> Result<Self, Self::Error> {
123 HexColor::new_hex(value)
124 }
125}
126
127impl From<HexColor<'_>> for String {
128 fn from(color: HexColor<'_>) -> Self {
129 color.to_string()
130 }
131}
132
133impl From<DefaultColor> for Color<'_> {
134 fn from(default_color: DefaultColor) -> Self {
135 Color::Default(default_color)
136 }
137}
138
139impl<'a> From<HexColor<'a>> for Color<'a> {
140 fn from(hex_color: HexColor<'a>) -> Self {
141 Color::Hex(hex_color)
142 }
143}