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 Green {
18 YellowGreen,
19 DarkOliveGreen,
20 Olive,
21 OliveDrab,
22 LawnGreen,
23 ChartReuse,
24 GreenYellow,
25 DarkGreen,
26 Green,
27 ForestGreen,
28 Lime,
29 LimeGreen,
30 LightGreen,
31 PaleGreen,
32 DarkSeaGreen,
33 MediumSpringGreen,
34 SpringGreen,
35 SeaGreen,
36}
37
38impl fmt::Display for Green {
39 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40 match self {
41 Self::YellowGreen => write!(f, "#9ACD32"),
42 Self::DarkOliveGreen => write!(f, "#556B2F"),
43 Self::Olive => write!(f, "#808000"),
44 Self::OliveDrab => write!(f, "#6B8E23"),
45 Self::LawnGreen => write!(f, "#7CFC00"),
46 Self::ChartReuse => write!(f, "#7FFF00"),
47 Self::GreenYellow => write!(f, "#ADFF2F"),
48 Self::DarkGreen => write!(f, "#006400"),
49 Self::Green => write!(f, "#008000"),
50 Self::ForestGreen => write!(f, "#228B22"),
51 Self::Lime => write!(f, "#00FF00"),
52 Self::LimeGreen => write!(f, "#32CD32"),
53 Self::LightGreen => write!(f, "#90EE90"),
54 Self::PaleGreen => write!(f, "#98FB98"),
55 Self::DarkSeaGreen => write!(f, "#8FBC8F"),
56 Self::MediumSpringGreen => write!(f, "#00FA9A"),
57 Self::SpringGreen => write!(f, "#00FF7F"),
58 Self::SeaGreen => write!(f, "#2E8B57"),
59 }
60 }
61}
62
63impl Green {
64 pub fn to_rgb(&self) -> Rgb<u8> {
80 let colour = self.to_string();
81
82 let r: u8 = u8::from_str_radix(&colour[1..3], 16).unwrap();
83 let g: u8 = u8::from_str_radix(&colour[3..5], 16).unwrap();
84 let b: u8 = u8::from_str_radix(&colour[5..7], 16).unwrap();
85
86 Rgb::new(r, g, b)
87 }
88
89 pub fn to_hex_triplet(&self, prefix: Prefix) -> String {
102 let rgb = self.to_rgb();
103
104 let prefix = match prefix {
105 Prefix::Hash => "#",
106 Prefix::None => "",
107 };
108
109 format!("{}{:02X}{:02X}{:02X}", prefix, rgb.r, rgb.g, rgb.b)
110 }
111 pub fn parse(name: &str) -> Option<Self> {
124 match name.to_lowercase().as_str() {
125 "#9acd32" | "9acd32" | "yellowgreen" => Some(Self::YellowGreen),
126 "#556b2f" | "556b2f" | "darkolivegreen" => Some(Self::DarkOliveGreen),
127 "#808000" | "808000" | "olive" => Some(Self::Olive),
128 "#6b8e23" | "6b8e23" | "olivedrab" => Some(Self::OliveDrab),
129 "#7cfc00" | "7cfc00" | "lawngreen" => Some(Self::LawnGreen),
130 "#7fff00" | "7fff00" | "chartreuse" => Some(Self::ChartReuse),
131 "#adff2f" | "adff2f" | "greenyellow" => Some(Self::GreenYellow),
132 "#006400" | "006400" | "darkgreen" => Some(Self::DarkGreen),
133 "#008000" | "008000" | "green" => Some(Self::Green),
134 "#228b22" | "228b22" | "forestgreen" => Some(Self::ForestGreen),
135 "#32cd32" | "32cd32" | "limegreen" => Some(Self::LimeGreen),
136 "#90ee90" | "90ee90" | "lightgreen" => Some(Self::LightGreen),
137 "#98fb98" | "98fb98" | "palegreen" => Some(Self::PaleGreen),
138 "#8fbc8f" | "8fbc8f" | "darkseagreen" => Some(Self::DarkSeaGreen),
139 "#00fa9a" | "00fa9a" | "mediumspringgreen" => Some(Self::MediumSpringGreen),
140 "#00ff7f" | "00ff7f" | "springgreen" => Some(Self::SpringGreen),
141 "#2e8b57" | "2e8b57" | "seagreen" => Some(Self::SeaGreen),
142 "#00ff00" | "00ff00" | "lime" => Some(Self::Lime),
143 _ => None,
144 }
145 }
146
147 pub fn random() -> Self {
159 let mut rand = StdRand::default();
160
161 match rand.next_range(0..Self::COUNT) {
162 0 => Self::YellowGreen,
163 1 => Self::DarkOliveGreen,
164 2 => Self::Olive,
165 3 => Self::OliveDrab,
166 4 => Self::LawnGreen,
167 5 => Self::ChartReuse,
168 6 => Self::GreenYellow,
169 7 => Self::DarkGreen,
170 8 => Self::Green,
171 9 => Self::ForestGreen,
172 10 => Self::Lime,
173 11 => Self::LimeGreen,
174 12 => Self::LightGreen,
175 13 => Self::PaleGreen,
176 14 => Self::DarkSeaGreen,
177 15 => Self::MediumSpringGreen,
178 16 => Self::SpringGreen,
179 17 => Self::SeaGreen,
180 _ => Self::Green,
181 }
182 }
183}
184
185impl FromStr for Green {
186 type Err = String;
187 fn from_str(s: &str) -> Result<Self, Self::Err> {
188 match Self::parse(s) {
189 Some(colour) => Ok(colour),
190 None => Err(format!("Invalid Colour: {}", s)),
191 }
192 }
193}
194
195impl ExtendedColour for Green {}
196
197#[cfg(test)]
198mod tests {
199 use super::*;
200 use rstest::rstest;
201
202 #[rstest]
203 #[case(Green::YellowGreen, "rgb(154,205,50)")]
204 #[case(Green::DarkOliveGreen, "rgb(85,107,47)")]
205 #[case(Green::OliveDrab, "rgb(107,142,35)")]
206 #[case(Green::LawnGreen, "rgb(124,252,0)")]
207 #[case(Green::ChartReuse, "rgb(127,255,0)")]
208 #[case(Green::GreenYellow, "rgb(173,255,47)")]
209 #[case(Green::DarkGreen, "rgb(0,100,0)")]
210 #[case(Green::Green, "rgb(0,128,0)")]
211 #[case(Green::ForestGreen, "rgb(34,139,34)")]
212 #[case(Green::Lime, "rgb(0,255,0)")]
213 #[case(Green::LimeGreen, "rgb(50,205,50)")]
214 #[case(Green::LightGreen, "rgb(144,238,144)")]
215 #[case(Green::PaleGreen, "rgb(152,251,152)")]
216 #[case(Green::DarkSeaGreen, "rgb(143,188,143)")]
217 #[case(Green::MediumSpringGreen, "rgb(0,250,154)")]
218 #[case(Green::SpringGreen, "rgb(0,255,127)")]
219 #[case(Green::SeaGreen, "rgb(46,139,87)")]
220 fn test_rgb_string(#[case] colour: Green, #[case] expected: String) {
221 let rgb_colour = colour.to_rgb();
222 let string = rgb_colour.to_string();
223
224 assert_eq!(expected, string);
225 }
226
227 #[rstest]
228 #[case(Green::YellowGreen, "9ACD32")]
229 #[case(Green::DarkOliveGreen, "556B2F")]
230 #[case(Green::OliveDrab, "6B8E23")]
231 #[case(Green::LawnGreen, "7CFC00")]
232 #[case(Green::ChartReuse, "7FFF00")]
233 #[case(Green::GreenYellow, "ADFF2F")]
234 #[case(Green::DarkGreen, "006400")]
235 #[case(Green::Green, "008000")]
236 #[case(Green::ForestGreen, "228B22")]
237 #[case(Green::Lime, "00FF00")]
238 #[case(Green::LimeGreen, "32CD32")]
239 #[case(Green::LightGreen, "90EE90")]
240 #[case(Green::PaleGreen, "98FB98")]
241 #[case(Green::DarkSeaGreen, "8FBC8F")]
242 #[case(Green::MediumSpringGreen, "00FA9A")]
243 #[case(Green::SpringGreen, "00FF7F")]
244 #[case(Green::SeaGreen, "2E8B57")]
245 fn test_hex_triplet_string(
246 #[case] colour: Green,
247 #[values(Prefix::None, Prefix::Hash)] prefix: Prefix,
248 #[case] expected: String,
249 ) {
250 let prefix_string = match prefix {
251 Prefix::None => "".to_string(),
252 Prefix::Hash => "#".to_string(),
253 };
254
255 let expected = format!("{}{}", prefix_string, expected);
256
257 let hex_colour = colour.to_hex_triplet(prefix);
258
259 assert_eq!(expected, hex_colour);
260 }
261
262 #[rstest]
263 #[case("#9acd32", Green::YellowGreen)]
264 #[case("9acd32", Green::YellowGreen)]
265 #[case("yellowgreen", Green::YellowGreen)]
266 #[case("#556b2f", Green::DarkOliveGreen)]
267 #[case("556b2f", Green::DarkOliveGreen)]
268 #[case("darkolivegreen", Green::DarkOliveGreen)]
269 #[case("#808000", Green::Olive)]
270 #[case("808000", Green::Olive)]
271 #[case("olive", Green::Olive)]
272 #[case("#6b8e23", Green::OliveDrab)]
273 #[case("6b8e23", Green::OliveDrab)]
274 #[case("olivedrab", Green::OliveDrab)]
275 #[case("#7cfc00", Green::LawnGreen)]
276 #[case("7cfc00", Green::LawnGreen)]
277 #[case("lawngreen", Green::LawnGreen)]
278 #[case("#7fff00", Green::ChartReuse)]
279 #[case("7fff00", Green::ChartReuse)]
280 #[case("chartreuse", Green::ChartReuse)]
281 #[case("#adff2f", Green::GreenYellow)]
282 #[case("adff2f", Green::GreenYellow)]
283 #[case("greenyellow", Green::GreenYellow)]
284 #[case("#008000", Green::Green)]
285 #[case("008000", Green::Green)]
286 #[case("green", Green::Green)]
287 #[case("#228b22", Green::ForestGreen)]
288 #[case("228b22", Green::ForestGreen)]
289 #[case("forestgreen", Green::ForestGreen)]
290 #[case("#00ff7f", Green::SpringGreen)]
291 #[case("00ff7f", Green::SpringGreen)]
292 #[case("springgreen", Green::SpringGreen)]
293 #[case("#98fb98", Green::PaleGreen)]
294 #[case("98fb98", Green::PaleGreen)]
295 #[case("palegreen", Green::PaleGreen)]
296 #[case("#8fbc8f", Green::DarkSeaGreen)]
297 #[case("8fbc8f", Green::DarkSeaGreen)]
298 #[case("darkseagreen", Green::DarkSeaGreen)]
299 #[case("#00fa9a", Green::MediumSpringGreen)]
300 #[case("00fa9a", Green::MediumSpringGreen)]
301 #[case("mediumspringgreen", Green::MediumSpringGreen)]
302 #[case("#2e8b57", Green::SeaGreen)]
303 #[case("2e8b57", Green::SeaGreen)]
304 #[case("seagreen", Green::SeaGreen)]
305 fn test_from_str(#[case] input: &str, #[case] expected: Green) {
306 assert_eq!(expected, Green::from_str(input).unwrap())
307 }
308
309 #[rstest]
310 #[case("#9acd32", Some(Green::YellowGreen))]
311 #[case("9acd32", Some(Green::YellowGreen))]
312 #[case("yellowgreen", Some(Green::YellowGreen))]
313 #[case("#556b2f", Some(Green::DarkOliveGreen))]
314 #[case("556b2f", Some(Green::DarkOliveGreen))]
315 #[case("darkolivegreen", Some(Green::DarkOliveGreen))]
316 #[case("#808000", Some(Green::Olive))]
317 #[case("808000", Some(Green::Olive))]
318 #[case("olive", Some(Green::Olive))]
319 #[case("#6b8e23", Some(Green::OliveDrab))]
320 #[case("6b8e23", Some(Green::OliveDrab))]
321 #[case("olivedrab", Some(Green::OliveDrab))]
322 #[case("#7cfc00", Some(Green::LawnGreen))]
323 #[case("7cfc00", Some(Green::LawnGreen))]
324 #[case("lawngreen", Some(Green::LawnGreen))]
325 #[case("#7fff00", Some(Green::ChartReuse))]
326 #[case("7fff00", Some(Green::ChartReuse))]
327 #[case("chartreuse", Some(Green::ChartReuse))]
328 #[case("#adff2f", Some(Green::GreenYellow))]
329 #[case("adff2f", Some(Green::GreenYellow))]
330 #[case("greenyellow", Some(Green::GreenYellow))]
331 #[case("#008000", Some(Green::Green))]
332 #[case("008000", Some(Green::Green))]
333 #[case("green", Some(Green::Green))]
334 #[case("#228b22", Some(Green::ForestGreen))]
335 #[case("228b22", Some(Green::ForestGreen))]
336 #[case("forestgreen", Some(Green::ForestGreen))]
337 #[case("#00ff7f", Some(Green::SpringGreen))]
338 #[case("00ff7f", Some(Green::SpringGreen))]
339 #[case("springgreen", Some(Green::SpringGreen))]
340 #[case("#98fb98", Some(Green::PaleGreen))]
341 #[case("98fb98", Some(Green::PaleGreen))]
342 #[case("palegreen", Some(Green::PaleGreen))]
343 #[case("#8fbc8f", Some(Green::DarkSeaGreen))]
344 #[case("8fbc8f", Some(Green::DarkSeaGreen))]
345 #[case("darkseagreen", Some(Green::DarkSeaGreen))]
346 #[case("#00fa9a", Some(Green::MediumSpringGreen))]
347 #[case("00fa9a", Some(Green::MediumSpringGreen))]
348 #[case("mediumspringgreen", Some(Green::MediumSpringGreen))]
349 #[case("#2e8b57", Some(Green::SeaGreen))]
350 #[case("2e8b57", Some(Green::SeaGreen))]
351 #[case("seagreen", Some(Green::SeaGreen))]
352 #[case("012345", None)]
353 fn test_name_colour(#[case] input: &str, #[case] expected: Option<Green>) {
354 assert_eq!(expected, Green::name_colour(input))
355 }
356}