named_colour/ext/
black.rs

1//! Extended named colours providing shades collected in enums for the main colour
2//!
3
4use std::{fmt, str::FromStr};
5
6use rgb::Rgb;
7use strum::EnumCount;
8use tinyrand::{RandRange, StdRand};
9
10use crate::Prefix;
11
12use super::ExtendedColour;
13
14/// Shades of black
15#[derive(Debug, Clone, Copy, PartialEq, EnumCount, Eq, Hash)]
16#[allow(missing_docs)]
17pub enum Black {
18    SlateGray,
19    SlateGrey,
20    LightSlateGray,
21    LightSlateGrey,
22    Black,
23    DimGray,
24    DimGrey,
25    Gray,
26    Grey,
27    DarkGray,
28    DarkGrey,
29    Silver,
30    LightGray,
31    LightGrey,
32    Gainsboro,
33}
34
35impl fmt::Display for Black {
36    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37        match self {
38            Black::SlateGray => write!(f, "#708090"),
39            Black::SlateGrey => write!(f, "#708090"),
40            Black::LightSlateGray => write!(f, "#778899"),
41            Black::LightSlateGrey => write!(f, "#778899"),
42            Black::Black => write!(f, "#000000"),
43            Black::DimGray => write!(f, "#696969"),
44            Black::DimGrey => write!(f, "#696969"),
45            Black::Gray => write!(f, "#808080"),
46            Black::Grey => write!(f, "#808080"),
47            Black::DarkGray => write!(f, "#A9A9A9"),
48            Black::DarkGrey => write!(f, "#A9A9A9"),
49            Black::Silver => write!(f, "#C0C0C0"),
50            Black::LightGray => write!(f, "#D3D3D3"),
51            Black::LightGrey => write!(f, "#D3D3D3"),
52            Black::Gainsboro => write!(f, "#DCDCDC"),
53        }
54    }
55}
56
57impl Black {
58    /// Display the colour name as an RGB Tuple
59    ///
60    /// ## Example
61    ///
62    ///```
63    /// # use named_colour::ext::Black;
64    /// # fn main() {
65    ///    let colour = Black::Gainsboro;
66    ///    let rgb_colour = colour.to_rgb();
67    ///
68    ///    let string = rgb_colour.to_string();
69    ///    assert_eq!("rgb(220,220,220)", string);
70    ///
71    ///  # }
72    ///```
73    pub fn to_rgb(&self) -> Rgb<u8> {
74        let colour = self.to_string();
75
76        let r: u8 = u8::from_str_radix(&colour[1..3], 16).unwrap();
77        let g: u8 = u8::from_str_radix(&colour[3..5], 16).unwrap();
78        let b: u8 = u8::from_str_radix(&colour[5..7], 16).unwrap();
79
80        Rgb::new(r, g, b)
81    }
82
83    /// Display the colour name as an RGB Tuple
84    ///
85    /// ## Example
86    ///
87    ///```
88    /// # use named_colour::ext::Black;
89    /// # use named_colour::Prefix;
90    ///    let colour = Black::Gainsboro;
91    ///
92    ///     assert_eq!("#DCDCDC", colour.to_hex_triplet(Prefix::Hash));
93    ///
94    ///```
95    pub fn to_hex_triplet(&self, prefix: Prefix) -> String {
96        let rgb = self.to_rgb();
97
98        let prefix = match prefix {
99            Prefix::Hash => "#",
100            Prefix::None => "",
101        };
102
103        format!("{}{:02X}{:02X}{:02X}", prefix, rgb.r, rgb.g, rgb.b)
104    }
105
106    /// Parse a colour from string
107    ///
108    /// ## Example
109    ///
110    ///```
111    /// # use named_colour::ext::Black;
112    /// # fn main() {
113    ///    let colour = Black::parse("gainsboro");
114    ///
115    ///    assert_eq!(Some(Black::Gainsboro), colour);
116    /// # }
117    /// ```
118    ///
119    pub fn parse(name: &str) -> Option<Self> {
120        match name.to_lowercase().as_str() {
121            "#708090" | "708090" | "slategray" => Some(Self::SlateGray),
122            "slategrey" => Some(Self::SlateGrey),
123            "#778899" | "778899" | "lightslategray" => Some(Self::LightSlateGray),
124            "lightslategrey" => Some(Self::LightSlateGrey),
125            "#000000" | "000000" | "black" => Some(Self::Black),
126            "#696969" | "696969" | "dimgray" => Some(Self::DimGray),
127            "dimgrey" => Some(Self::DimGrey),
128            "#808080" | "808080" | "gray" => Some(Self::Gray),
129            "grey" => Some(Self::Grey),
130            "#a9a9a9" | "a9a9a9" | "darkgray" => Some(Self::DarkGray),
131            "darkgrey" => Some(Self::DarkGrey),
132            "#c0c0c0" | "c0c0c0" | "silver" => Some(Self::Silver),
133            "#d3d3d3" | "d3d3d3" | "lightgray" => Some(Self::LightGray),
134            "lightgrey" => Some(Self::LightGrey),
135            "#dcdcdc" | "dcdcdc" | "gainsboro" => Some(Self::Gainsboro),
136            _ => None,
137        }
138    }
139
140    /// Generate a random colour
141    ///     
142    /// ## Example
143    ///
144    ///```
145    /// # use named_colour::ext::Black;
146    /// # fn main() {
147    ///    let colour = Black::random();
148    ///
149    /// # }
150    /// ```
151    pub fn random() -> Self {
152        let mut rand = StdRand::default();
153
154        match rand.next_range(0..Self::COUNT) {
155            0 => Black::SlateGray,
156            1 => Black::SlateGrey,
157            2 => Black::LightSlateGray,
158            3 => Black::LightSlateGrey,
159            4 => Black::Black,
160            5 => Black::DimGray,
161            6 => Black::DimGrey,
162            7 => Black::Gray,
163            8 => Black::Grey,
164            9 => Black::DarkGray,
165            10 => Black::DarkGrey,
166            11 => Black::Silver,
167            12 => Black::LightGray,
168            13 => Black::LightGrey,
169            14 => Black::Gainsboro,
170            _ => Black::Black,
171        }
172    }
173}
174
175impl FromStr for Black {
176    type Err = String;
177    fn from_str(s: &str) -> Result<Self, Self::Err> {
178        match Self::parse(s) {
179            Some(colour) => Ok(colour),
180            None => Err(format!("Invalid Colour: {}", s)),
181        }
182    }
183}
184
185impl ExtendedColour for Black {}
186
187#[cfg(test)]
188mod tests {
189    use super::*;
190    use rstest::rstest;
191
192    #[rstest]
193    #[case(Black::SlateGray, "rgb(112,128,144)")]
194    #[case(Black::LightSlateGray, "rgb(119,136,153)")]
195    #[case(Black::Black, "rgb(0,0,0)")]
196    #[case(Black::DimGray, "rgb(105,105,105)")]
197    #[case(Black::DimGrey, "rgb(105,105,105)")]
198    #[case(Black::Gray, "rgb(128,128,128)")]
199    #[case(Black::Grey, "rgb(128,128,128)")]
200    #[case(Black::DarkGray, "rgb(169,169,169)")]
201    #[case(Black::DarkGrey, "rgb(169,169,169)")]
202    #[case(Black::Silver, "rgb(192,192,192)")]
203    #[case(Black::LightGray, "rgb(211,211,211)")]
204    #[case(Black::LightGrey, "rgb(211,211,211)")]
205    #[case(Black::Gainsboro, "rgb(220,220,220)")]
206    fn test_rgb_string(#[case] colour: Black, #[case] expected: String) {
207        let rgb_colour = colour.to_rgb();
208        let string = rgb_colour.to_string();
209
210        assert_eq!(expected, string);
211    }
212
213    #[rstest]
214    #[case(Black::SlateGray, "708090")]
215    #[case(Black::LightSlateGray, "778899")]
216    #[case(Black::Black, "000000")]
217    #[case(Black::DimGray, "696969")]
218    #[case(Black::DimGrey, "696969")]
219    #[case(Black::Gray, "808080")]
220    #[case(Black::Grey, "808080")]
221    #[case(Black::DarkGray, "A9A9A9")]
222    #[case(Black::DarkGrey, "A9A9A9")]
223    #[case(Black::Silver, "C0C0C0")]
224    #[case(Black::LightGray, "D3D3D3")]
225    #[case(Black::LightGrey, "D3D3D3")]
226    #[case(Black::Gainsboro, "DCDCDC")]
227    fn test_hex_triplet_string(
228        #[case] colour: Black,
229        #[values(Prefix::None, Prefix::Hash)] prefix: Prefix,
230        #[case] expected: String,
231    ) {
232        let prefix_string = match prefix {
233            Prefix::None => "".to_string(),
234            Prefix::Hash => "#".to_string(),
235        };
236
237        let expected = format!("{}{}", prefix_string, expected);
238
239        let hex_colour = colour.to_hex_triplet(prefix);
240
241        assert_eq!(expected, hex_colour);
242    }
243
244    #[rstest]
245    #[case("#708090", Black::SlateGray)]
246    #[case("708090", Black::SlateGray)]
247    #[case("slategray", Black::SlateGray)]
248    #[case("slategrey", Black::SlateGrey)]
249    #[case("#778899", Black::LightSlateGray)]
250    #[case("778899", Black::LightSlateGray)]
251    #[case("lightslategray", Black::LightSlateGray)]
252    #[case("lightslategrey", Black::LightSlateGrey)]
253    #[case("#000000", Black::Black)]
254    #[case("000000", Black::Black)]
255    #[case("black", Black::Black)]
256    #[case("#696969", Black::DimGray)]
257    #[case("696969", Black::DimGray)]
258    #[case("dimgray", Black::DimGray)]
259    #[case("dimgrey", Black::DimGrey)]
260    #[case("#808080", Black::Gray)]
261    #[case("808080", Black::Gray)]
262    #[case("gray", Black::Gray)]
263    #[case("grey", Black::Grey)]
264    #[case("#A9A9A9", Black::DarkGray)]
265    #[case("A9A9A9", Black::DarkGray)]
266    #[case("darkgray", Black::DarkGray)]
267    #[case("darkgrey", Black::DarkGrey)]
268    #[case("#C0C0C0", Black::Silver)]
269    #[case("C0C0C0", Black::Silver)]
270    #[case("silver", Black::Silver)]
271    #[case("#D3D3D3", Black::LightGray)]
272    #[case("D3D3D3", Black::LightGray)]
273    #[case("lightgray", Black::LightGray)]
274    #[case("lightgrey", Black::LightGrey)]
275    #[case("#DCDCDC", Black::Gainsboro)]
276    #[case("DCDCDC", Black::Gainsboro)]
277    #[case("gainsboro", Black::Gainsboro)]
278    fn test_parse(#[case] input: &str, #[case] expected: Black) {
279        assert_eq!(expected, Black::from_str(input).unwrap())
280    }
281
282    #[rstest]
283    #[case("#708090", Some(Black::SlateGray))]
284    #[case("708090", Some(Black::SlateGray))]
285    #[case("slategray", Some(Black::SlateGray))]
286    #[case("slategrey", Some(Black::SlateGrey))]
287    #[case("#778899", Some(Black::LightSlateGray))]
288    #[case("778899", Some(Black::LightSlateGray))]
289    #[case("lightslategray", Some(Black::LightSlateGray))]
290    #[case("lightslategrey", Some(Black::LightSlateGrey))]
291    #[case("#000000", Some(Black::Black))]
292    #[case("000000", Some(Black::Black))]
293    #[case("black", Some(Black::Black))]
294    #[case("#696969", Some(Black::DimGray))]
295    #[case("696969", Some(Black::DimGray))]
296    #[case("dimgray", Some(Black::DimGray))]
297    #[case("dimgrey", Some(Black::DimGrey))]
298    #[case("#808080", Some(Black::Gray))]
299    #[case("808080", Some(Black::Gray))]
300    #[case("gray", Some(Black::Gray))]
301    #[case("grey", Some(Black::Grey))]
302    #[case("#A9A9A9", Some(Black::DarkGray))]
303    #[case("A9A9A9", Some(Black::DarkGray))]
304    #[case("darkgray", Some(Black::DarkGray))]
305    #[case("darkgrey", Some(Black::DarkGrey))]
306    #[case("#C0C0C0", Some(Black::Silver))]
307    #[case("C0C0C0", Some(Black::Silver))]
308    #[case("silver", Some(Black::Silver))]
309    #[case("#D3D3D3", Some(Black::LightGray))]
310    #[case("D3D3D3", Some(Black::LightGray))]
311    #[case("lightgray", Some(Black::LightGray))]
312    #[case("lightgrey", Some(Black::LightGrey))]
313    #[case("#DCDCDC", Some(Black::Gainsboro))]
314    #[case("DCDCDC", Some(Black::Gainsboro))]
315    #[case("gainsboro", Some(Black::Gainsboro))]
316    #[case("012345", None)]
317    fn test_name_colour(#[case] input: &str, #[case] expected: Option<Black>) {
318        assert_eq!(expected, Black::name_colour(input))
319    }
320}