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) = (
29 f32::from(rgb.0) / 255.0,
30 f32::from(rgb.1) / 255.0,
31 f32::from(rgb.2) / 255.0,
32 );
33
34 Ok(Self { hex, rgb, dec })
35 }
36
37 #[must_use]
38 pub fn to_hex(&self) -> String {
39 format!("{}{}{}", &self.hex.0, &self.hex.1, &self.hex.2)
40 }
41}
42
43impl fmt::Display for Color {
44 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
45 write!(f, "#{}", &self.to_hex())
46 }
47}
48
49fn hex_to_rgb(hex: &(String, String, String)) -> Result<(u8, u8, u8), TintedBuilderError> {
50 let r = u8::from_str_radix(hex.0.as_str(), 16)?;
51 let g = u8::from_str_radix(hex.1.as_str(), 16)?;
52 let b = u8::from_str_radix(hex.2.as_str(), 16)?;
53
54 Ok((r, g, b))
55}
56
57fn process_hex_input(input: &str) -> Option<String> {
58 let hex_str = input.strip_prefix('#').unwrap_or(input);
60
61 match hex_str.len() {
62 3 => {
64 if hex_str.chars().all(|c| c.is_ascii_hexdigit()) {
65 Some(
66 hex_str
67 .chars()
68 .flat_map(|c| std::iter::repeat(c).take(2))
69 .collect(),
70 )
71 } else {
72 None }
74 }
75 6 => {
77 if hex_str.chars().all(|c| c.is_ascii_hexdigit()) {
78 Some(hex_str.to_string())
79 } else {
80 None }
82 }
83 _ => None,
85 }
86}