color_name/css/
mod.rs

1mod constants;
2
3use crate::utils::euclidean_distance;
4pub use constants::{color, colors, Colors, COLORS_DATA};
5
6pub struct Color;
7
8impl Color {
9    /// Get colour rgb array by enum color param.
10    ///
11    /// #### Example
12    ///
13    /// ```rust
14    /// use color_name::{
15    ///     Color,
16    ///     color
17    /// };
18    /// assert_eq!(Color::val().by_enum(color::white), [255, 255, 255]);
19    ///
20    /// ```
21    pub fn by_enum(&self, color: color) -> [u8; 3] {
22        let _clr = color as usize;
23        (COLORS_DATA)[_clr].1
24    }
25
26    /// Get colour rgb array by String param.
27    ///
28    /// #### Example:
29    ///
30    /// ```rust
31    /// use color_name::{
32    ///     Color
33    /// };
34    /// assert_eq!(Color::val().by_string("InDigo".to_string()).expect("Not found"), [75, 0, 130]);
35    ///
36    /// // NOTE: the string can be at any case, it will be converted into Title-case
37    /// assert_eq!(Color::val().by_string("inDigo".to_string()).expect("Not found"), [75, 0, 130]);
38    /// assert_eq!(Color::val().by_string("IndiGo ".to_string()).expect("Not found"), [75, 0, 130]);
39    /// ```
40    ///
41    /// **NOTE:** Return `Result<[u8;3],u16>` as color data `[u8;3]` or Not found (`404`).
42    pub fn by_string(&self, color: String) -> Result<[u8; 3], u16> {
43        // if there ends up being a color whose name is the empty string, this will need changing
44        if color.len() == 0 {
45            Err(404)
46        } else {
47            let mut got = false;
48            let mut col = [0, 0, 0];
49            for c in COLORS_DATA.iter() {
50                if String::from(c.0)
51                    == format!(
52                        "{}{}",
53                        &color.trim().to_uppercase()[0..1],
54                        &color.trim().to_lowercase()[1..]
55                    )
56                {
57                    col = (c.1).to_owned();
58                    got = true;
59                    break;
60                }
61            }
62            if got {
63                Ok(col)
64            } else {
65                Err(404)
66            }
67        }
68    }
69
70    /// Get value of a color by string OR enum.
71    pub fn val() -> Color {
72        Color
73    }
74
75    /// Get exact colour name if there's no data it return "404".
76    pub fn name(rgb: [u8; 3]) -> String {
77        for (name, value) in COLORS_DATA.iter() {
78            if value == &rgb {
79                return format!("{}", name);
80            }
81        }
82
83        format!("404")
84    }
85
86    /// Get closest colour name match the provided rgb data array.
87    pub fn similar(rgb: [u8; 3]) -> String {
88        let c = Self::name(rgb);
89        if c == "404" {
90            let mut current_closest_distance = u128::MAX;
91            let mut current_closest_keyword = "";
92
93            for (name, value) in COLORS_DATA.iter() {
94                // Compute comparative distance
95                let distance = euclidean_distance(rgb, *value);
96
97                if value == &rgb {
98                    return format!("{}", name);
99                }
100                // Check if its less, if so set as closest
101                #[allow(unused_parens)]
102                if (distance < current_closest_distance) {
103                    current_closest_distance = distance;
104                    current_closest_keyword = name;
105                }
106            }
107            format!("{}", current_closest_keyword)
108        } else {
109            c
110        }
111    }
112
113    /// Sweet proxy to simlar(rgb)
114    pub fn close_to(rgb: [u8; 3]) -> String {
115        Self::similar(rgb)
116    }
117}
118
119#[cfg(test)]
120mod test {
121    use super::*;
122
123    #[test]
124    fn test_lower_const_colors() {
125        assert_eq!(colors::yellow, [255, 255, 0]);
126        assert_eq!(colors::aqua, [0, 255, 255]);
127    }
128
129    #[test]
130    fn test_upper_const_colors() {
131        assert_eq!(Colors::Yellow, [255, 255, 0]);
132        assert_eq!(Colors::Aqua, [0, 255, 255]);
133    }
134
135    #[test]
136    fn test_color_by_enum_fn() {
137        assert_eq!(Color::val().by_enum(color::yellow), [255, 255, 0]);
138        assert_eq!(Color::val().by_enum(color::aqua), [0, 255, 255]);
139        assert_eq!(Color::val().by_enum(color::red), [255, 0, 0]);
140        assert_eq!(Color::val().by_enum(color::black), [0, 0, 0]);
141    }
142
143    #[test]
144    fn test_color_by_string_fn() {
145        assert_eq!(Color::val().by_string(String::from("")), Err(404));
146        assert_eq!(Color::val().by_string(String::from("A")), Err(404));
147        assert_eq!(
148            Color::val().by_string(String::from("Azure")).unwrap(),
149            [240, 255, 255]
150        );
151        assert_eq!(
152            Color::val().by_string(String::from("Chocolate")).unwrap(),
153            [210, 105, 30]
154        );
155        assert_eq!(
156            Color::val().by_string(String::from("Red")).unwrap(),
157            [255, 0, 0]
158        );
159        assert_eq!(
160            Color::val().by_string(String::from("Black")).unwrap(),
161            [0, 0, 0]
162        );
163        assert_eq!(Color::val().by_string(String::from("annymosse")), Err(404));
164    }
165
166    #[test]
167    fn test_color_name_fn() {
168        assert_eq!(Color::name([0, 0, 0]), "Black");
169        assert_eq!(Color::name([0, 1, 1]), "404");
170        assert_eq!(Color::name([0, 195, 0]), "404");
171    }
172
173    #[test]
174    fn test_color_similar_fn() {
175        assert_eq!(Color::similar([195, 40, 10]), "Firebrick");
176        assert_eq!(Color::similar([123, 45, 67]), "Brown");
177        assert_eq!(Color::similar([213, 69, 87]), "Indianred");
178        assert_eq!(Color::similar([87, 33, 26]), "Maroon");
179        assert_eq!(Color::similar([0, 195, 0]), "Lime");
180    }
181
182    #[test]
183    fn test_color_close_to() {
184        assert_eq!(Color::close_to([195, 40, 10]), "Firebrick");
185        assert_eq!(Color::close_to([123, 45, 67]), "Brown");
186        assert_eq!(Color::close_to([213, 69, 87]), "Indianred");
187        assert_eq!(Color::close_to([87, 33, 26]), "Maroon");
188        assert_eq!(Color::close_to([0, 195, 0]), "Lime");
189    }
190
191    #[test]
192    fn test_enum_colors() {
193        assert_eq!(Color::val().by_enum(color::yellow), [255, 255, 0]);
194        assert_eq!(Color::val().by_enum(color::aqua), [0, 255, 255]);
195        assert_eq!(Color::val().by_enum(color::red), [255, 0, 0]);
196        assert_eq!(Color::val().by_enum(color::black), [0, 0, 0]);
197    }
198
199    #[test]
200    fn test_not_found_cases() {
201        // Color::name returns type `String` thus the 404 in type of `String`
202        assert_eq!(Color::name([0, 195, 0]), "404");
203
204        // Color::val().by_string returns a type of `Result<[u8; {const}], u16>` thus the 404 in type of `Err(u16)`
205        assert_eq!(
206            Color::val().by_string(String::from("MayMoonsley")),
207            Err(404)
208        );
209    }
210}