coloriz 0.2.0

A simple library for colorful temrinal
Documentation
use crate::{ANSIColorCode, TargetGround, HSL};
use std::{f32, fmt, ops};

/// [RGB] color
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RGB {
    /// Red
    pub r: u8,
    /// Green
    pub g: u8,
    /// Blue
    pub b: u8,
}

impl RGB {
    /// Creates a new [RGB] color
    #[inline]
    pub const fn new(r: u8, g: u8, b: u8) -> Self {
        Self { r, g, b }
    }

    /// Creates a new [RGB] color with a hex code
    #[inline]
    pub const fn from_hex(hex: u32) -> Self {
        Self::new((hex >> 16) as u8, (hex >> 8) as u8, hex as u8)
    }

    /// Creates a new [RGB] color with three [f32] values
    pub fn from_f32(r: f32, g: f32, b: f32) -> Self {
        Self::new(
            (r.clamp(0.0, 1.0) * 255.0) as u8,
            (g.clamp(0.0, 1.0) * 255.0) as u8,
            (b.clamp(0.0, 1.0) * 255.0) as u8,
        )
    }

    /// Creates a grayscale [RGB] color
    #[inline]
    pub const fn gray(x: u8) -> Self {
        Self::new(x, x, x)
    }

    /// Creates a grayscale [RGB] color with a [f32] value
    pub fn gray_f32(x: f32) -> Self {
        Self::from_f32(x, x, x)
    }

    /// Creates a new [RGB] color from a [HSL] color
    pub fn from_hsl(hsl: HSL) -> Self {
        hsl.as_rgb()
    }

    /// Converts a [RGB] to a [HSL]
    pub fn as_hsl(&self) -> HSL {
        let r = self.r as f32 / 255.0;
        let g = self.g as f32 / 255.0;
        let b = self.b as f32 / 255.0;
        let c_max = r.max(g).max(b);
        let c_min = r.min(g).min(b);
        let delta = c_max - c_min;

        let h = if delta == 0.0 {
            0.0
        } else if c_max == r {
            f32::consts::FRAC_PI_3 * ((g - b) / delta).rem_euclid(6.0)
        } else if c_max == g {
            f32::consts::FRAC_PI_3 * ((b - r) / delta + 2.0)
        } else {
            f32::consts::FRAC_PI_3 * ((r - g) / delta + 4.0)
        };

        let l = 0.5 * (c_max + c_min);
        let s = if delta == 0.0 {
            0.0
        } else {
            delta / (1.0 - (2.0 * l - 1.0).abs())
        };

        HSL::new(h, s, l)
    } 

    /// Computes the linear interpolation between `self` and `other` for `t`
    pub fn lerp(&self, other: Self, t: f32) -> Self {
        let t = t.clamp(0.0, 1.0);
        self * (1.0 - t) + other * t
    }
}

impl From<(u8, u8, u8)> for RGB {
    fn from((r, g, b): (u8, u8, u8)) -> Self {
        Self::new(r, g, b)
    }
}

impl From<(f32, f32, f32)> for RGB {
    fn from((r, g, b): (f32, f32, f32)) -> Self {
        Self::from_f32(r, g, b)
    }
}

impl From<HSL> for RGB {
    fn from(hsl: HSL) -> Self {
        hsl.as_rgb()
    }
}

impl fmt::Display for RGB {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "\x1B[{}m  \x1B[0m", self.ansi_color_code(TargetGround::Background))     
    }
}

impl ANSIColorCode for RGB {
    fn ansi_color_code(&self, target: TargetGround) -> String {
        format!("{};2;{};{};{}", target.code() + 8, self.r, self.g, self.b)
    }
}

overload::overload!(
    (lhs: ?RGB) + (rhs: ?RGB) -> RGB {
        RGB::new(
            lhs.r.saturating_add(rhs.r),
            lhs.g.saturating_add(rhs.g),
            lhs.b.saturating_add(rhs.b)
        )
    }
);

overload::overload!(
    (lhs: ?RGB) - (rhs: ?RGB) -> RGB {
        RGB::new(
            lhs.r.saturating_sub(rhs.r),
            lhs.g.saturating_sub(rhs.g),
            lhs.b.saturating_sub(rhs.b)
        )
    }
);

overload::overload!(
    (lhs: ?RGB) * (rhs: ?f32) -> RGB {
        RGB::new(
            (lhs.r as f32 * rhs.clamp(0.0, 1.0)) as u8,
            (lhs.g as f32 * rhs.clamp(0.0, 1.0)) as u8,
            (lhs.b as f32 * rhs.clamp(0.0, 1.0)) as u8
        )
    }
);

overload::overload!(
    (lhs: ?f32) * (rhs: ?RGB) -> RGB {
        RGB::new(
            (rhs.r as f32 * lhs.clamp(0.0, 1.0)) as u8,
            (rhs.g as f32 * lhs.clamp(0.0, 1.0)) as u8,
            (rhs.b as f32 * lhs.clamp(0.0, 1.0)) as u8
        )
    }
);

overload::overload!(
    -(rgb: ?RGB) -> RGB {
        RGB::new(
            255 - rgb.r,
            255 - rgb.g,
            255 - rgb.b)
    }
);