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 Purple {
18 Indigo,
19 Purple,
20 DarkMagenta,
21 DarkViolet,
22 DarkSlateBlue,
23 BlueViolet,
24 DarkOrchid,
25 Fuchsia,
26 Magenta,
27 SlateBlue,
28 MediumSlateBlue,
29 MediumOrchid,
30 MediumPurple,
31 Orchid,
32 Violet,
33 Plum,
34 Thistle,
35 Lavender,
36 Pink,
37 MediumVioletRed,
38 PaleVioletRed,
39 DeepPink,
40 HotPink,
41 LightPink,
42}
43
44impl fmt::Display for Purple {
45 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46 match self {
47 Self::Indigo => write!(f, "#4B0082"),
48 Self::Purple => write!(f, "#800080"),
49 Self::DarkMagenta => write!(f, "#8B008B"),
50 Self::DarkViolet => write!(f, "#9400D3"),
51 Self::DarkSlateBlue => write!(f, "#483D8B"),
52 Self::BlueViolet => write!(f, "#8A2BE2"),
53 Self::DarkOrchid => write!(f, "#9932CC"),
54 Self::Fuchsia => write!(f, "#FF00FF"),
55 Self::Magenta => write!(f, "#FF00FF"),
56 Self::SlateBlue => write!(f, "#6A5ACD"),
57 Self::MediumSlateBlue => write!(f, "#7B68EE"),
58 Self::MediumOrchid => write!(f, "#BA55D3"),
59 Self::MediumPurple => write!(f, "#9370DB"),
60 Self::Orchid => write!(f, "#DA70D6"),
61 Self::Violet => write!(f, "#EE82EE"),
62 Self::Plum => write!(f, "#DDA0DD"),
63 Self::Thistle => write!(f, "#D8BFD8"),
64 Self::Lavender => write!(f, "#E6E6FA"),
65 Self::Pink => write!(f, "#FFC0CB"),
66 Self::MediumVioletRed => write!(f, "#C71585"),
67 Self::PaleVioletRed => write!(f, "#DB7093"),
68 Self::DeepPink => write!(f, "#FF1493"),
69 Self::HotPink => write!(f, "#FF69B4"),
70 Self::LightPink => write!(f, "#FFB6C1"),
71 }
72 }
73}
74
75impl Purple {
76 pub fn to_rgb(&self) -> Rgb<u8> {
92 let colour = self.to_string();
93
94 let r: u8 = u8::from_str_radix(&colour[1..3], 16).unwrap();
95 let g: u8 = u8::from_str_radix(&colour[3..5], 16).unwrap();
96 let b: u8 = u8::from_str_radix(&colour[5..7], 16).unwrap();
97
98 Rgb::new(r, g, b)
99 }
100
101 pub fn to_hex_triplet(&self, prefix: Prefix) -> String {
114 let rgb = self.to_rgb();
115
116 let prefix = match prefix {
117 Prefix::Hash => "#",
118 Prefix::None => "",
119 };
120
121 format!("{}{:02X}{:02X}{:02X}", prefix, rgb.r, rgb.g, rgb.b)
122 }
123
124 pub fn parse(name: &str) -> Option<Self> {
139 match name.to_lowercase().as_str() {
140 "#4b0082" | "4b0082" | "indigo" => Some(Self::Indigo),
141 "#800080" | "800080" | "purple" => Some(Self::Purple),
142 "#8b008b" | "8b008b" | "darkmagenta" => Some(Self::DarkMagenta),
143 "#9400d3" | "9400d3" | "darkviolet" => Some(Self::DarkViolet),
144 "#483d8b" | "483d8b" | "darkslateblue" => Some(Self::DarkSlateBlue),
145 "#8a2be2" | "8a2be2" | "blueviolet" => Some(Self::BlueViolet),
146 "#9932cc" | "9932cc" | "darkorchid" => Some(Self::DarkOrchid),
147 "#a020f0" | "a020f0" | "fuchsia" => Some(Self::Fuchsia),
148 "#ff00ff" | "ff00ff" | "magenta" => Some(Self::Magenta),
149 "#6a5acd" | "6a5acd" | "slateblue" => Some(Self::SlateBlue),
150 "#7b68ee" | "7b68ee" | "mediumslateblue" => Some(Self::MediumSlateBlue),
151 "#ba55d3" | "ba55d3" | "mediumorchid" => Some(Self::MediumOrchid),
152 "#9370db" | "9370db" | "mediumpurple" => Some(Self::MediumPurple),
153 "#da70d6" | "da70d6" | "orchid" => Some(Self::Orchid),
154 "#ee82ee" | "ee82ee" | "violet" => Some(Self::Violet),
155 "#dda0dd" | "dda0dd" | "plum" => Some(Self::Plum),
156 "#d8bfd8" | "d8bfd8" | "thistle" => Some(Self::Thistle),
157 "#e6e6fa" | "e6e6fa" | "lavender" => Some(Self::Lavender),
158 "#ffc0cb" | "ffc0cb" | "pink" => Some(Self::Pink),
159 "#c71585" | "c71585" | "mediumvioletred" => Some(Self::MediumVioletRed),
160 "#db7093" | "db7093" | "palevioletred" => Some(Self::PaleVioletRed),
161 "#ff1493" | "ff1493" | "deeppink" => Some(Self::DeepPink),
162 "#ff69b4" | "ff69b4" | "hotpink" => Some(Self::HotPink),
163 "#ffb6c1" | "ffb6c1" | "lightpink" => Some(Self::LightPink),
164 _ => None,
165 }
166 }
167
168 pub fn random() -> Self {
180 let mut rand = StdRand::default();
181
182 match rand.next_range(0..Self::COUNT) {
183 0 => Self::Indigo,
184 1 => Self::Purple,
185 2 => Self::DarkMagenta,
186 3 => Self::DarkViolet,
187 4 => Self::DarkSlateBlue,
188 5 => Self::BlueViolet,
189 6 => Self::DarkOrchid,
190 7 => Self::Fuchsia,
191 8 => Self::Magenta,
192 9 => Self::SlateBlue,
193 10 => Self::MediumSlateBlue,
194 11 => Self::MediumOrchid,
195 12 => Self::MediumPurple,
196 13 => Self::Orchid,
197 14 => Self::Violet,
198 15 => Self::Plum,
199 16 => Self::Thistle,
200 17 => Self::Lavender,
201 18 => Self::Pink,
202 19 => Self::MediumVioletRed,
203 20 => Self::PaleVioletRed,
204 21 => Self::DeepPink,
205 22 => Self::HotPink,
206 23 => Self::LightPink,
207 _ => Self::Purple,
208 }
209 }
210}
211
212impl FromStr for Purple {
213 type Err = String;
214 fn from_str(s: &str) -> Result<Self, Self::Err> {
215 match Self::parse(s) {
216 Some(colour) => Ok(colour),
217 None => Err(format!("Invalid Colour: {s}")),
218 }
219 }
220}
221
222impl ExtendedColour for Purple {}
223
224#[cfg(test)]
225mod tests {
226 use super::*;
227 use rstest::rstest;
228
229 #[rstest]
230 #[case(Purple::Purple, "rgb(128,0,128)")]
231 #[case(Purple::Thistle, "rgb(216,191,216)")]
232 #[case(Purple::Plum, "rgb(221,160,221)")]
233 #[case(Purple::Violet, "rgb(238,130,238)")]
234 #[case(Purple::Magenta, "rgb(255,0,255)")]
235 #[case(Purple::Fuchsia, "rgb(255,0,255)")]
236 #[case(Purple::Orchid, "rgb(218,112,214)")]
237 #[case(Purple::MediumVioletRed, "rgb(199,21,133)")]
238 #[case(Purple::PaleVioletRed, "rgb(219,112,147)")]
239 #[case(Purple::DeepPink, "rgb(255,20,147)")]
240 #[case(Purple::HotPink, "rgb(255,105,180)")]
241 #[case(Purple::LightPink, "rgb(255,182,193)")]
242 #[case(Purple::Pink, "rgb(255,192,203)")]
243 fn test_rgb_string(#[case] colour: Purple, #[case] expected: String) {
244 let rgb_colour = colour.to_rgb();
245 let string = rgb_colour.to_string();
246
247 assert_eq!(expected, string);
248 }
249
250 #[rstest]
251 #[case(Purple::Purple, "800080")]
252 #[case(Purple::Thistle, "D8BFD8")]
253 #[case(Purple::Plum, "DDA0DD")]
254 #[case(Purple::Violet, "EE82EE")]
255 #[case(Purple::Magenta, "FF00FF")]
256 #[case(Purple::Fuchsia, "FF00FF")]
257 #[case(Purple::Orchid, "DA70D6")]
258 #[case(Purple::MediumVioletRed, "C71585")]
259 #[case(Purple::PaleVioletRed, "DB7093")]
260 #[case(Purple::DeepPink, "FF1493")]
261 #[case(Purple::HotPink, "FF69B4")]
262 #[case(Purple::LightPink, "FFB6C1")]
263 #[case(Purple::Pink, "FFC0CB")]
264 fn test_hex_triplet_string(
265 #[case] colour: Purple,
266 #[values(Prefix::None, Prefix::Hash)] prefix: Prefix,
267 #[case] expected: String,
268 ) {
269 let prefix_string = match prefix {
270 Prefix::None => "".to_string(),
271 Prefix::Hash => "#".to_string(),
272 };
273
274 let expected = format!("{prefix_string}{expected}");
275
276 let hex_colour = colour.to_hex_triplet(prefix);
277
278 assert_eq!(expected, hex_colour);
279 }
280
281 #[rstest]
282 #[case("#4b0082", Purple::Indigo)]
283 #[case("4b0082", Purple::Indigo)]
284 #[case("indigo", Purple::Indigo)]
285 #[case("#800080", Purple::Purple)]
286 #[case("800080", Purple::Purple)]
287 #[case("purple", Purple::Purple)]
288 #[case("#8b008b", Purple::DarkMagenta)]
289 #[case("8b008b", Purple::DarkMagenta)]
290 #[case("darkmagenta", Purple::DarkMagenta)]
291 #[case("#9400d3", Purple::DarkViolet)]
292 #[case("9400d3", Purple::DarkViolet)]
293 #[case("darkviolet", Purple::DarkViolet)]
294 #[case("#483d8b", Purple::DarkSlateBlue)]
295 #[case("483d8b", Purple::DarkSlateBlue)]
296 #[case("darkslateblue", Purple::DarkSlateBlue)]
297 #[case("#8a2be2", Purple::BlueViolet)]
298 #[case("8a2be2", Purple::BlueViolet)]
299 #[case("blueviolet", Purple::BlueViolet)]
300 #[case("#9932cc", Purple::DarkOrchid)]
301 #[case("9932cc", Purple::DarkOrchid)]
302 #[case("darkorchid", Purple::DarkOrchid)]
303 #[case("#a020f0", Purple::Fuchsia)]
304 #[case("a020f0", Purple::Fuchsia)]
305 #[case("fuchsia", Purple::Fuchsia)]
306 #[case("#ff00ff", Purple::Magenta)]
307 #[case("ff00ff", Purple::Magenta)]
308 #[case("magenta", Purple::Magenta)]
309 #[case("#6a5acd", Purple::SlateBlue)]
310 #[case("6a5acd", Purple::SlateBlue)]
311 #[case("slateblue", Purple::SlateBlue)]
312 #[case("#7b68ee", Purple::MediumSlateBlue)]
313 #[case("7b68ee", Purple::MediumSlateBlue)]
314 #[case("mediumslateblue", Purple::MediumSlateBlue)]
315 #[case("#ba55d3", Purple::MediumOrchid)]
316 #[case("ba55d3", Purple::MediumOrchid)]
317 #[case("mediumorchid", Purple::MediumOrchid)]
318 #[case("#9370db", Purple::MediumPurple)]
319 #[case("9370db", Purple::MediumPurple)]
320 #[case("mediumpurple", Purple::MediumPurple)]
321 #[case("#da70d6", Purple::Orchid)]
322 #[case("da70d6", Purple::Orchid)]
323 #[case("orchid", Purple::Orchid)]
324 #[case("#ee82ee", Purple::Violet)]
325 #[case("ee82ee", Purple::Violet)]
326 #[case("violet", Purple::Violet)]
327 #[case("#dda0dd", Purple::Plum)]
328 #[case("dda0dd", Purple::Plum)]
329 #[case("plum", Purple::Plum)]
330 #[case("#d8bfd8", Purple::Thistle)]
331 #[case("d8bfd8", Purple::Thistle)]
332 #[case("thistle", Purple::Thistle)]
333 #[case("#e6e6fa", Purple::Lavender)]
334 #[case("e6e6fa", Purple::Lavender)]
335 #[case("lavender", Purple::Lavender)]
336 #[case("#ffc0cb", Purple::Pink)]
337 #[case("ffc0cb", Purple::Pink)]
338 #[case("pink", Purple::Pink)]
339 #[case("#c71585", Purple::MediumVioletRed)]
340 #[case("c71585", Purple::MediumVioletRed)]
341 #[case("mediumvioletred", Purple::MediumVioletRed)]
342 #[case("#db7093", Purple::PaleVioletRed)]
343 #[case("db7093", Purple::PaleVioletRed)]
344 #[case("palevioletred", Purple::PaleVioletRed)]
345 #[case("deeppink", Purple::DeepPink)]
346 #[case("ff1493", Purple::DeepPink)]
347 #[case("deeppink", Purple::DeepPink)]
348 #[case("#ff69b4", Purple::HotPink)]
349 #[case("ff69b4", Purple::HotPink)]
350 #[case("hotpink", Purple::HotPink)]
351 #[case("#ffb6c1", Purple::LightPink)]
352 #[case("ffb6c1", Purple::LightPink)]
353 #[case("lightpink", Purple::LightPink)]
354 fn test_from_str(#[case] input: &str, #[case] expected: Purple) {
355 assert_eq!(expected, Purple::from_str(input).unwrap())
356 }
357
358 #[rstest]
359 #[case("#4b0082", Some(Purple::Indigo))]
360 #[case("4b0082", Some(Purple::Indigo))]
361 #[case("indigo", Some(Purple::Indigo))]
362 #[case("#800080", Some(Purple::Purple))]
363 #[case("800080", Some(Purple::Purple))]
364 #[case("purple", Some(Purple::Purple))]
365 #[case("#8b008b", Some(Purple::DarkMagenta))]
366 #[case("8b008b", Some(Purple::DarkMagenta))]
367 #[case("darkmagenta", Some(Purple::DarkMagenta))]
368 #[case("#9400d3", Some(Purple::DarkViolet))]
369 #[case("9400d3", Some(Purple::DarkViolet))]
370 #[case("darkviolet", Some(Purple::DarkViolet))]
371 #[case("#483d8b", Some(Purple::DarkSlateBlue))]
372 #[case("483d8b", Some(Purple::DarkSlateBlue))]
373 #[case("darkslateblue", Some(Purple::DarkSlateBlue))]
374 #[case("#8a2be2", Some(Purple::BlueViolet))]
375 #[case("8a2be2", Some(Purple::BlueViolet))]
376 #[case("blueviolet", Some(Purple::BlueViolet))]
377 #[case("#9932cc", Some(Purple::DarkOrchid))]
378 #[case("9932cc", Some(Purple::DarkOrchid))]
379 #[case("darkorchid", Some(Purple::DarkOrchid))]
380 #[case("#a020f0", Some(Purple::Fuchsia))]
381 #[case("a020f0", Some(Purple::Fuchsia))]
382 #[case("fuchsia", Some(Purple::Fuchsia))]
383 #[case("#ff00ff", Some(Purple::Magenta))]
384 #[case("ff00ff", Some(Purple::Magenta))]
385 #[case("magenta", Some(Purple::Magenta))]
386 #[case("#6a5acd", Some(Purple::SlateBlue))]
387 #[case("6a5acd", Some(Purple::SlateBlue))]
388 #[case("slateblue", Some(Purple::SlateBlue))]
389 #[case("#7b68ee", Some(Purple::MediumSlateBlue))]
390 #[case("7b68ee", Some(Purple::MediumSlateBlue))]
391 #[case("mediumslateblue", Some(Purple::MediumSlateBlue))]
392 #[case("#ba55d3", Some(Purple::MediumOrchid))]
393 #[case("ba55d3", Some(Purple::MediumOrchid))]
394 #[case("mediumorchid", Some(Purple::MediumOrchid))]
395 #[case("#9370db", Some(Purple::MediumPurple))]
396 #[case("9370db", Some(Purple::MediumPurple))]
397 #[case("mediumpurple", Some(Purple::MediumPurple))]
398 #[case("#da70d6", Some(Purple::Orchid))]
399 #[case("da70d6", Some(Purple::Orchid))]
400 #[case("orchid", Some(Purple::Orchid))]
401 #[case("#ee82ee", Some(Purple::Violet))]
402 #[case("ee82ee", Some(Purple::Violet))]
403 #[case("violet", Some(Purple::Violet))]
404 #[case("#dda0dd", Some(Purple::Plum))]
405 #[case("dda0dd", Some(Purple::Plum))]
406 #[case("plum", Some(Purple::Plum))]
407 #[case("#d8bfd8", Some(Purple::Thistle))]
408 #[case("d8bfd8", Some(Purple::Thistle))]
409 #[case("thistle", Some(Purple::Thistle))]
410 #[case("#e6e6fa", Some(Purple::Lavender))]
411 #[case("e6e6fa", Some(Purple::Lavender))]
412 #[case("lavender", Some(Purple::Lavender))]
413 #[case("#ffc0cb", Some(Purple::Pink))]
414 #[case("ffc0cb", Some(Purple::Pink))]
415 #[case("pink", Some(Purple::Pink))]
416 #[case("#c71585", Some(Purple::MediumVioletRed))]
417 #[case("c71585", Some(Purple::MediumVioletRed))]
418 #[case("mediumvioletred", Some(Purple::MediumVioletRed))]
419 #[case("#db7093", Some(Purple::PaleVioletRed))]
420 #[case("db7093", Some(Purple::PaleVioletRed))]
421 #[case("palevioletred", Some(Purple::PaleVioletRed))]
422 #[case("deeppink", Some(Purple::DeepPink))]
423 #[case("ff1493", Some(Purple::DeepPink))]
424 #[case("deeppink", Some(Purple::DeepPink))]
425 #[case("#ff69b4", Some(Purple::HotPink))]
426 #[case("ff69b4", Some(Purple::HotPink))]
427 #[case("hotpink", Some(Purple::HotPink))]
428 #[case("#ffb6c1", Some(Purple::LightPink))]
429 #[case("ffb6c1", Some(Purple::LightPink))]
430 #[case("lightpink", Some(Purple::LightPink))]
431 #[case("012345", None)]
432 fn test_name_colour(#[case] input: &str, #[case] expected: Option<Purple>) {
433 assert_eq!(expected, Purple::name_colour(input))
434 }
435}