1use std::fmt;
5
6use rgb::Rgb;
7use strum::EnumCount;
8use tinyrand::{RandRange, StdRand};
9
10use super::ExtendedColour;
11
12#[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 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 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}