use std::num::ParseIntError;
use crate::constants::HEX_PATTERN;
use crate::error::ColorConversionError;
use crate::models::{HEX, HSL, HSV, RGB};
pub fn hex_to_rgb(hex_color: &HEX) -> Result<RGB, ColorConversionError> {
let mut normalized: String = hex_color.normalized();
if normalized.len() == 3 {
normalized = normalized.chars().flat_map(|c: char| [c, c]).collect();
}
if !HEX_PATTERN.is_match(&normalized) {
return Err(ColorConversionError::new("hex", "rgb", &hex_color.value));
}
let r: u8 = u8::from_str_radix(&normalized[0..2], 16).map_err(|_: ParseIntError| ColorConversionError::new("hex", "rgb", &hex_color.value))?;
let g: u8 = u8::from_str_radix(&normalized[2..4], 16).map_err(|_: ParseIntError| ColorConversionError::new("hex", "rgb", &hex_color.value))?;
let b: u8 = u8::from_str_radix(&normalized[4..6], 16).map_err(|_: ParseIntError| ColorConversionError::new("hex", "rgb", &hex_color.value))?;
Ok(RGB::new(r, g, b))
}
pub fn hsl_to_rgb(hsl: &HSL) -> Result<RGB, ColorConversionError> {
let (r, g, b): (f32, f32, f32) = hsl_to_rgb_float(hsl.h, hsl.s, hsl.l);
Ok(RGB::new((r * 255.0).round() as u8, (g * 255.0).round() as u8, (b * 255.0).round() as u8))
}
pub fn hsv_to_rgb(hsv: &HSV) -> Result<RGB, ColorConversionError> {
let (r, g, b): (f32, f32, f32) = hsv_to_rgb_float(hsv.h, hsv.s, hsv.v);
Ok(RGB::new((r * 255.0).round() as u8, (g * 255.0).round() as u8, (b * 255.0).round() as u8))
}
fn hsl_to_rgb_float(h: f32, s: f32, l: f32) -> (f32, f32, f32) {
if s == 0.0 {
return (l, l, l);
}
let q: f32 = if l < 0.5 {
l * (1.0 + s)
} else {
l + s - l * s
};
let p: f32 = 2.0 * l - q;
let r: f32 = hue_to_rgb(p, q, h + 1.0 / 3.0);
let g: f32 = hue_to_rgb(p, q, h);
let b: f32 = hue_to_rgb(p, q, h - 1.0 / 3.0);
(r, g, b)
}
fn hue_to_rgb(p: f32, q: f32, mut t: f32) -> f32 {
if t < 0.0 {
t += 1.0;
}
if t > 1.0 {
t -= 1.0;
}
if t < 1.0 / 6.0 {
return p + (q - p) * 6.0 * t;
}
if t < 1.0 / 2.0 {
return q;
}
if t < 2.0 / 3.0 {
return p + (q - p) * (2.0 / 3.0 - t) * 6.0;
}
p
}
fn hsv_to_rgb_float(h: f32, s: f32, v: f32) -> (f32, f32, f32) {
let s: f32 = s / 100.0;
let v: f32 = v / 100.0;
let h: f32 = h / 60.0;
let c: f32 = v * s;
let x: f32 = c * (1.0 - ((h % 2.0) - 1.0).abs());
let m: f32 = v - c;
let (r, g, b): (f32, f32, f32) = match h as i32 {
0 => (c, x, 0.0),
1 => (x, c, 0.0),
2 => (0.0, c, x),
3 => (0.0, x, c),
4 => (x, 0.0, c),
_ => (c, 0.0, x)
};
(r + m, g + m, b + m)
}