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, Eq, Hash, EnumCount)]
16#[allow(missing_docs)]
17pub enum White {
18 AntiqueWhite,
19 Beige,
20 Bisque,
21 BlanchedAlmond,
22 Wheat,
23 CornSilk,
24 White,
25 NavajoWhite,
26 MistyRose,
27 LavenderBlush,
28 Linen,
29 OldLace,
30 SeaShell,
31 MintCream,
32 FloralWhite,
33 GhostWhite,
34 Ivory,
35 Snow,
36 WhiteSmoke,
37 AliceBlue,
38}
39
40impl fmt::Display for White {
41 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42 match self {
43 Self::AntiqueWhite => write!(f, "#FAEBD7"),
44 Self::Beige => write!(f, "#F5F5DC"),
45 Self::Bisque => write!(f, "#FFE4C4"),
46 Self::BlanchedAlmond => write!(f, "#FFEBCD"),
47 Self::Wheat => write!(f, "#F5DEB3"),
48 Self::CornSilk => write!(f, "#FFF8DC"),
49 Self::White => write!(f, "#FFFFFF"),
50 Self::NavajoWhite => write!(f, "#FFDEAD"),
51 Self::MistyRose => write!(f, "#FFE4E1"),
52 Self::LavenderBlush => write!(f, "#FFF0F5"),
53 Self::Linen => write!(f, "#FAF0E6"),
54 Self::OldLace => write!(f, "#FDF5E6"),
55 Self::SeaShell => write!(f, "#FFF5EE"),
56 Self::MintCream => write!(f, "#F5FFFA"),
57 Self::FloralWhite => write!(f, "#FFFAF0"),
58 Self::GhostWhite => write!(f, "#F8F8FF"),
59 Self::Ivory => write!(f, "#FFFFF0"),
60 Self::Snow => write!(f, "#FFFAFA"),
61 Self::WhiteSmoke => write!(f, "#F5F5F5"),
62 Self::AliceBlue => write!(f, "#F0F8FF"),
63 }
64 }
65}
66
67impl White {
68 pub fn to_rgb(&self) -> Rgb<u8> {
84 let colour = self.to_string();
85
86 let r: u8 = u8::from_str_radix(&colour[1..3], 16).unwrap();
87 let g: u8 = u8::from_str_radix(&colour[3..5], 16).unwrap();
88 let b: u8 = u8::from_str_radix(&colour[5..7], 16).unwrap();
89
90 Rgb::new(r, g, b)
91 }
92
93 pub fn to_hex_triplet(&self, prefix: Prefix) -> String {
106 let rgb = self.to_rgb();
107
108 let prefix = match prefix {
109 Prefix::Hash => "#",
110 Prefix::None => "",
111 };
112
113 format!("{}{:02X}{:02X}{:02X}", prefix, rgb.r, rgb.g, rgb.b)
114 }
115
116 pub fn parse(name: &str) -> Option<Self> {
129 match name.to_lowercase().as_str() {
130 "#faebd7" | "faebd7" | "antiquewhite" => Some(Self::AntiqueWhite),
131 "#f5f5dc" | "f5f5dc" | "beige" => Some(Self::Beige),
132 "#ffe4c4" | "ffe4c4" | "bisque" => Some(Self::Bisque),
133 "#ffebcd" | "ffebcd" | "blanchedalmond" => Some(Self::BlanchedAlmond),
134 "#f5deb3" | "f5deb3" | "wheat" => Some(Self::Wheat),
135 "#fff8dc" | "fff8dc" | "cornsilk" => Some(Self::CornSilk),
136 "#ffffff" | "ffffff" | "white" => Some(Self::White),
137 "#ffdead" | "ffdead" | "navajowhite" => Some(Self::NavajoWhite),
138 "#ffe4e1" | "ffe4e1" | "mistyrose" => Some(Self::MistyRose),
139 "#fff0f5" | "fff0f5" | "lavenderblush" => Some(Self::LavenderBlush),
140 "#faf0e6" | "faf0e6" | "linen" => Some(Self::Linen),
141 "#fdf5e6" | "fdf5e6" | "oldlace" => Some(Self::OldLace),
142 "#fff5ee" | "fff5ee" | "seashell" => Some(Self::SeaShell),
143 "#f5fffa" | "f5fffa" | "mintcream" => Some(Self::MintCream),
144 "#fffaf0" | "fffaf0" | "floralwhite" => Some(Self::FloralWhite),
145 "#f8f8ff" | "f8f8ff" | "ghostwhite" => Some(Self::GhostWhite),
146 "#fffff0" | "fffff0" | "ivory" => Some(Self::Ivory),
147 "#fffafa" | "fffafa" | "snow" => Some(Self::Snow),
148 "#f5f5f5" | "f5f5f5" | "whitesmoke" => Some(Self::WhiteSmoke),
149 "#f0f8ff" | "f0f8ff" | "aliceblue" => Some(Self::AliceBlue),
150 _ => None,
151 }
152 }
153
154 pub fn random() -> Self {
166 let mut rand = StdRand::default();
167
168 match rand.next_range(0..Self::COUNT) {
169 0 => Self::AntiqueWhite,
170 1 => Self::Beige,
171 2 => Self::Bisque,
172 3 => Self::BlanchedAlmond,
173 4 => Self::Wheat,
174 5 => Self::CornSilk,
175 6 => Self::White,
176 7 => Self::NavajoWhite,
177 8 => Self::MistyRose,
178 9 => Self::LavenderBlush,
179 10 => Self::Linen,
180 11 => Self::OldLace,
181 12 => Self::SeaShell,
182 13 => Self::MintCream,
183 14 => Self::FloralWhite,
184 15 => Self::GhostWhite,
185 16 => Self::Ivory,
186 17 => Self::Snow,
187 18 => Self::WhiteSmoke,
188 19 => Self::AliceBlue,
189 _ => Self::White,
190 }
191 }
192}
193
194impl FromStr for White {
195 type Err = String;
196 fn from_str(s: &str) -> Result<Self, Self::Err> {
197 match Self::parse(s) {
198 Some(colour) => Ok(colour),
199 None => Err(format!("Invalid Colour: {s}")),
200 }
201 }
202}
203
204impl ExtendedColour for White {}
205
206#[cfg(test)]
207mod tests {
208 use super::*;
209 use rstest::rstest;
210
211 #[rstest]
212 #[case(White::AntiqueWhite, "rgb(250,235,215)")]
213 #[case(White::Beige, "rgb(245,245,220)")]
214 #[case(White::Bisque, "rgb(255,228,196)")]
215 #[case(White::BlanchedAlmond, "rgb(255,235,205)")]
216 #[case(White::Wheat, "rgb(245,222,179)")]
217 #[case(White::CornSilk, "rgb(255,248,220)")]
218 #[case(White::White, "rgb(255,255,255)")]
219 #[case(White::NavajoWhite, "rgb(255,222,173)")]
220 #[case(White::MistyRose, "rgb(255,228,225)")]
221 #[case(White::LavenderBlush, "rgb(255,240,245)")]
222 #[case(White::Linen, "rgb(250,240,230)")]
223 #[case(White::OldLace, "rgb(253,245,230)")]
224 #[case(White::SeaShell, "rgb(255,245,238)")]
225 #[case(White::MintCream, "rgb(245,255,250)")]
226 #[case(White::FloralWhite, "rgb(255,250,240)")]
227 #[case(White::GhostWhite, "rgb(248,248,255)")]
228 #[case(White::Ivory, "rgb(255,255,240)")]
229 #[case(White::Snow, "rgb(255,250,250)")]
230 #[case(White::WhiteSmoke, "rgb(245,245,245)")]
231 fn test_rgb_string(#[case] colour: White, #[case] expected: String) {
232 let rgb_colour = colour.to_rgb();
233 let string = rgb_colour.to_string();
234
235 assert_eq!(expected, string);
236 }
237
238 #[rstest]
239 #[case(White::AntiqueWhite, "FAEBD7")]
240 #[case(White::Beige, "F5F5DC")]
241 #[case(White::Bisque, "FFE4C4")]
242 #[case(White::BlanchedAlmond, "FFEBCD")]
243 #[case(White::Wheat, "F5DEB3")]
244 #[case(White::CornSilk, "FFF8DC")]
245 #[case(White::White, "FFFFFF")]
246 #[case(White::NavajoWhite, "FFDEAD")]
247 #[case(White::MistyRose, "FFE4E1")]
248 #[case(White::LavenderBlush, "FFF0F5")]
249 #[case(White::Linen, "FAF0E6")]
250 #[case(White::OldLace, "FDF5E6")]
251 #[case(White::SeaShell, "FFF5EE")]
252 #[case(White::MintCream, "F5FFFA")]
253 #[case(White::FloralWhite, "FFFAF0")]
254 #[case(White::GhostWhite, "F8F8FF")]
255 #[case(White::Ivory, "FFFFF0")]
256 #[case(White::Snow, "FFFAFA")]
257 #[case(White::WhiteSmoke, "F5F5F5")]
258 fn test_hex_triplet_string(
259 #[case] colour: White,
260 #[values(Prefix::None, Prefix::Hash)] prefix: Prefix,
261 #[case] expected: String,
262 ) {
263 let prefix_string = match prefix {
264 Prefix::None => "".to_string(),
265 Prefix::Hash => "#".to_string(),
266 };
267
268 let expected = format!("{prefix_string}{expected}");
269
270 let hex_colour = colour.to_hex_triplet(prefix);
271
272 assert_eq!(expected, hex_colour);
273 }
274
275 #[rstest]
276 #[case("#faebd7", White::AntiqueWhite)]
277 #[case("faebd7", White::AntiqueWhite)]
278 #[case("AntiqueWhite", White::AntiqueWhite)]
279 #[case("#f5f5dc", White::Beige)]
280 #[case("f5f5dc", White::Beige)]
281 #[case("Beige", White::Beige)]
282 #[case("#ffe4c4", White::Bisque)]
283 #[case("ffe4c4", White::Bisque)]
284 #[case("Bisque", White::Bisque)]
285 #[case("#ffebcd", White::BlanchedAlmond)]
286 #[case("ffebcd", White::BlanchedAlmond)]
287 #[case("BlanchedAlmond", White::BlanchedAlmond)]
288 #[case("#f5deb3", White::Wheat)]
289 #[case("f5deb3", White::Wheat)]
290 #[case("wheat", White::Wheat)]
291 #[case("#fff8dc", White::CornSilk)]
292 #[case("fff8dc", White::CornSilk)]
293 #[case("CornSilk", White::CornSilk)]
294 #[case("#ffffff", White::White)]
295 #[case("ffffff", White::White)]
296 #[case("White", White::White)]
297 #[case("#ffdead", White::NavajoWhite)]
298 #[case("ffdead", White::NavajoWhite)]
299 #[case("NavajoWhite", White::NavajoWhite)]
300 #[case("#ffe4e1", White::MistyRose)]
301 #[case("ffe4e1", White::MistyRose)]
302 #[case("MistyRose", White::MistyRose)]
303 #[case("#fff0f5", White::LavenderBlush)]
304 #[case("fff0f5", White::LavenderBlush)]
305 #[case("LavenderBlush", White::LavenderBlush)]
306 #[case("#faf0e6", White::Linen)]
307 #[case("faf0e6", White::Linen)]
308 #[case("Linen", White::Linen)]
309 #[case("#fdf5e6", White::OldLace)]
310 #[case("fdf5e6", White::OldLace)]
311 #[case("OldLace", White::OldLace)]
312 #[case("#fff5ee", White::SeaShell)]
313 #[case("fff5ee", White::SeaShell)]
314 #[case("SeaShell", White::SeaShell)]
315 #[case("#f5fffa", White::MintCream)]
316 #[case("f5fffa", White::MintCream)]
317 #[case("MintCream", White::MintCream)]
318 #[case("#fffaf0", White::FloralWhite)]
319 #[case("fffaf0", White::FloralWhite)]
320 #[case("FloralWhite", White::FloralWhite)]
321 #[case("#f8f8ff", White::GhostWhite)]
322 #[case("f8f8ff", White::GhostWhite)]
323 #[case("GhostWhite", White::GhostWhite)]
324 #[case("#fffff0", White::Ivory)]
325 #[case("fffff0", White::Ivory)]
326 #[case("Ivory", White::Ivory)]
327 #[case("#fffafa", White::Snow)]
328 #[case("fffafa", White::Snow)]
329 #[case("Snow", White::Snow)]
330 #[case("#f5f5f5", White::WhiteSmoke)]
331 #[case("f5f5f5", White::WhiteSmoke)]
332 #[case("WhiteSmoke", White::WhiteSmoke)]
333 #[case("#f0f8ff", White::AliceBlue)]
334 #[case("f0f8ff", White::AliceBlue)]
335 #[case("AliceBlue", White::AliceBlue)]
336 fn test_from_str(#[case] input: &str, #[case] expected: White) {
337 assert_eq!(expected, White::from_str(input).unwrap())
338 }
339
340 #[rstest]
341 #[case("#faebd7", Some(White::AntiqueWhite))]
342 #[case("faebd7", Some(White::AntiqueWhite))]
343 #[case("AntiqueWhite", Some(White::AntiqueWhite))]
344 #[case("#f5f5dc", Some(White::Beige))]
345 #[case("f5f5dc", Some(White::Beige))]
346 #[case("Beige", Some(White::Beige))]
347 #[case("#ffe4c4", Some(White::Bisque))]
348 #[case("ffe4c4", Some(White::Bisque))]
349 #[case("Bisque", Some(White::Bisque))]
350 #[case("#ffebcd", Some(White::BlanchedAlmond))]
351 #[case("ffebcd", Some(White::BlanchedAlmond))]
352 #[case("BlanchedAlmond", Some(White::BlanchedAlmond))]
353 #[case("#f5deb3", Some(White::Wheat))]
354 #[case("f5deb3", Some(White::Wheat))]
355 #[case("wheat", Some(White::Wheat))]
356 #[case("#fff8dc", Some(White::CornSilk))]
357 #[case("fff8dc", Some(White::CornSilk))]
358 #[case("CornSilk", Some(White::CornSilk))]
359 #[case("#ffffff", Some(White::White))]
360 #[case("ffffff", Some(White::White))]
361 #[case("White", Some(White::White))]
362 #[case("#ffdead", Some(White::NavajoWhite))]
363 #[case("ffdead", Some(White::NavajoWhite))]
364 #[case("NavajoWhite", Some(White::NavajoWhite))]
365 #[case("#ffe4e1", Some(White::MistyRose))]
366 #[case("ffe4e1", Some(White::MistyRose))]
367 #[case("MistyRose", Some(White::MistyRose))]
368 #[case("#fff0f5", Some(White::LavenderBlush))]
369 #[case("fff0f5", Some(White::LavenderBlush))]
370 #[case("LavenderBlush", Some(White::LavenderBlush))]
371 #[case("#faf0e6", Some(White::Linen))]
372 #[case("faf0e6", Some(White::Linen))]
373 #[case("Linen", Some(White::Linen))]
374 #[case("#fdf5e6", Some(White::OldLace))]
375 #[case("fdf5e6", Some(White::OldLace))]
376 #[case("OldLace", Some(White::OldLace))]
377 #[case("#fff5ee", Some(White::SeaShell))]
378 #[case("fff5ee", Some(White::SeaShell))]
379 #[case("SeaShell", Some(White::SeaShell))]
380 #[case("#f5fffa", Some(White::MintCream))]
381 #[case("f5fffa", Some(White::MintCream))]
382 #[case("MintCream", Some(White::MintCream))]
383 #[case("#fffaf0", Some(White::FloralWhite))]
384 #[case("fffaf0", Some(White::FloralWhite))]
385 #[case("FloralWhite", Some(White::FloralWhite))]
386 #[case("#f8f8ff", Some(White::GhostWhite))]
387 #[case("f8f8ff", Some(White::GhostWhite))]
388 #[case("GhostWhite", Some(White::GhostWhite))]
389 #[case("#fffff0", Some(White::Ivory))]
390 #[case("fffff0", Some(White::Ivory))]
391 #[case("Ivory", Some(White::Ivory))]
392 #[case("#fffafa", Some(White::Snow))]
393 #[case("fffafa", Some(White::Snow))]
394 #[case("Snow", Some(White::Snow))]
395 #[case("#f5f5f5", Some(White::WhiteSmoke))]
396 #[case("f5f5f5", Some(White::WhiteSmoke))]
397 #[case("WhiteSmoke", Some(White::WhiteSmoke))]
398 #[case("#f0f8ff", Some(White::AliceBlue))]
399 #[case("f0f8ff", Some(White::AliceBlue))]
400 #[case("AliceBlue", Some(White::AliceBlue))]
401 #[case("012345", None)]
402 fn test_name_colour(#[case] input: &str, #[case] expected: Option<White>) {
403 assert_eq!(expected, White::name_colour(input))
404 }
405}