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 Blue {
18 PowderBlue,
19 CadetBlue,
20 SteelBlue,
21 CornflowerBlue,
22 DeepSkyBlue,
23 DodgerBlue,
24 LightBlue,
25 SkyBlue,
26 LightSkyBlue,
27 MidnightBlue,
28 Navy,
29 DarkBlue,
30 MediumBlue,
31 Blue,
32 RoyalBlue,
33 Azure,
34 LightSteelBlue,
35}
36
37impl fmt::Display for Blue {
38 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39 match self {
40 Self::PowderBlue => write!(f, "#B0E0E6"),
41 Self::CadetBlue => write!(f, "#5F9EA0"),
42 Self::SteelBlue => write!(f, "#4682B4"),
43 Self::CornflowerBlue => write!(f, "#6495ED"),
44 Self::DeepSkyBlue => write!(f, "#00BFFF"),
45 Self::DodgerBlue => write!(f, "#1E90FF"),
46 Self::LightBlue => write!(f, "#ADD8E6"),
47 Self::SkyBlue => write!(f, "#87CEEB"),
48 Self::LightSkyBlue => write!(f, "#87CEFA"),
49 Self::LightSteelBlue => write!(f, "#B0C4DE"),
50 Self::MidnightBlue => write!(f, "#191970"),
51 Self::Navy => write!(f, "#000080"),
52 Self::DarkBlue => write!(f, "#00008B"),
53 Self::MediumBlue => write!(f, "#0000CD"),
54 Self::Blue => write!(f, "#0000FF"),
55 Self::RoyalBlue => write!(f, "#4169E1"),
56 Self::Azure => write!(f, "#F0FFFF"),
57 }
58 }
59}
60
61impl Blue {
62 pub fn to_rgb(&self) -> Rgb<u8> {
78 let colour = self.to_string();
79
80 let r: u8 = u8::from_str_radix(&colour[1..3], 16).unwrap();
81 let g: u8 = u8::from_str_radix(&colour[3..5], 16).unwrap();
82 let b: u8 = u8::from_str_radix(&colour[5..7], 16).unwrap();
83
84 Rgb::new(r, g, b)
85 }
86
87 pub fn to_hex_triplet(&self, prefix: Prefix) -> String {
100 let rgb = self.to_rgb();
101
102 let prefix = match prefix {
103 Prefix::Hash => "#",
104 Prefix::None => "",
105 };
106
107 format!("{}{:02X}{:02X}{:02X}", prefix, rgb.r, rgb.g, rgb.b)
108 }
109
110 pub fn parse(name: &str) -> Option<Self> {
123 match name.to_lowercase().as_str() {
124 "#b0e0e6" | "b0e0e6" | "powderblue" => Some(Self::PowderBlue),
125 "#5f9ea0" | "5f9ea0" | "cadetblue" => Some(Self::CadetBlue),
126 "#4682b4" | "4682b4" | "steelblue" => Some(Self::SteelBlue),
127 "#6495ed" | "6495ed" | "cornflowerblue" => Some(Self::CornflowerBlue),
128 "#00bfff" | "00bfff" | "deepskyblue" => Some(Self::DeepSkyBlue),
129 "#1e90ff" | "1e90ff" | "dodgerblue" => Some(Self::DodgerBlue),
130 "#add8e6" | "add8e6" | "lightblue" => Some(Self::LightBlue),
131 "#87ceeb" | "87ceeb" | "skyblue" => Some(Self::SkyBlue),
132 "#b0c4de" | "b0c4de" | "lightsteelblue" => Some(Self::LightSteelBlue),
133 "#87cefa" | "87cefa" | "lightskyblue" => Some(Self::LightSkyBlue),
134 "#191970" | "191970" | "midnightblue" => Some(Self::MidnightBlue),
135 "#000080" | "000080" | "navy" => Some(Self::Navy),
136 "#00008b" | "00008b" | "darkblue" => Some(Self::DarkBlue),
137 "#0000cd" | "0000cd" | "mediumblue" => Some(Self::MediumBlue),
138 "#0000ff" | "0000ff" | "blue" => Some(Self::Blue),
139 "#4169e1" | "4169e1" | "royalblue" => Some(Self::RoyalBlue),
140 "#f0ffff" | "f0ffff" | "azure" => Some(Self::Azure),
141 _ => None,
142 }
143 }
144
145 pub fn random() -> Self {
157 let mut rand = StdRand::default();
158
159 match rand.next_range(0..Self::COUNT) {
160 0 => Self::PowderBlue,
161 1 => Self::CadetBlue,
162 2 => Self::SteelBlue,
163 3 => Self::CornflowerBlue,
164 4 => Self::DeepSkyBlue,
165 5 => Self::DodgerBlue,
166 6 => Self::LightBlue,
167 7 => Self::SkyBlue,
168 8 => Self::LightSkyBlue,
169 9 => Self::LightSteelBlue,
170 10 => Self::MidnightBlue,
171 11 => Self::Navy,
172 12 => Self::DarkBlue,
173 13 => Self::MediumBlue,
174 14 => Self::Blue,
175 15 => Self::RoyalBlue,
176 16 => Self::Azure,
177 _ => Self::Blue,
178 }
179 }
180}
181
182impl FromStr for Blue {
183 type Err = String;
184 fn from_str(s: &str) -> Result<Self, Self::Err> {
185 match Self::parse(s) {
186 Some(colour) => Ok(colour),
187 None => Err(format!("Invalid Colour: {s}")),
188 }
189 }
190}
191
192impl ExtendedColour for Blue {}
193
194#[cfg(test)]
195mod tests {
196 use super::*;
197 use rstest::rstest;
198
199 #[rstest]
200 #[case(Blue::PowderBlue, "rgb(176,224,230)")]
201 #[case(Blue::CadetBlue, "rgb(95,158,160)")]
202 #[case(Blue::SteelBlue, "rgb(70,130,180)")]
203 #[case(Blue::CornflowerBlue, "rgb(100,149,237)")]
204 #[case(Blue::DeepSkyBlue, "rgb(0,191,255)")]
205 #[case(Blue::DodgerBlue, "rgb(30,144,255)")]
206 #[case(Blue::LightBlue, "rgb(173,216,230)")]
207 #[case(Blue::SkyBlue, "rgb(135,206,235)")]
208 #[case(Blue::LightSteelBlue, "rgb(176,196,222)")]
209 #[case(Blue::LightSkyBlue, "rgb(135,206,250)")]
210 #[case(Blue::MidnightBlue, "rgb(25,25,112)")]
211 #[case(Blue::Navy, "rgb(0,0,128)")]
212 #[case(Blue::DarkBlue, "rgb(0,0,139)")]
213 #[case(Blue::MediumBlue, "rgb(0,0,205)")]
214 #[case(Blue::Blue, "rgb(0,0,255)")]
215 #[case(Blue::RoyalBlue, "rgb(65,105,225)")]
216 #[case(Blue::Azure, "rgb(240,255,255)")]
217
218 fn test_rgb_string(#[case] colour: Blue, #[case] expected: String) {
219 let rgb_colour = colour.to_rgb();
220 let string = rgb_colour.to_string();
221
222 assert_eq!(expected, string);
223 }
224
225 #[rstest]
226 #[case(Blue::PowderBlue, "B0E0E6")]
227 #[case(Blue::CadetBlue, "5F9EA0")]
228 #[case(Blue::SteelBlue, "4682B4")]
229 #[case(Blue::CornflowerBlue, "6495ED")]
230 #[case(Blue::DeepSkyBlue, "00BFFF")]
231 #[case(Blue::DodgerBlue, "1E90FF")]
232 #[case(Blue::LightBlue, "ADD8E6")]
233 #[case(Blue::SkyBlue, "87CEEB")]
234 #[case(Blue::LightSteelBlue, "B0C4DE")]
235 #[case(Blue::LightSkyBlue, "87CEFA")]
236 #[case(Blue::MidnightBlue, "191970")]
237 #[case(Blue::Navy, "000080")]
238 #[case(Blue::DarkBlue, "00008B")]
239 #[case(Blue::MediumBlue, "0000CD")]
240 #[case(Blue::Blue, "0000FF")]
241 #[case(Blue::RoyalBlue, "4169E1")]
242 #[case(Blue::Azure, "F0FFFF")]
243
244 fn test_hex_triplet_string(
245 #[case] colour: Blue,
246 #[values(Prefix::None, Prefix::Hash)] prefix: Prefix,
247 #[case] expected: String,
248 ) {
249 let prefix_string = match prefix {
250 Prefix::None => "".to_string(),
251 Prefix::Hash => "#".to_string(),
252 };
253
254 let expected = format!("{prefix_string}{expected}");
255
256 let hex_colour = colour.to_hex_triplet(prefix);
257
258 assert_eq!(expected, hex_colour);
259 }
260
261 #[rstest]
262 #[case("#b0e0e6", Blue::PowderBlue)]
263 #[case("b0e0e6", Blue::PowderBlue)]
264 #[case("powderblue", Blue::PowderBlue)]
265 #[case("#5f9ea0", Blue::CadetBlue)]
266 #[case("5f9ea0", Blue::CadetBlue)]
267 #[case("cadetblue", Blue::CadetBlue)]
268 #[case("#4682b4", Blue::SteelBlue)]
269 #[case("4682b4", Blue::SteelBlue)]
270 #[case("steelblue", Blue::SteelBlue)]
271 #[case("#6495ed", Blue::CornflowerBlue)]
272 #[case("6495ed", Blue::CornflowerBlue)]
273 #[case("cornflowerblue", Blue::CornflowerBlue)]
274 #[case("#00bfff", Blue::DeepSkyBlue)]
275 #[case("00bfff", Blue::DeepSkyBlue)]
276 #[case("deepskyblue", Blue::DeepSkyBlue)]
277 #[case("#1e90ff", Blue::DodgerBlue)]
278 #[case("1e90ff", Blue::DodgerBlue)]
279 #[case("dodgerblue", Blue::DodgerBlue)]
280 #[case("#add8e6", Blue::LightBlue)]
281 #[case("add8e6", Blue::LightBlue)]
282 #[case("lightblue", Blue::LightBlue)]
283 #[case("#87ceeb", Blue::SkyBlue)]
284 #[case("87ceeb", Blue::SkyBlue)]
285 #[case("skyblue", Blue::SkyBlue)]
286 #[case("#b0c4de", Blue::LightSteelBlue)]
287 #[case("b0c4de", Blue::LightSteelBlue)]
288 #[case("lightsteelblue", Blue::LightSteelBlue)]
289 #[case("#87cefa", Blue::LightSkyBlue)]
290 #[case("87cefa", Blue::LightSkyBlue)]
291 #[case("lightskyblue", Blue::LightSkyBlue)]
292 #[case("#191970", Blue::MidnightBlue)]
293 #[case("191970", Blue::MidnightBlue)]
294 #[case("midnightblue", Blue::MidnightBlue)]
295 #[case("#000080", Blue::Navy)]
296 #[case("000080", Blue::Navy)]
297 #[case("navy", Blue::Navy)]
298 #[case("#00008b", Blue::DarkBlue)]
299 #[case("00008b", Blue::DarkBlue)]
300 #[case("darkblue", Blue::DarkBlue)]
301 #[case("#0000cd", Blue::MediumBlue)]
302 #[case("0000cd", Blue::MediumBlue)]
303 #[case("mediumblue", Blue::MediumBlue)]
304 #[case("#0000ff", Blue::Blue)]
305 #[case("0000ff", Blue::Blue)]
306 #[case("blue", Blue::Blue)]
307 #[case("#4169e1", Blue::RoyalBlue)]
308 #[case("4169e1", Blue::RoyalBlue)]
309 #[case("royalblue", Blue::RoyalBlue)]
310 #[case("#f0ffff", Blue::Azure)]
311 #[case("f0ffff", Blue::Azure)]
312 #[case("azure", Blue::Azure)]
313 fn test_from_str(#[case] input: &str, #[case] expected: Blue) {
314 assert_eq!(expected, Blue::from_str(input).unwrap())
315 }
316
317 #[rstest]
318 #[case("#b0e0e6", Some(Blue::PowderBlue))]
319 #[case("b0e0e6", Some(Blue::PowderBlue))]
320 #[case("powderblue", Some(Blue::PowderBlue))]
321 #[case("#5f9ea0", Some(Blue::CadetBlue))]
322 #[case("5f9ea0", Some(Blue::CadetBlue))]
323 #[case("cadetblue", Some(Blue::CadetBlue))]
324 #[case("#4682b4", Some(Blue::SteelBlue))]
325 #[case("4682b4", Some(Blue::SteelBlue))]
326 #[case("steelblue", Some(Blue::SteelBlue))]
327 #[case("#6495ed", Some(Blue::CornflowerBlue))]
328 #[case("6495ed", Some(Blue::CornflowerBlue))]
329 #[case("cornflowerblue", Some(Blue::CornflowerBlue))]
330 #[case("#00bfff", Some(Blue::DeepSkyBlue))]
331 #[case("00bfff", Some(Blue::DeepSkyBlue))]
332 #[case("deepskyblue", Some(Blue::DeepSkyBlue))]
333 #[case("#1e90ff", Some(Blue::DodgerBlue))]
334 #[case("1e90ff", Some(Blue::DodgerBlue))]
335 #[case("dodgerblue", Some(Blue::DodgerBlue))]
336 #[case("#add8e6", Some(Blue::LightBlue))]
337 #[case("add8e6", Some(Blue::LightBlue))]
338 #[case("lightblue", Some(Blue::LightBlue))]
339 #[case("#87ceeb", Some(Blue::SkyBlue))]
340 #[case("87ceeb", Some(Blue::SkyBlue))]
341 #[case("skyblue", Some(Blue::SkyBlue))]
342 #[case("#b0c4de", Some(Blue::LightSteelBlue))]
343 #[case("b0c4de", Some(Blue::LightSteelBlue))]
344 #[case("lightsteelblue", Some(Blue::LightSteelBlue))]
345 #[case("#87cefa", Some(Blue::LightSkyBlue))]
346 #[case("87cefa", Some(Blue::LightSkyBlue))]
347 #[case("lightskyblue", Some(Blue::LightSkyBlue))]
348 #[case("#191970", Some(Blue::MidnightBlue))]
349 #[case("191970", Some(Blue::MidnightBlue))]
350 #[case("midnightblue", Some(Blue::MidnightBlue))]
351 #[case("#000080", Some(Blue::Navy))]
352 #[case("000080", Some(Blue::Navy))]
353 #[case("navy", Some(Blue::Navy))]
354 #[case("#00008b", Some(Blue::DarkBlue))]
355 #[case("00008b", Some(Blue::DarkBlue))]
356 #[case("darkblue", Some(Blue::DarkBlue))]
357 #[case("#0000cd", Some(Blue::MediumBlue))]
358 #[case("0000cd", Some(Blue::MediumBlue))]
359 #[case("mediumblue", Some(Blue::MediumBlue))]
360 #[case("#0000ff", Some(Blue::Blue))]
361 #[case("0000ff", Some(Blue::Blue))]
362 #[case("blue", Some(Blue::Blue))]
363 #[case("#4169e1", Some(Blue::RoyalBlue))]
364 #[case("4169e1", Some(Blue::RoyalBlue))]
365 #[case("royalblue", Some(Blue::RoyalBlue))]
366 #[case("#f0ffff", Some(Blue::Azure))]
367 #[case("f0ffff", Some(Blue::Azure))]
368 #[case("azure", Some(Blue::Azure))]
369 #[case("012345", None)]
370 fn test_name_colour(#[case] input: &str, #[case] expected: Option<Blue>) {
371 assert_eq!(expected, Blue::name_colour(input))
372 }
373}