use std::fmt;
use std::hash::Hash;
use serde::de::Visitor;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use thiserror::Error;
#[derive(Default, Debug, PartialOrd, Eq, Ord, Clone, Copy)]
pub enum Color {
#[default]
Reset,
Rgb(RgbColor),
Named(NamedColor),
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct RgbColor {
pub r: u8,
pub g: u8,
pub b: u8,
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub enum NamedColor {
Black = 0,
DarkBlue,
DarkGreen,
DarkAqua,
DarkRed,
DarkPurple,
Gold,
Gray,
DarkGray,
Blue,
Green,
Aqua,
Red,
LightPurple,
Yellow,
White,
}
#[derive(Debug, Error, PartialEq, PartialOrd, Clone, Copy, Hash, Eq, Ord)]
#[error("invalid color name or hex code")]
pub struct ColorError;
impl Color {
pub const RESET: Self = Self::Reset;
pub const AQUA: Self = Self::Named(NamedColor::Aqua);
pub const BLACK: Self = Self::Named(NamedColor::Black);
pub const BLUE: Self = Self::Named(NamedColor::Blue);
pub const DARK_AQUA: Self = Self::Named(NamedColor::DarkAqua);
pub const DARK_BLUE: Self = Self::Named(NamedColor::DarkBlue);
pub const DARK_GRAY: Self = Self::Named(NamedColor::DarkGray);
pub const DARK_GREEN: Self = Self::Named(NamedColor::DarkGreen);
pub const DARK_PURPLE: Self = Self::Named(NamedColor::DarkPurple);
pub const DARK_RED: Self = Self::Named(NamedColor::DarkRed);
pub const GOLD: Self = Self::Named(NamedColor::Gold);
pub const GRAY: Self = Self::Named(NamedColor::Gray);
pub const GREEN: Self = Self::Named(NamedColor::Green);
pub const LIGHT_PURPLE: Self = Self::Named(NamedColor::LightPurple);
pub const RED: Self = Self::Named(NamedColor::Red);
pub const WHITE: Self = Self::Named(NamedColor::White);
pub const YELLOW: Self = Self::Named(NamedColor::Yellow);
pub const fn rgb(r: u8, g: u8, b: u8) -> Self {
Self::Rgb(RgbColor::new(r, g, b))
}
}
impl RgbColor {
pub const fn new(r: u8, g: u8, b: u8) -> Self {
Self { r, g, b }
}
pub fn to_named_lossy(self) -> NamedColor {
fn squared_distance(c1: RgbColor, c2: RgbColor) -> i32 {
(i32::from(c1.r) - i32::from(c2.r)).pow(2)
+ (i32::from(c1.g) - i32::from(c2.g)).pow(2)
+ (i32::from(c1.b) - i32::from(c2.b)).pow(2)
}
[
NamedColor::Aqua,
NamedColor::Black,
NamedColor::Blue,
NamedColor::DarkAqua,
NamedColor::DarkBlue,
NamedColor::DarkGray,
NamedColor::DarkGreen,
NamedColor::DarkPurple,
NamedColor::DarkRed,
NamedColor::Gold,
NamedColor::Gray,
NamedColor::Green,
NamedColor::LightPurple,
NamedColor::Red,
NamedColor::White,
NamedColor::Yellow,
]
.into_iter()
.min_by_key(|&named| squared_distance(named.into(), self))
.unwrap()
}
}
impl NamedColor {
pub const fn hex_digit(self) -> char {
b"0123456789abcdef"[self as usize] as char
}
pub const fn name(self) -> &'static str {
[
"black",
"dark_blue",
"dark_green",
"dark_aqua",
"dark_red",
"dark_purple",
"gold",
"gray",
"dark_gray",
"blue",
"green",
"aqua",
"red",
"light_purple",
"yellow",
"white",
][self as usize]
}
}
impl PartialEq for Color {
fn eq(&self, other: &Self) -> bool {
match (*self, *other) {
(Self::Reset, Self::Reset) => true,
(Self::Rgb(rgb1), Self::Rgb(rgb2)) => rgb1 == rgb2,
(Self::Named(normal1), Self::Named(normal2)) => normal1 == normal2,
(Self::Rgb(rgb), Self::Named(normal)) | (Self::Named(normal), Self::Rgb(rgb)) => {
rgb == RgbColor::from(normal)
}
(Self::Reset, _) | (_, Self::Reset) => false,
}
}
}
impl Hash for Color {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
match self {
Self::Reset => state.write_u8(0),
Self::Rgb(rgb) => {
state.write_u8(1);
rgb.hash(state);
}
Self::Named(normal) => {
state.write_u8(1);
RgbColor::from(*normal).hash(state);
}
}
}
}
impl From<NamedColor> for RgbColor {
fn from(value: NamedColor) -> Self {
match value {
NamedColor::Aqua => Self::new(85, 255, 255),
NamedColor::Black => Self::new(0, 0, 0),
NamedColor::Blue => Self::new(85, 85, 255),
NamedColor::DarkAqua => Self::new(0, 170, 170),
NamedColor::DarkBlue => Self::new(0, 0, 170),
NamedColor::DarkGray => Self::new(85, 85, 85),
NamedColor::DarkGreen => Self::new(0, 170, 0),
NamedColor::DarkPurple => Self::new(170, 0, 170),
NamedColor::DarkRed => Self::new(170, 0, 0),
NamedColor::Gold => Self::new(255, 170, 0),
NamedColor::Gray => Self::new(170, 170, 170),
NamedColor::Green => Self::new(85, 255, 85),
NamedColor::LightPurple => Self::new(255, 85, 255),
NamedColor::Red => Self::new(255, 85, 85),
NamedColor::White => Self::new(255, 255, 255),
NamedColor::Yellow => Self::new(255, 255, 85),
}
}
}
impl From<RgbColor> for Color {
fn from(value: RgbColor) -> Self {
Self::Rgb(value)
}
}
impl From<NamedColor> for Color {
fn from(value: NamedColor) -> Self {
Self::Named(value)
}
}
impl TryFrom<&str> for Color {
type Error = ColorError;
fn try_from(value: &str) -> Result<Self, Self::Error> {
if value.starts_with('#') {
return Ok(Self::Rgb(RgbColor::try_from(value)?));
}
if value == "reset" {
return Ok(Self::Reset);
}
Ok(Self::Named(NamedColor::try_from(value)?))
}
}
impl TryFrom<&str> for NamedColor {
type Error = ColorError;
fn try_from(value: &str) -> Result<Self, Self::Error> {
match value {
"black" => Ok(NamedColor::Black),
"dark_blue" => Ok(NamedColor::DarkBlue),
"dark_green" => Ok(NamedColor::DarkGreen),
"dark_aqua" => Ok(NamedColor::DarkAqua),
"dark_red" => Ok(NamedColor::DarkRed),
"dark_purple" => Ok(NamedColor::DarkPurple),
"gold" => Ok(NamedColor::Gold),
"gray" => Ok(NamedColor::Gray),
"dark_gray" => Ok(NamedColor::DarkGray),
"blue" => Ok(NamedColor::Blue),
"green" => Ok(NamedColor::Green),
"aqua" => Ok(NamedColor::Aqua),
"red" => Ok(NamedColor::Red),
"light_purple" => Ok(NamedColor::LightPurple),
"yellow" => Ok(NamedColor::Yellow),
"white" => Ok(NamedColor::White),
_ => Err(ColorError),
}
}
}
impl TryFrom<&str> for RgbColor {
type Error = ColorError;
fn try_from(value: &str) -> Result<Self, Self::Error> {
let to_num = |d| match d {
b'0'..=b'9' => Ok(d - b'0'),
b'a'..=b'f' => Ok(d - b'a' + 0xa),
b'A'..=b'F' => Ok(d - b'A' + 0xa),
_ => Err(ColorError),
};
if let &[b'#', r0, r1, g0, g1, b0, b1] = value.as_bytes() {
Ok(RgbColor {
r: to_num(r0)? << 4 | to_num(r1)?,
g: to_num(g0)? << 4 | to_num(g1)?,
b: to_num(b0)? << 4 | to_num(b1)?,
})
} else {
Err(ColorError)
}
}
}
impl Serialize for Color {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
format!("{self}").serialize(serializer)
}
}
impl<'de> Deserialize<'de> for Color {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
deserializer.deserialize_str(ColorVisitor)
}
}
struct ColorVisitor;
impl<'de> Visitor<'de> for ColorVisitor {
type Value = Color;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "a hex color (#rrggbb), a normal color or 'reset'")
}
fn visit_str<E: serde::de::Error>(self, s: &str) -> Result<Self::Value, E> {
Color::try_from(s).map_err(|_| E::custom("invalid color"))
}
}
impl fmt::Display for Color {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Color::Reset => write!(f, "reset"),
Color::Rgb(rgb) => rgb.fmt(f),
Color::Named(normal) => normal.fmt(f),
}
}
}
impl fmt::Display for RgbColor {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "#{:02x}{:02x}{:02x}", self.r, self.g, self.b)
}
}
impl fmt::Display for NamedColor {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.name())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn colors() {
assert_eq!(
Color::try_from("#aBcDeF"),
Ok(RgbColor::new(0xab, 0xcd, 0xef).into())
);
assert_eq!(
Color::try_from("#fFfFfF"),
Ok(RgbColor::new(255, 255, 255).into())
);
assert_eq!(Color::try_from("#000000"), Ok(NamedColor::Black.into()));
assert_eq!(Color::try_from("red"), Ok(NamedColor::Red.into()));
assert_eq!(Color::try_from("blue"), Ok(NamedColor::Blue.into()));
assert!(Color::try_from("#ffTf00").is_err());
assert!(Color::try_from("#ffš00").is_err());
assert!(Color::try_from("#00000000").is_err());
assert!(Color::try_from("#").is_err());
}
}