rust-color 0.1.0

rust library for handling colors
Documentation
use std::fmt::{Display, Error};

pub trait Color: Copy {
    type Type;
    fn complement(&self) -> Self::Type;

    fn get_rgba(&self) -> RgbaColorType;
    fn get_hsla(&self) -> HslaColorType;
    fn set_opacity(&mut self, a: f64) -> Self::Type;
}

#[derive(Debug, Clone, Copy, PartialEq)]
pub struct RgbaColorType {
    pub r: f64,
    pub g: f64,
    pub b: f64,
    pub a: f64,
}

#[derive(Debug, Clone, Copy, PartialEq)]
pub struct HslaColorType {
    pub h: f64,
    pub s: f64,
    pub l: f64,
    pub a: f64,
}

impl RgbaColorType {
    pub fn new(r: f64, g: f64, b: f64) -> RgbaColorType {
        RgbaColorType {
            r: r,
            g: g,
            b: b,
            a: 1.0,
        }
    }
}

impl HslaColorType {
    pub fn new(h: f64, s: f64, l: f64) -> HslaColorType {
        let mut a = HslaColorType {
            h: h,
            s: s,
            l: l,
            a: 1.0,
        };
        a.validate();
        a
    }

    fn validate(&mut self) {
        self.h = self.h % 360.0;
        if self.h < 0.0 {
            self.h += 360.0
        };

        if self.s < 0.0 {
            self.s = 0.0
        };
        if self.s > 100.0 {
            self.s = 100.0
        };
        if self.l < 0.0 {
            self.l = 0.0
        };
        if self.l > 100.0 {
            self.l = 100.0
        };
    }
}
fn hue2rgb(p: f64, q: f64, t: f64) -> f64 {
    let mut t = t;
    if t < 0.0 {
        t += 1.0;
    }
    if t > 1.0 {
        t -= 1.0;
    }
    if t < 1.0 / 6.0 {
        p + (q - p) * 6.0 * t
    } else if t < 1.0 / 2.0 {
        q
    } else if t < 2.0 / 3.0 {
        p + (q - p) * (2.0 / 3.0 - t) * 6.0
    } else {
        p
    }
}

impl From<[f64; 3]> for RgbaColorType {
    fn from(c: [f64; 3]) -> RgbaColorType {
        RgbaColorType::new(c[0], c[1], c[2])
    }
}
impl From<[u8; 3]> for RgbaColorType {
    fn from(c: [u8; 3]) -> RgbaColorType {
        RgbaColorType::new(
            c[0] as f64 / 255.0,
            c[1] as f64 / 255.0,
            c[2] as f64 / 255.0,
        )
    }
}

impl Into<[u8; 3]> for RgbaColorType {
    fn into(self) -> [u8; 3] {
        [
            (self.r * 255.0).round() as u8,
            (self.g * 255.0).round() as u8,
            (self.b * 255.0).round() as u8,
        ]
    }
}
impl Into<[u8; 4]> for RgbaColorType {
    fn into(self) -> [u8; 4] {
        [
            (self.r * 255.0).round() as u8,
            (self.g * 255.0).round() as u8,
            (self.b * 255.0).round() as u8,
            (self.a * 255.0).round() as u8,
        ]
    }
}
impl Into<[f64; 4]> for RgbaColorType {
    fn into(self) -> [f64; 4] {
        [self.r, self.g, self.b, self.a]
    }
}

impl Into<[f32; 4]> for RgbaColorType {
    fn into(self) -> [f32; 4] {
        [self.r as f32, self.g as f32, self.b as f32, self.a as f32]
    }
}
impl From<HslaColorType> for RgbaColorType {
    fn from(c: HslaColorType) -> RgbaColorType {
        let h: f64 = c.h / 360.0;
        let s: f64 = c.s / 100.0;
        let l: f64 = c.l / 100.0;

        let mut r: f64 = l;
        let mut g: f64 = l;
        let mut b: f64 = l;

        if s != 0.0 {
            let q = if l < 0.5 {
                l * (1.0 + s)
            } else {
                l + s - l * s
            };
            let p = 2.0 * l - q;
            r = hue2rgb(p, q, h + 1.0 / 3.0);
            g = hue2rgb(p, q, h);
            b = hue2rgb(p, q, h - 1.0 / 3.0);
        }
        RgbaColorType::new(r, g, b)
    }
}
impl From<RgbaColorType> for HslaColorType {
    fn from(c: RgbaColorType) -> HslaColorType {
        let mut h: f64;
        let mut s: f64;
        let mut l: f64;
        let r: f64 = c.r;
        let g: f64 = c.g;
        let b: f64 = c.b;

        let max: f64 = c.r.max(c.g).max(c.b);
        let min: f64 = c.r.min(c.g).min(c.b);
        h = (max + min) / 2.0;
        s = h;
        l = h;

        let error = 0.00001;
        if (max - min).abs() < error {
            // achromatic

            h = 0.0;
            s = 0.0;
        } else {
            let d = max - min;
            s = if l > 0.5 {
                d / (2.0 - max - min)
            } else {
                d / (max + min)
            };
            if (max - r).abs() < error {
                h = (g - b) / d + (if g < b { 6.0 } else { 0.0 })
            } else if (max - g).abs() < error {
                h = (b - r) / d + 2.0
            } else if (max - b).abs() < error {
                h = (r - g) / d + 4.0;
            }

            h /= 6.0;
        }
        h *= 360.0;
        s *= 100.0;
        l *= 100.0;

        HslaColorType::new(h, s, l)
    }
}
impl Display for RgbaColorType {
    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::result::Result<(), std::fmt::Error> {
        fmt.write_str(format!("RGB({:?}, {:?}, {:?})", self.r, self.g, self.b).as_str())
    }
}
impl Color for RgbaColorType {
    type Type = RgbaColorType;

    fn set_opacity(&mut self, a:  f64) -> Self::Type {
        self.a = a;
        *self
    }

    fn complement(&self) -> Self::Type {
        RgbaColorType{r:1.0 - self.r, g: 1.0 - self.g, b: 1.0 - self.b, a: self.a}
    }
    fn get_hsla(&self) -> HslaColorType {
        (*self).into()
    }
    fn get_rgba(&self) -> RgbaColorType {
        *self
    }
}

mod tests {
    use super::*;
    #[test]
    fn test_new() {
        let c = RgbaColorType::new(0.1, 0.2, 0.3);
        assert_eq!(
            c,
            RgbaColorType {
                r: 0.1,
                g: 0.2,
                b: 0.3,
                a: 1.0
            }
        );
    }

    #[test]
    fn test_complement() {
        let c = RgbaColorType::new(0.1, 0.2, 0.3);
        assert_eq!(
            c.complement(),
            RgbaColorType {
                r: 0.9,
                g: 0.8,
                b: 0.7,
                a: 1.0
            }
        );
    }

    #[test]
    fn test_from_array() {
        let a: RgbaColorType = RgbaColorType::from([1.0, 0.5, 0.0]);
        assert_eq!(
            a,
            RgbaColorType {
                r: 1.0,
                g: 0.5,
                b: 0.0,
                a: 1.0
            }
        );
    }

    #[test]
    fn test_from_bytes() {
        let a = RgbaColorType::from([255, 255, 255]);
        assert_eq!(
            a,
            RgbaColorType {
                r: 1.0,
                g: 1.0,
                b: 1.0,
                a: 1.0
            }
        );
        let b = RgbaColorType::from([128, 128, 128]);
        assert!(
            error(
                b,
                RgbaColorType {
                    r: 0.5,
                    g: 0.5,
                    b: 0.5,
                    a: 1.0
                }
            ) < 0.005
        );
    }
    #[test]
    fn test_into() {
        let a = RgbaColorType::new(1.0, 0.5, 0.25);
        let arr: [u8; 3] = a.into();
        assert_eq!(arr, [255, 128, 64]);
    }
    #[test]
    fn test_conv() {
        let a = RgbaColorType::new(1.0, 0.5, 0.25);
        let b: HslaColorType = a.into();
        assert_eq!(a, b.into());
    }
    fn error(c: RgbaColorType, d: RgbaColorType) -> f64 {
        let e = c.r - d.r + c.g - d.g + c.b - d.b + c.a - d.a;
        e / 4.0
    }
}