Skip to main content

named_colour/ext/
red.rs

1//! Extended named colours providing shades collected in enums for the main colour
2//!
3
4use std::fmt;
5
6use rgb::Rgb;
7use strum::EnumCount;
8use tinyrand::{RandRange, StdRand};
9
10use super::ExtendedColour;
11
12/// Shades of red
13#[allow(missing_docs)]
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, EnumCount)]
15pub enum Red {
16    Maroon,
17    #[allow(clippy::enum_variant_names)]
18    DarkRed,
19    Brown,
20    Firebrick,
21    Crimson,
22    #[allow(clippy::enum_variant_names)]
23    Red,
24    Tomato,
25    Coral,
26    #[allow(clippy::enum_variant_names)]
27    IndianRed,
28    LightCoral,
29    DarkSalmon,
30    Salmon,
31    LightSalmon,
32    #[allow(clippy::enum_variant_names)]
33    OrangeRed,
34    DarkOrange,
35    Orange,
36}
37
38impl fmt::Display for Red {
39    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40        match self {
41            Self::Maroon => write!(f, "#800000"),
42            Self::DarkRed => write!(f, "#8B0000"),
43            Self::Brown => write!(f, "#A52A2A"),
44            Self::Firebrick => write!(f, "#B22222"),
45            Self::Crimson => write!(f, "#DC143C"),
46            Self::Red => write!(f, "#FF0000"),
47            Self::Tomato => write!(f, "#FF6347"),
48            Self::Coral => write!(f, "#FF7F50"),
49            Self::IndianRed => write!(f, "#CD5C5C"),
50            Self::LightCoral => write!(f, "#F08080"),
51            Self::DarkSalmon => write!(f, "#E9967A"),
52            Self::Salmon => write!(f, "#FA8072"),
53            Self::LightSalmon => write!(f, "#FFA07A"),
54            Self::OrangeRed => write!(f, "#FF4500"),
55            Self::DarkOrange => write!(f, "#FF8C00"),
56            Self::Orange => write!(f, "#FFA500"),
57        }
58    }
59}
60
61impl_colour_methods!(Red);
62
63impl Red {
64    /// Parse a colour from string
65    ///
66    /// ## Example
67    ///
68    ///```
69    /// # use named_colour::ext::Red;
70    /// # use std::str::FromStr;
71    /// # fn main() {
72    ///    let colour = Red::from_str("Maroon");
73    ///    assert_eq!(Red::Maroon, colour.unwrap());
74    ///
75    ///  # }
76    ///```      
77    ///
78    pub fn parse(name: &str) -> Option<Self> {
79        match name.to_lowercase().as_str() {
80            "#800000" | "800000" | "maroon" => Some(Self::Maroon),
81            "#8b0000" | "8b0000" | "darkred" => Some(Self::DarkRed),
82            "#a52a2a" | "a52a2a" | "brown" => Some(Self::Brown),
83            "#b22222" | "b22222" | "firebrick" => Some(Self::Firebrick),
84            "#dc143c" | "dc143c" | "crimson" => Some(Self::Crimson),
85            "#ff0000" | "ff0000" | "red" => Some(Self::Red),
86            "#ff6347" | "ff6347" | "tomato" => Some(Self::Tomato),
87            "#ff7f50" | "ff7f50" | "coral" => Some(Self::Coral),
88            "#cd5c5c" | "cd5c5c" | "indianred" => Some(Self::IndianRed),
89            "#f08080" | "f08080" | "lightcoral" => Some(Self::LightCoral),
90            "#e9967a" | "e9967a" | "darksalmon" => Some(Self::DarkSalmon),
91            "#fa8072" | "fa8072" | "salmon" => Some(Self::Salmon),
92            "#ffa07a" | "ffa07a" | "lightsalmon" => Some(Self::LightSalmon),
93            "#ff4500" | "ff4500" | "orangered" => Some(Self::OrangeRed),
94            "#ff8c00" | "ff8c00" | "darkorange" => Some(Self::DarkOrange),
95            "#ffa500" | "ffa500" | "orange" => Some(Self::Orange),
96            _ => None,
97        }
98    }
99
100    /// Generate a random colour
101    ///     
102    /// ## Example
103    ///
104    ///```
105    /// # use named_colour::ext::Red;
106    /// # fn main() {
107    ///    let colour = Red::random();
108    ///
109    /// # }
110    /// ```
111    pub fn random() -> Self {
112        let mut rand = StdRand::default();
113
114        match rand.next_range(0..Self::COUNT) {
115            0 => Self::Maroon,
116            1 => Self::DarkRed,
117            2 => Self::Brown,
118            3 => Self::Firebrick,
119            4 => Self::Crimson,
120            5 => Self::Red,
121            6 => Self::Tomato,
122            7 => Self::Coral,
123            8 => Self::IndianRed,
124            9 => Self::LightCoral,
125            10 => Self::DarkSalmon,
126            11 => Self::Salmon,
127            12 => Self::LightSalmon,
128            13 => Self::OrangeRed,
129            14 => Self::DarkOrange,
130            15 => Self::Orange,
131            _ => Self::Red,
132        }
133    }
134}
135
136impl ExtendedColour for Red {}
137
138#[cfg(test)]
139mod tests {
140    use std::str::FromStr;
141
142    use crate::Prefix;
143
144    use super::*;
145    use rstest::rstest;
146
147    #[rstest]
148    #[case(Red::Maroon, "rgb(128,0,0)")]
149    #[case(Red::DarkRed, "rgb(139,0,0)")]
150    #[case(Red::Brown, "rgb(165,42,42)")]
151    #[case(Red::Firebrick, "rgb(178,34,34)")]
152    #[case(Red::Crimson, "rgb(220,20,60)")]
153    #[case(Red::Red, "rgb(255,0,0)")]
154    #[case(Red::Tomato, "rgb(255,99,71)")]
155    #[case(Red::Coral, "rgb(255,127,80)")]
156    #[case(Red::IndianRed, "rgb(205,92,92)")]
157    #[case(Red::LightCoral, "rgb(240,128,128)")]
158    #[case(Red::DarkSalmon, "rgb(233,150,122)")]
159    #[case(Red::Salmon, "rgb(250,128,114)")]
160    #[case(Red::LightSalmon, "rgb(255,160,122)")]
161    #[case(Red::OrangeRed, "rgb(255,69,0)")]
162    #[case(Red::DarkOrange, "rgb(255,140,0)")]
163    #[case(Red::Orange, "rgb(255,165,0)")]
164    fn test_rgb_string(#[case] colour: Red, #[case] expected: String) {
165        let rgb_colour = colour.to_rgb();
166        let string = rgb_colour.to_string();
167
168        assert_eq!(expected, string);
169    }
170
171    #[rstest]
172    #[case(Red::Maroon, "800000")]
173    #[case(Red::DarkRed, "8B0000")]
174    #[case(Red::Brown, "A52A2A")]
175    #[case(Red::Firebrick, "B22222")]
176    #[case(Red::Crimson, "DC143C")]
177    #[case(Red::Red, "FF0000")]
178    #[case(Red::Tomato, "FF6347")]
179    #[case(Red::Coral, "FF7F50")]
180    #[case(Red::IndianRed, "CD5C5C")]
181    #[case(Red::LightCoral, "F08080")]
182    #[case(Red::DarkSalmon, "E9967A")]
183    #[case(Red::Salmon, "FA8072")]
184    #[case(Red::LightSalmon, "FFA07A")]
185    #[case(Red::OrangeRed, "FF4500")]
186    #[case(Red::DarkOrange, "FF8C00")]
187    #[case(Red::Orange, "FFA500")]
188    fn test_hex_triplet_string(
189        #[case] colour: Red,
190        #[values(Prefix::None, Prefix::Hash)] prefix: Prefix,
191        #[case] expected: String,
192    ) {
193        let prefix_string = match prefix {
194            Prefix::None => "".to_string(),
195            Prefix::Hash => "#".to_string(),
196        };
197
198        let expected = format!("{prefix_string}{expected}");
199
200        let hex_colour = colour.to_hex_triplet(prefix);
201
202        assert_eq!(expected, hex_colour);
203    }
204
205    #[rstest]
206    #[case("#800000", Red::Maroon)]
207    #[case("800000", Red::Maroon)]
208    #[case("maroon", Red::Maroon)]
209    #[case("#8b0000", Red::DarkRed)]
210    #[case("8b0000", Red::DarkRed)]
211    #[case("darkred", Red::DarkRed)]
212    #[case("#a52a2a", Red::Brown)]
213    #[case("a52a2a", Red::Brown)]
214    #[case("brown", Red::Brown)]
215    #[case("#b22222", Red::Firebrick)]
216    #[case("b22222", Red::Firebrick)]
217    #[case("firebrick", Red::Firebrick)]
218    #[case("#dc143c", Red::Crimson)]
219    #[case("dc143c", Red::Crimson)]
220    #[case("crimson", Red::Crimson)]
221    #[case("#ff0000", Red::Red)]
222    #[case("ff0000", Red::Red)]
223    #[case("red", Red::Red)]
224    #[case("#ff6347", Red::Tomato)]
225    #[case("ff6347", Red::Tomato)]
226    #[case("tomato", Red::Tomato)]
227    #[case("#ff7f50", Red::Coral)]
228    #[case("ff7f50", Red::Coral)]
229    #[case("coral", Red::Coral)]
230    #[case("#cd5c5c", Red::IndianRed)]
231    #[case("cd5c5c", Red::IndianRed)]
232    #[case("indianred", Red::IndianRed)]
233    #[case("#f08080", Red::LightCoral)]
234    #[case("f08080", Red::LightCoral)]
235    #[case("lightcoral", Red::LightCoral)]
236    #[case("#e9967a", Red::DarkSalmon)]
237    #[case("e9967a", Red::DarkSalmon)]
238    #[case("darksalmon", Red::DarkSalmon)]
239    #[case("#fa8072", Red::Salmon)]
240    #[case("fa8072", Red::Salmon)]
241    #[case("salmon", Red::Salmon)]
242    #[case("#ffa07a", Red::LightSalmon)]
243    #[case("ffa07a", Red::LightSalmon)]
244    #[case("lightsalmon", Red::LightSalmon)]
245    #[case("#ff4500", Red::OrangeRed)]
246    #[case("ff4500", Red::OrangeRed)]
247    #[case("orangered", Red::OrangeRed)]
248    #[case("#ff8c00", Red::DarkOrange)]
249    #[case("ff8c00", Red::DarkOrange)]
250    #[case("darkorange", Red::DarkOrange)]
251    #[case("#ffa500", Red::Orange)]
252    #[case("ffa500", Red::Orange)]
253    #[case("orange", Red::Orange)]
254    fn test_from_str(#[case] input: &str, #[case] expected: Red) {
255        assert_eq!(expected, Red::from_str(input).unwrap())
256    }
257
258    #[rstest]
259    #[case("#800000", Some(Red::Maroon))]
260    #[case("800000", Some(Red::Maroon))]
261    #[case("maroon", Some(Red::Maroon))]
262    #[case("#8b0000", Some(Red::DarkRed))]
263    #[case("8b0000", Some(Red::DarkRed))]
264    #[case("darkred", Some(Red::DarkRed))]
265    #[case("#a52a2a", Some(Red::Brown))]
266    #[case("a52a2a", Some(Red::Brown))]
267    #[case("brown", Some(Red::Brown))]
268    #[case("#b22222", Some(Red::Firebrick))]
269    #[case("b22222", Some(Red::Firebrick))]
270    #[case("firebrick", Some(Red::Firebrick))]
271    #[case("#dc143c", Some(Red::Crimson))]
272    #[case("dc143c", Some(Red::Crimson))]
273    #[case("crimson", Some(Red::Crimson))]
274    #[case("#ff0000", Some(Red::Red))]
275    #[case("ff0000", Some(Red::Red))]
276    #[case("red", Some(Red::Red))]
277    #[case("#ff6347", Some(Red::Tomato))]
278    #[case("ff6347", Some(Red::Tomato))]
279    #[case("tomato", Some(Red::Tomato))]
280    #[case("#ff7f50", Some(Red::Coral))]
281    #[case("ff7f50", Some(Red::Coral))]
282    #[case("coral", Some(Red::Coral))]
283    #[case("#cd5c5c", Some(Red::IndianRed))]
284    #[case("cd5c5c", Some(Red::IndianRed))]
285    #[case("indianred", Some(Red::IndianRed))]
286    #[case("#f08080", Some(Red::LightCoral))]
287    #[case("f08080", Some(Red::LightCoral))]
288    #[case("lightcoral", Some(Red::LightCoral))]
289    #[case("#e9967a", Some(Red::DarkSalmon))]
290    #[case("e9967a", Some(Red::DarkSalmon))]
291    #[case("darksalmon", Some(Red::DarkSalmon))]
292    #[case("#fa8072", Some(Red::Salmon))]
293    #[case("fa8072", Some(Red::Salmon))]
294    #[case("salmon", Some(Red::Salmon))]
295    #[case("#ffa07a", Some(Red::LightSalmon))]
296    #[case("ffa07a", Some(Red::LightSalmon))]
297    #[case("lightsalmon", Some(Red::LightSalmon))]
298    #[case("#ff4500", Some(Red::OrangeRed))]
299    #[case("ff4500", Some(Red::OrangeRed))]
300    #[case("orangered", Some(Red::OrangeRed))]
301    #[case("#ff8c00", Some(Red::DarkOrange))]
302    #[case("ff8c00", Some(Red::DarkOrange))]
303    #[case("darkorange", Some(Red::DarkOrange))]
304    #[case("#ffa500", Some(Red::Orange))]
305    #[case("ffa500", Some(Red::Orange))]
306    #[case("orange", Some(Red::Orange))]
307    #[case("012345", None)]
308    fn test_name_colour(#[case] input: &str, #[case] expected: Option<Red>) {
309        assert_eq!(expected, Red::name_colour(input))
310    }
311}