1use std::fmt;
4use std::hash::Hash;
5
6use bitfield_struct::bitfield;
7use serde::de::Visitor;
8use serde::{Deserialize, Deserializer, Serialize, Serializer};
9use thiserror::Error;
10
11#[derive(Default, Debug, PartialOrd, Eq, Ord, Clone, Copy)]
13pub enum Color {
14 #[default]
18 Reset,
19 Rgb(RgbColor),
21 Named(NamedColor),
23}
24
25#[bitfield(u32)]
27#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
28pub struct RgbColor {
29 _padding: u8,
30 pub r: u8,
32 pub g: u8,
34 pub b: u8,
36}
37
38#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
40pub enum NamedColor {
41 Black = 0,
43 DarkBlue,
45 DarkGreen,
47 DarkAqua,
49 DarkRed,
51 DarkPurple,
53 Gold,
55 Gray,
57 DarkGray,
59 Blue,
61 Green,
63 Aqua,
65 Red,
67 LightPurple,
69 Yellow,
71 White,
73}
74
75#[derive(Debug, Error, PartialEq, PartialOrd, Clone, Copy, Hash, Eq, Ord)]
77#[error("invalid color name or hex code")]
78pub struct ColorError;
79
80impl Color {
81 pub const RESET: Self = Self::Reset;
82 pub const AQUA: Self = Self::Named(NamedColor::Aqua);
83 pub const BLACK: Self = Self::Named(NamedColor::Black);
84 pub const BLUE: Self = Self::Named(NamedColor::Blue);
85 pub const DARK_AQUA: Self = Self::Named(NamedColor::DarkAqua);
86 pub const DARK_BLUE: Self = Self::Named(NamedColor::DarkBlue);
87 pub const DARK_GRAY: Self = Self::Named(NamedColor::DarkGray);
88 pub const DARK_GREEN: Self = Self::Named(NamedColor::DarkGreen);
89 pub const DARK_PURPLE: Self = Self::Named(NamedColor::DarkPurple);
90 pub const DARK_RED: Self = Self::Named(NamedColor::DarkRed);
91 pub const GOLD: Self = Self::Named(NamedColor::Gold);
92 pub const GRAY: Self = Self::Named(NamedColor::Gray);
93 pub const GREEN: Self = Self::Named(NamedColor::Green);
94 pub const LIGHT_PURPLE: Self = Self::Named(NamedColor::LightPurple);
95 pub const RED: Self = Self::Named(NamedColor::Red);
96 pub const WHITE: Self = Self::Named(NamedColor::White);
97 pub const YELLOW: Self = Self::Named(NamedColor::Yellow);
98
99 pub const fn rgb(r: u8, g: u8, b: u8) -> Self {
101 Self::Rgb(RgbColor::rgb(r, g, b))
102 }
103}
104
105impl RgbColor {
106 pub const fn rgb(r: u8, g: u8, b: u8) -> Self {
108 Self::new().with_r(r).with_g(g).with_b(b)
109 }
110 pub fn to_named_lossy(self) -> NamedColor {
112 fn squared_distance(c1: RgbColor, c2: RgbColor) -> i32 {
114 (i32::from(c1.r()) - i32::from(c2.r())).pow(2)
115 + (i32::from(c1.g()) - i32::from(c2.g())).pow(2)
116 + (i32::from(c1.b()) - i32::from(c2.b())).pow(2)
117 }
118
119 [
120 NamedColor::Aqua,
121 NamedColor::Black,
122 NamedColor::Blue,
123 NamedColor::DarkAqua,
124 NamedColor::DarkBlue,
125 NamedColor::DarkGray,
126 NamedColor::DarkGreen,
127 NamedColor::DarkPurple,
128 NamedColor::DarkRed,
129 NamedColor::Gold,
130 NamedColor::Gray,
131 NamedColor::Green,
132 NamedColor::LightPurple,
133 NamedColor::Red,
134 NamedColor::White,
135 NamedColor::Yellow,
136 ]
137 .into_iter()
138 .min_by_key(|&named| squared_distance(named.into(), self))
139 .unwrap()
140 }
141}
142
143impl NamedColor {
144 pub const fn hex_digit(self) -> char {
146 b"0123456789abcdef"[self as usize] as char
147 }
148 pub const fn name(self) -> &'static str {
150 [
151 "black",
152 "dark_blue",
153 "dark_green",
154 "dark_aqua",
155 "dark_red",
156 "dark_purple",
157 "gold",
158 "gray",
159 "dark_gray",
160 "blue",
161 "green",
162 "aqua",
163 "red",
164 "light_purple",
165 "yellow",
166 "white",
167 ][self as usize]
168 }
169}
170
171impl PartialEq for Color {
172 fn eq(&self, other: &Self) -> bool {
173 match (*self, *other) {
174 (Self::Reset, Self::Reset) => true,
175 (Self::Rgb(rgb1), Self::Rgb(rgb2)) => rgb1 == rgb2,
176 (Self::Named(normal1), Self::Named(normal2)) => normal1 == normal2,
177 (Self::Rgb(rgb), Self::Named(normal)) | (Self::Named(normal), Self::Rgb(rgb)) => {
178 rgb == RgbColor::from(normal)
179 }
180 (Self::Reset, _) | (_, Self::Reset) => false,
181 }
182 }
183}
184
185impl Hash for Color {
186 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
187 match self {
188 Self::Reset => state.write_u8(0),
189 Self::Rgb(rgb) => {
190 state.write_u8(1);
191 rgb.hash(state);
192 }
193 Self::Named(normal) => {
194 state.write_u8(1);
195 RgbColor::from(*normal).hash(state);
196 }
197 }
198 }
199}
200
201impl From<NamedColor> for RgbColor {
202 fn from(value: NamedColor) -> Self {
203 match value {
204 NamedColor::Aqua => Self::rgb(85, 255, 255),
205 NamedColor::Black => Self::rgb(0, 0, 0),
206 NamedColor::Blue => Self::rgb(85, 85, 255),
207 NamedColor::DarkAqua => Self::rgb(0, 170, 170),
208 NamedColor::DarkBlue => Self::rgb(0, 0, 170),
209 NamedColor::DarkGray => Self::rgb(85, 85, 85),
210 NamedColor::DarkGreen => Self::rgb(0, 170, 0),
211 NamedColor::DarkPurple => Self::rgb(170, 0, 170),
212 NamedColor::DarkRed => Self::rgb(170, 0, 0),
213 NamedColor::Gold => Self::rgb(255, 170, 0),
214 NamedColor::Gray => Self::rgb(170, 170, 170),
215 NamedColor::Green => Self::rgb(85, 255, 85),
216 NamedColor::LightPurple => Self::rgb(255, 85, 255),
217 NamedColor::Red => Self::rgb(255, 85, 85),
218 NamedColor::White => Self::rgb(255, 255, 255),
219 NamedColor::Yellow => Self::rgb(255, 255, 85),
220 }
221 }
222}
223
224impl From<RgbColor> for Color {
225 fn from(value: RgbColor) -> Self {
226 Self::Rgb(value)
227 }
228}
229
230impl From<NamedColor> for Color {
231 fn from(value: NamedColor) -> Self {
232 Self::Named(value)
233 }
234}
235
236impl TryFrom<&str> for Color {
237 type Error = ColorError;
238
239 fn try_from(value: &str) -> Result<Self, Self::Error> {
240 if value.starts_with('#') {
241 return Ok(Self::Rgb(RgbColor::try_from(value)?));
242 }
243
244 if value == "reset" {
245 return Ok(Self::Reset);
246 }
247
248 Ok(Self::Named(NamedColor::try_from(value)?))
249 }
250}
251
252impl TryFrom<&str> for NamedColor {
253 type Error = ColorError;
254
255 fn try_from(value: &str) -> Result<Self, Self::Error> {
256 match value {
257 "black" => Ok(NamedColor::Black),
258 "dark_blue" => Ok(NamedColor::DarkBlue),
259 "dark_green" => Ok(NamedColor::DarkGreen),
260 "dark_aqua" => Ok(NamedColor::DarkAqua),
261 "dark_red" => Ok(NamedColor::DarkRed),
262 "dark_purple" => Ok(NamedColor::DarkPurple),
263 "gold" => Ok(NamedColor::Gold),
264 "gray" => Ok(NamedColor::Gray),
265 "dark_gray" => Ok(NamedColor::DarkGray),
266 "blue" => Ok(NamedColor::Blue),
267 "green" => Ok(NamedColor::Green),
268 "aqua" => Ok(NamedColor::Aqua),
269 "red" => Ok(NamedColor::Red),
270 "light_purple" => Ok(NamedColor::LightPurple),
271 "yellow" => Ok(NamedColor::Yellow),
272 "white" => Ok(NamedColor::White),
273 _ => Err(ColorError),
274 }
275 }
276}
277
278impl TryFrom<&str> for RgbColor {
279 type Error = ColorError;
280
281 fn try_from(value: &str) -> Result<Self, Self::Error> {
282 let to_num = |d| match d {
283 b'0'..=b'9' => Ok(d - b'0'),
284 b'a'..=b'f' => Ok(d - b'a' + 0xa),
285 b'A'..=b'F' => Ok(d - b'A' + 0xa),
286 _ => Err(ColorError),
287 };
288
289 if let &[b'#', r0, r1, g0, g1, b0, b1] = value.as_bytes() {
290 Ok(RgbColor::rgb(
291 to_num(r0)? << 4 | to_num(r1)?,
292 to_num(g0)? << 4 | to_num(g1)?,
293 to_num(b0)? << 4 | to_num(b1)?,
294 ))
295 } else {
296 Err(ColorError)
297 }
298 }
299}
300
301impl Serialize for Color {
302 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
303 format!("{self}").serialize(serializer)
304 }
305}
306
307impl<'de> Deserialize<'de> for Color {
308 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
309 deserializer.deserialize_str(ColorVisitor)
310 }
311}
312
313struct ColorVisitor;
314
315impl Visitor<'_> for ColorVisitor {
316 type Value = Color;
317
318 fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
319 write!(f, "a hex color (#rrggbb), a normal color or 'reset'")
320 }
321
322 fn visit_str<E: serde::de::Error>(self, s: &str) -> Result<Self::Value, E> {
323 Color::try_from(s).map_err(|_| E::custom("invalid color"))
324 }
325}
326
327impl fmt::Display for Color {
328 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
329 match self {
330 Color::Reset => write!(f, "reset"),
331 Color::Rgb(rgb) => rgb.fmt(f),
332 Color::Named(normal) => normal.fmt(f),
333 }
334 }
335}
336
337impl fmt::Display for RgbColor {
338 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
339 write!(f, "#{:02x}{:02x}{:02x}", self.r(), self.g(), self.b())
340 }
341}
342
343impl fmt::Display for NamedColor {
344 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
345 write!(f, "{}", self.name())
346 }
347}
348
349#[cfg(test)]
350mod tests {
351 use super::*;
352
353 #[test]
354 fn colors() {
355 assert_eq!(
356 Color::try_from("#aBcDeF"),
357 Ok(RgbColor::rgb(0xab, 0xcd, 0xef).into())
358 );
359 assert_eq!(
360 Color::try_from("#fFfFfF"),
361 Ok(RgbColor::rgb(255, 255, 255).into())
362 );
363 assert_eq!(Color::try_from("#000000"), Ok(NamedColor::Black.into()));
364 assert_eq!(Color::try_from("red"), Ok(NamedColor::Red.into()));
365 assert_eq!(Color::try_from("blue"), Ok(NamedColor::Blue.into()));
366 assert!(Color::try_from("#ffTf00").is_err());
367 assert!(Color::try_from("#ffš00").is_err());
368 assert!(Color::try_from("#00000000").is_err());
369 assert!(Color::try_from("#").is_err());
370 }
371}