utilities/src/color.rs

209 lines
5.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 Color {
pub fn r(&self) -> u8 {
match self {
Color::White => 255,
Color::Black => 0,
Color::Red => 255,
Color::Blue => 0,
Color::Green => 0,
Color::Orange => 255,
Color::Yellow => 255,
Color::Custom(r, _g, _b) => *r,
}
}
pub fn g(&self) -> u8 {
match self {
Color::White => 255,
Color::Black => 0,
Color::Red => 0,
Color::Blue => 0,
Color::Green => 255,
Color::Orange => 166,
Color::Yellow => 255,
Color::Custom(_r, g, _b) => *g,
}
}
pub fn b(&self) -> u8 {
match self {
Color::White => 255,
Color::Black => 0,
Color::Red => 0,
Color::Blue => 255,
Color::Green => 0,
Color::Orange => 0,
Color::Yellow => 0,
Color::Custom(_r, _g, b) => *b,
}
}
}
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");
}