tinted_builder/scheme/
color.rs1use serde::{Deserialize, Serialize};
2use std::fmt;
3
4use crate::error::TintedBuilderError;
5
6#[derive(Debug, Clone, Deserialize, Serialize)]
7pub struct Color {
8 pub hex: (String, String, String),
9 pub rgb: (u8, u8, u8),
10 pub dec: (f32, f32, f32),
11}
12
13impl Color {
14 pub fn new(hex_color: &str) -> Result<Self, TintedBuilderError> {
21 let hex_full = process_hex_input(hex_color).ok_or(TintedBuilderError::HexInputFormat)?;
22 let hex: (String, String, String) = (
23 hex_full[0..2].to_lowercase(),
24 hex_full[2..4].to_lowercase(),
25 hex_full[4..6].to_lowercase(),
26 );
27 let rgb = hex_to_rgb(&hex)?;
28 let dec: (f32, f32, f32) = (f32::from(rgb.0), f32::from(rgb.1), f32::from(rgb.2));
29
30 Ok(Self { hex, rgb, dec })
31 }
32
33 #[must_use]
34 pub fn to_hex(&self) -> String {
35 format!("{}{}{}", &self.hex.0, &self.hex.1, &self.hex.2)
36 }
37}
38
39impl fmt::Display for Color {
40 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
41 write!(f, "#{}", &self.to_hex())
42 }
43}
44
45fn hex_to_rgb(hex: &(String, String, String)) -> Result<(u8, u8, u8), TintedBuilderError> {
46 let r = u8::from_str_radix(hex.0.as_str(), 16)?;
47 let g = u8::from_str_radix(hex.1.as_str(), 16)?;
48 let b = u8::from_str_radix(hex.2.as_str(), 16)?;
49
50 Ok((r, g, b))
51}
52
53fn process_hex_input(input: &str) -> Option<String> {
54 let hex_str = input.strip_prefix('#').unwrap_or(input);
56
57 match hex_str.len() {
58 3 => {
60 if hex_str.chars().all(|c| c.is_ascii_hexdigit()) {
61 Some(
62 hex_str
63 .chars()
64 .flat_map(|c| std::iter::repeat(c).take(2))
65 .collect(),
66 )
67 } else {
68 None }
70 }
71 6 => {
73 if hex_str.chars().all(|c| c.is_ascii_hexdigit()) {
74 Some(hex_str.to_string())
75 } else {
76 None }
78 }
79 _ => None,
81 }
82}