named_colour/ext/
blue.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 blue
15#[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    /// Display the colour name as an RGB Tuple
63    ///
64    /// ## Example
65    ///
66    ///```
67    /// # use named_colour::ext::Blue;
68    /// # fn main() {
69    ///    let colour = Blue::Azure;
70    ///    let rgb_colour = colour.to_rgb();
71    ///
72    ///    let string = rgb_colour.to_string();
73    ///    assert_eq!("rgb(240,255,255)", string);
74    ///
75    ///  # }
76    ///```
77    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    /// Display the colour name as an RGB Tuple
88    ///
89    /// ## Example
90    ///
91    ///```
92    /// # use named_colour::ext::Blue;
93    /// # use named_colour::Prefix;
94    ///    let colour = Blue::Azure;
95    ///
96    ///     assert_eq!("#F0FFFF", colour.to_hex_triplet(Prefix::Hash));
97    ///
98    ///```
99    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    /// Parse a colour from string
111    ///
112    /// ## Example
113    ///
114    ///```
115    /// # use named_colour::ext::Blue;
116    /// # use named_colour::Prefix;
117    ///    let colour = Blue::Azure;
118    ///
119    ///     assert_eq!(Blue::Azure, Blue::parse("#F0FFFF").unwrap());
120    ///
121    ///```
122    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    /// Generate a random colour
146    ///     
147    /// ## Example
148    ///
149    ///```
150    /// # use named_colour::ext::Blue;
151    /// # fn main() {
152    ///    let colour = Blue::random();
153    ///
154    /// # }
155    /// ```
156    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}