easy_color 0.1.0

This is a very simple and easy-to-use color conversion tool that can easily convert colors between Hex, RGB, RGBA, HSL, HSLA, HSV, and CMYK. And each type has its unique API, such as RGB can set color channels, RGBA can set transparency, HSL can set hue, saturation, and brightness, and so on.
Documentation
use crate::common::{rgb_to_hsl, rgba_to_hsla};
use crate::traits::Color;
use crate::{ColorError, Hex, CMYK, HSL, HSV, RGB, RGBA};
use std::fmt::{Display, Formatter};
use std::ops::{Deref, DerefMut};

/// HSLA can be parsed from a string in the format "hsla(h, s%, l%, a)" or from a tuple (h,s,l,a).
/// * h:u32 - Hue(0~360)
/// * s:u32 - saturation(0~100)
/// * l:u32 - lightness(0~100)
/// * a:f32 - alpha(0~1)
/// ### example
/// ```rust
/// use easy_color::{RGBA, HSLA};
/// let mut hsla:HSLA = "hsla(262,85%,79%, 0.7)".try_into().unwrap();
/// hsla.set_alpha(0.5);
/// assert_eq!(hsla.to_string(), "hsla(262,85%,79%,0.50)");
///
/// let hsla:HSLA = (125,60,75,0.6).try_into().unwrap();
/// let rgba:RGBA = hsla.into();
/// assert_eq!(rgba.to_string(), "rgba(153,229,159,0.60)");
/// ```
#[derive(Debug, Default, PartialEq, Clone, Copy)]
pub struct HSLA {
    pub(crate) hsl: HSL,
    pub(crate) a: f32,
}

impl TryFrom<&str> for HSLA {
    type Error = ColorError;
    fn try_from(hsla_str: &str) -> Result<Self, Self::Error> {
        let mut color = hsla_str.trim().to_lowercase();
        if color.starts_with("hsla(") && color.ends_with(')') {
            let mut val = vec![];
            let mut alpha = None;
            color = color.replace("hsla(", "").replace(")", "");
            let tmp = color.split(',').collect::<Vec<_>>();
            if tmp.len() == 4 {
                for (idx, s) in tmp.iter().enumerate() {
                    if idx == 3 {
                        alpha = s.trim().parse::<f32>().ok();
                    } else {
                        if let Ok(v) = s.trim().trim_end_matches('%').parse::<u32>() {
                            val.push(v);
                        }
                    }
                }
            }
            if val.len() == 3 && alpha.is_some() {
                return (val[0], val[1], val[2], alpha.unwrap()).try_into();
            }
        }
        Err(ColorError::FormatErr(format!(
            "HSLA: {} format error!",
            hsla_str
        )))
    }
}

impl TryFrom<(u32, u32, u32, f32)> for HSLA {
    type Error = ColorError;
    fn try_from(value: (u32, u32, u32, f32)) -> Result<Self, Self::Error> {
        return if !(0..=360).contains(&value.0)
            || !(0..=100).contains(&value.1)
            || !(0..=100).contains(&value.2)
            || !(0.0..=1.0).contains(&value.3)
        {
            Err(ColorError::ValueErr(format!("HSLA: args ({},{},{},{}) value error. first value must between 0~360, second and third must between 0~100, and last one must between 0~1", value.0, value.1, value.2, value.3)))
        } else {
            let hsl = HSL {
                h: value.0,
                s: value.1,
                l: value.2,
            };
            Ok(HSLA { hsl, a: value.3 })
        };
    }
}
impl From<Hex> for HSLA {
    fn from(hex: Hex) -> Self {
        let rgba: RGBA = hex.into();
        rgba.into()
    }
}

impl From<RGB> for HSLA {
    fn from(rgb: RGB) -> Self {
        let RGB { r, g, b } = rgb;
        let (h, s, l) = rgb_to_hsl(r, g, b);
        let hsl = HSL { h, s, l };
        Self { hsl, a: 1.0 }
    }
}

impl From<RGBA> for HSLA {
    fn from(rgba: RGBA) -> Self {
        let RGBA { rgb, a } = rgba;
        let RGB { r, g, b } = rgb;
        let (h, s, l, a) = rgba_to_hsla(r, g, b, a);
        let hsl = HSL { h, s, l };
        Self { hsl, a }
    }
}

impl From<HSL> for HSLA {
    fn from(hsl: HSL) -> Self {
        Self {
            hsl: hsl.clone(),
            a: 1.0,
        }
    }
}

impl From<HSV> for HSLA {
    fn from(hsv: HSV) -> Self {
        let rgb: RGB = hsv.into();
        rgb.into()
    }
}

impl From<CMYK> for HSLA {
    fn from(cmyk: CMYK) -> Self {
        let rgb: RGB = cmyk.into();
        rgb.into()
    }
}

impl Deref for HSLA {
    type Target = HSL;
    fn deref(&self) -> &Self::Target {
        &self.hsl
    }
}

impl DerefMut for HSLA {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.hsl
    }
}

impl Display for HSLA {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        let HSL { h, s, l } = self.hsl;
        write!(f, "hsla({},{}%,{}%,{:.2})", h, s, l, self.a)
    }
}
impl HSLA {
    pub fn alpha(&self) -> f32 {
        self.a
    }

    pub fn set_alpha(&mut self, alpha: f32) -> &mut Self {
        self.a = alpha.max(0.0).min(1.0);
        self
    }
}

impl Color for HSLA {
    fn is_dark(&self) -> bool {
        let rgb = RGB::from(*self);
        rgb.is_dark()
    }

    fn is_light(&self) -> bool {
        !self.is_dark()
    }
}