1use 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#[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 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 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 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 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}