168 lines
4.3 KiB
Rust
168 lines
4.3 KiB
Rust
|
use std::fmt;
|
||
|
use std::ops::MulAssign;
|
||
|
use std::str::FromStr;
|
||
|
use std::{convert::TryFrom, ops::Mul};
|
||
|
|
||
|
use serde::{Deserialize, Serialize};
|
||
|
|
||
|
/// `TextColor` describes the color of the text
|
||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
|
||
|
pub enum Color {
|
||
|
White,
|
||
|
Black,
|
||
|
Red,
|
||
|
Blue,
|
||
|
Green,
|
||
|
Orange,
|
||
|
Yellow,
|
||
|
Custom(u8, u8, u8),
|
||
|
}
|
||
|
|
||
|
impl Into<[f32; 3]> for Color {
|
||
|
fn into(self) -> [f32; 3] {
|
||
|
match self {
|
||
|
Color::White => [1.0, 1.0, 1.0],
|
||
|
Color::Black => [0.0, 0.0, 0.0],
|
||
|
Color::Red => [1.0, 0.0, 0.0],
|
||
|
Color::Blue => [0.0, 0.0, 1.0],
|
||
|
Color::Green => [0.0, 1.0, 0.0],
|
||
|
Color::Orange => [1.0, 0.65, 0.0],
|
||
|
Color::Yellow => [1.0, 1.0, 0.0],
|
||
|
Color::Custom(r, g, b) => [r as f32 / 255.0, g as f32 / 255.0, b as f32 / 255.0],
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl Into<[u8; 3]> for Color {
|
||
|
fn into(self) -> [u8; 3] {
|
||
|
match self {
|
||
|
Color::White => [255, 255, 255],
|
||
|
Color::Black => [0, 0, 0],
|
||
|
Color::Red => [255, 0, 0],
|
||
|
Color::Blue => [0, 0, 255],
|
||
|
Color::Green => [0, 255, 0],
|
||
|
Color::Orange => [255, 166, 0],
|
||
|
Color::Yellow => [255, 255, 0],
|
||
|
Color::Custom(r, g, b) => [r, g, b],
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl Mul<f32> for Color {
|
||
|
type Output = Self;
|
||
|
|
||
|
fn mul(self, rhs: f32) -> Self::Output {
|
||
|
let values: [f32; 3] = self.into();
|
||
|
|
||
|
let r = (values[0] * rhs).min(1.0);
|
||
|
let g = (values[1] * rhs).min(1.0);
|
||
|
let b = (values[2] * rhs).min(1.0);
|
||
|
|
||
|
Color::from([r, g, b])
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl MulAssign<f32> for Color {
|
||
|
fn mul_assign(&mut self, rhs: f32) {
|
||
|
*self = *self * rhs;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl From<[u8; 3]> for Color {
|
||
|
fn from(array: [u8; 3]) -> Self {
|
||
|
Self::Custom(array[0], array[1], array[2])
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl From<[f32; 3]> for Color {
|
||
|
fn from(array: [f32; 3]) -> Self {
|
||
|
Self::Custom(
|
||
|
(array[0] * 255.0).min(255.0) as u8,
|
||
|
(array[1] * 255.0).min(255.0) as u8,
|
||
|
(array[2] * 255.0).min(255.0) as u8,
|
||
|
)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl TryFrom<&str> for Color {
|
||
|
type Error = anyhow::Error;
|
||
|
|
||
|
fn try_from(text: &str) -> Result<Color, Self::Error> {
|
||
|
// check if color is a hex value
|
||
|
if text.starts_with("#") {
|
||
|
let without_prefix = text.trim_start_matches("#");
|
||
|
|
||
|
if without_prefix.len() != 6 {
|
||
|
return Err(anyhow::Error::msg(format!(
|
||
|
"Hex color format has wrong format: {}",
|
||
|
without_prefix
|
||
|
)));
|
||
|
}
|
||
|
|
||
|
let r = u8::from_str_radix(&without_prefix[0..2], 16)?;
|
||
|
let g = u8::from_str_radix(&without_prefix[2..4], 16)?;
|
||
|
let b = u8::from_str_radix(&without_prefix[4..6], 16)?;
|
||
|
|
||
|
return Ok(Color::Custom(r, g, b));
|
||
|
}
|
||
|
|
||
|
match text {
|
||
|
"white" => Ok(Color::White),
|
||
|
"black" => Ok(Color::Black),
|
||
|
"red" => Ok(Color::Red),
|
||
|
"blue" => Ok(Color::Blue),
|
||
|
"green" => Ok(Color::Green),
|
||
|
"orange" => Ok(Color::Orange),
|
||
|
"yellow" => Ok(Color::Yellow),
|
||
|
_ => Err(anyhow::Error::msg(format!("Unknown color: {}", text))),
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl fmt::Display for Color {
|
||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||
|
match self {
|
||
|
Color::White => write!(f, "white"),
|
||
|
Color::Black => write!(f, "black"),
|
||
|
Color::Red => write!(f, "red"),
|
||
|
Color::Blue => write!(f, "blue"),
|
||
|
Color::Green => write!(f, "green"),
|
||
|
Color::Orange => write!(f, "orange"),
|
||
|
Color::Yellow => write!(f, "yellow"),
|
||
|
Color::Custom(r, g, b) => write!(f, "#{:X?}{:X?}{:X?}", r, g, b),
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl FromStr for Color {
|
||
|
type Err = anyhow::Error;
|
||
|
|
||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||
|
Self::try_from(s)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl Default for Color {
|
||
|
fn default() -> Self {
|
||
|
Self::Black
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#[test]
|
||
|
fn simple_color_conversion() {
|
||
|
let color = Color::try_from("white").unwrap();
|
||
|
|
||
|
assert_eq!(color, Color::White);
|
||
|
}
|
||
|
|
||
|
#[test]
|
||
|
fn hex_color_conversion() {
|
||
|
let color = Color::try_from("#FFFFFF").unwrap();
|
||
|
|
||
|
assert_eq!(color, Color::Custom(255, 255, 255));
|
||
|
|
||
|
let string = format!("{}", color);
|
||
|
|
||
|
assert_eq!(string, "#FFFFFF");
|
||
|
}
|