inksac/color/
manipulation.rs

1use super::basic::Color;
2use crate::error::ColorError;
3
4impl Color {
5    /// Lighten a color by a percentage
6    ///
7    /// # Arguments
8    /// * `percent` - Amount to lighten (0-100)
9    ///
10    /// # Returns
11    /// * `Ok(Color)` - Lightened color
12    /// * `Err(ColorError)` - If color manipulation fails
13    ///
14    /// # Examples
15    /// ```rust
16    /// use inksac::Color;
17    ///
18    /// fn main() -> Result<(), Box<dyn std::error::Error>> {
19    ///     let color = Color::new_rgb(255, 100, 0)?;
20    ///     let lighter = color.lighten(30)?;
21    ///     Ok(())
22    /// }
23    /// ```
24    pub fn lighten(self, percent: u8) -> Result<Self, ColorError> {
25        match self {
26            Color::RGB(r, g, b) => {
27                let percent = f32::from(percent.min(100)) / 100.0;
28                let r = ((255.0 - f32::from(r)) * percent + f32::from(r)) as u8;
29                let g = ((255.0 - f32::from(g)) * percent + f32::from(g)) as u8;
30                let b = ((255.0 - f32::from(b)) * percent + f32::from(b)) as u8;
31                Color::new_rgb(r, g, b)
32            }
33            Color::HEX(hex) => {
34                let (r, g, b) = Self::validate_hex(hex)?;
35                Color::RGB(r, g, b).lighten(percent)
36            }
37            _ => Ok(self),
38        }
39    }
40
41    /// Darken a color by a percentage
42    ///
43    /// # Arguments
44    /// * `percent` - Amount to darken (0-100)
45    ///
46    /// # Returns
47    /// * `Ok(Color)` - Darkened color
48    /// * `Err(ColorError)` - If color manipulation fails
49    ///
50    /// # Examples
51    /// ```rust
52    /// use inksac::Color;
53    ///
54    /// fn main() -> Result<(), Box<dyn std::error::Error>> {
55    ///     let color = Color::new_rgb(255, 100, 0)?;
56    ///     let darker = color.darken(30)?;
57    ///     Ok(())
58    /// }
59    /// ```
60    pub fn darken(self, percent: u8) -> Result<Self, ColorError> {
61        match self {
62            Color::RGB(r, g, b) => {
63                let percent = f32::from(percent.min(100)) / 100.0;
64                let r = (f32::from(r) * (1.0 - percent)) as u8;
65                let g = (f32::from(g) * (1.0 - percent)) as u8;
66                let b = (f32::from(b) * (1.0 - percent)) as u8;
67                Color::new_rgb(r, g, b)
68            }
69            Color::HEX(hex) => {
70                let (r, g, b) = Self::validate_hex(hex)?;
71                Color::RGB(r, g, b).darken(percent)
72            }
73            _ => Ok(self),
74        }
75    }
76}
77
78#[cfg(test)]
79mod tests {
80    use super::*;
81    use crate::env::tests::run_with_env_vars;
82
83    #[test]
84    fn test_lighten() {
85        run_with_env_vars(
86            &[
87                ("COLORTERM", Some("truecolor")),
88                ("TERM", Some("xterm-256color")),
89                ("NO_COLOR", None),
90            ],
91            || {
92                let color = Color::new_rgb(100, 100, 100).unwrap();
93                let lightened = color.lighten(50).unwrap();
94                if let Color::RGB(r, g, b) = lightened {
95                    assert!(r > 100);
96                    assert!(g > 100);
97                    assert!(b > 100);
98                }
99            },
100        );
101    }
102
103    #[test]
104    fn test_darken() {
105        run_with_env_vars(
106            &[
107                ("COLORTERM", Some("truecolor")),
108                ("TERM", Some("xterm-256color")),
109                ("NO_COLOR", None),
110            ],
111            || {
112                let color = Color::new_rgb(100, 100, 100).unwrap();
113                let darkened = color.darken(50).unwrap();
114                if let Color::RGB(r, g, b) = darkened {
115                    assert!(r < 100);
116                    assert!(g < 100);
117                    assert!(b < 100);
118                }
119            },
120        );
121    }
122}