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 Yellow {
18 Gold,
19 DarkGoldenrod,
20 Goldenrod,
21 PaleGoldenrod,
22 DarkKhaki,
23 Khaki,
24 Yellow,
25 YellowGreen,
26 PeachPuff,
27 Moccasin,
28 PapayaWhip,
29 LightGoldenrodYellow,
30 LemonChiffon,
31 LightYellow,
32}
33
34impl fmt::Display for Yellow {
35 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36 match self {
37 Self::Gold => write!(f, "#FFD700"),
38 Self::DarkGoldenrod => write!(f, "#B8860B"),
39 Self::Goldenrod => write!(f, "#DAA520"),
40 Self::PaleGoldenrod => write!(f, "#EEE8AA"),
41 Self::PeachPuff => write!(f, "#FFDAB9"),
42 Self::Moccasin => write!(f, "#FFE4B5"),
43 Self::PapayaWhip => write!(f, "#FFEFD5"),
44 Self::DarkKhaki => write!(f, "#BDB76B"),
45 Self::LemonChiffon => write!(f, "#FFFACD"),
46 Self::LightGoldenrodYellow => write!(f, "#FAFAD2"),
47 Self::Khaki => write!(f, "#F0E68C"),
48 Self::Yellow => write!(f, "#FFFF00"),
49 Self::YellowGreen => write!(f, "#9ACD32"),
50 Self::LightYellow => write!(f, "#FFFFE0"),
51 }
52 }
53}
54
55impl Yellow {
56 pub fn to_rgb(&self) -> Rgb<u8> {
71 let colour = self.to_string();
72
73 let r: u8 = u8::from_str_radix(&colour[1..3], 16).unwrap();
74 let g: u8 = u8::from_str_radix(&colour[3..5], 16).unwrap();
75 let b: u8 = u8::from_str_radix(&colour[5..7], 16).unwrap();
76
77 Rgb::new(r, g, b)
78 }
79
80 pub fn to_hex_triplet(&self, prefix: Prefix) -> String {
93 let rgb = self.to_rgb();
94
95 let prefix = match prefix {
96 Prefix::Hash => "#",
97 Prefix::None => "",
98 };
99
100 format!("{}{:02X}{:02X}{:02X}", prefix, rgb.r, rgb.g, rgb.b)
101 }
102
103 pub fn parse(name: &str) -> Option<Self> {
116 match name.to_lowercase().as_str() {
117 "#ffd700" | "ffd700" | "gold" => Some(Self::Gold),
118 "#b8860b" | "b8860b" | "darkgoldenrod" => Some(Self::DarkGoldenrod),
119 "#dab500" | "dab500" | "goldenrod" => Some(Self::Goldenrod),
120 "#eee8aa" | "eee8aa" | "palegoldenrod" => Some(Self::PaleGoldenrod),
121 "#bdb76b" | "bdb76b" | "darkkhaki" => Some(Self::DarkKhaki),
122 "#f0e68c" | "f0e68c" | "khaki" => Some(Self::Khaki),
123 "#ffff00" | "ffff00" | "yellow" => Some(Self::Yellow),
124 "#9acd32" | "9acd32" | "yellowgreen" => Some(Self::YellowGreen),
125 "#ffdab9" | "ffdab9" | "peachpuff" => Some(Self::PeachPuff),
126 "#ffe4b5" | "ffe4b5" | "moccasin" => Some(Self::Moccasin),
127 "#ffefd5" | "ffefd5" | "papayawhip" => Some(Self::PapayaWhip),
128 "#fffacd" | "fffacd" | "lemonchiffon" => Some(Self::LemonChiffon),
129 "#fafad2" | "fafad2" | "lightgoldenrodyellow" => Some(Self::LightGoldenrodYellow),
130 "#ffffe0" | "ffffe0" | "lightyellow" => Some(Self::LightYellow),
131 _ => None,
132 }
133 }
134
135 pub fn random() -> Self {
147 let mut rand = StdRand::default();
148
149 match rand.next_range(0..Self::COUNT) {
150 0 => Self::Gold,
151 1 => Self::DarkGoldenrod,
152 2 => Self::Goldenrod,
153 3 => Self::PaleGoldenrod,
154 4 => Self::PeachPuff,
155 5 => Self::Moccasin,
156 6 => Self::PapayaWhip,
157 7 => Self::DarkKhaki,
158 8 => Self::LemonChiffon,
159 9 => Self::LightGoldenrodYellow,
160 10 => Self::Khaki,
161 11 => Self::Yellow,
162 12 => Self::YellowGreen,
163 13 => Self::LightYellow,
164 _ => Self::Yellow,
165 }
166 }
167}
168
169impl FromStr for Yellow {
170 type Err = String;
171 fn from_str(s: &str) -> Result<Self, Self::Err> {
172 match Self::parse(s) {
173 Some(colour) => Ok(colour),
174 None => Err(format!("Invalid Colour: {}", s)),
175 }
176 }
177}
178
179impl ExtendedColour for Yellow {}
180
181#[cfg(test)]
182mod tests {
183 use super::*;
184 use rstest::rstest;
185
186 #[rstest]
187 #[case(Yellow::Gold, "rgb(255,215,0)")]
188 #[case(Yellow::DarkGoldenrod, "rgb(184,134,11)")]
189 #[case(Yellow::Goldenrod, "rgb(218,165,32)")]
190 #[case(Yellow::PaleGoldenrod, "rgb(238,232,170)")]
191 #[case(Yellow::DarkKhaki, "rgb(189,183,107)")]
192 #[case(Yellow::Khaki, "rgb(240,230,140)")]
193 #[case(Yellow::Yellow, "rgb(255,255,0)")]
194 #[case(Yellow::YellowGreen, "rgb(154,205,50)")]
195 #[case(Yellow::PeachPuff, "rgb(255,218,185)")]
196 #[case(Yellow::Moccasin, "rgb(255,228,181)")]
197 #[case(Yellow::PapayaWhip, "rgb(255,239,213)")]
198 #[case(Yellow::LemonChiffon, "rgb(255,250,205)")]
199 #[case(Yellow::LightGoldenrodYellow, "rgb(250,250,210)")]
200 #[case(Yellow::LightYellow, "rgb(255,255,224)")]
201 fn test_rgb_string(#[case] colour: Yellow, #[case] expected: String) {
202 let rgb_colour = colour.to_rgb();
203 let string = rgb_colour.to_string();
204
205 assert_eq!(expected, string);
206 }
207
208 #[rstest]
209 #[case(Yellow::Gold, "FFD700")]
210 #[case(Yellow::DarkGoldenrod, "B8860B")]
211 #[case(Yellow::Goldenrod, "DAA520")]
212 #[case(Yellow::PaleGoldenrod, "EEE8AA")]
213 #[case(Yellow::DarkKhaki, "BDB76B")]
214 #[case(Yellow::Khaki, "F0E68C")]
215 #[case(Yellow::Yellow, "FFFF00")]
216 #[case(Yellow::YellowGreen, "9ACD32")]
217 #[case(Yellow::PeachPuff, "FFDAB9")]
218 #[case(Yellow::Moccasin, "FFE4B5")]
219 #[case(Yellow::PapayaWhip, "FFEFD5")]
220 #[case(Yellow::LemonChiffon, "FFFACD")]
221 #[case(Yellow::LightGoldenrodYellow, "FAFAD2")]
222 #[case(Yellow::LightYellow, "FFFFE0")]
223 fn test_hex_triplet_string(
224 #[case] colour: Yellow,
225 #[values(Prefix::None, Prefix::Hash)] prefix: Prefix,
226 #[case] expected: String,
227 ) {
228 let prefix_string = match prefix {
229 Prefix::None => "".to_string(),
230 Prefix::Hash => "#".to_string(),
231 };
232
233 let expected = format!("{}{}", prefix_string, expected);
234
235 let hex_colour = colour.to_hex_triplet(prefix);
236
237 assert_eq!(expected, hex_colour);
238 }
239
240 #[rstest]
241 #[case("#ffd700", Yellow::Gold)]
242 #[case("ffd700", Yellow::Gold)]
243 #[case("Gold", Yellow::Gold)]
244 #[case("#b8860b", Yellow::DarkGoldenrod)]
245 #[case("b8860b", Yellow::DarkGoldenrod)]
246 #[case("DarkGoldenRod", Yellow::DarkGoldenrod)]
247 #[case("#dab500", Yellow::Goldenrod)]
248 #[case("dab500", Yellow::Goldenrod)]
249 #[case("GoldenRod", Yellow::Goldenrod)]
250 #[case("#eee8aa", Yellow::PaleGoldenrod)]
251 #[case("eee8aa", Yellow::PaleGoldenrod)]
252 #[case("PaleGoldenRod", Yellow::PaleGoldenrod)]
253 #[case("#bdb76b", Yellow::DarkKhaki)]
254 #[case("bdb76b", Yellow::DarkKhaki)]
255 #[case("DarkKhaki", Yellow::DarkKhaki)]
256 #[case("#f0e68c", Yellow::Khaki)]
257 #[case("f0e68c", Yellow::Khaki)]
258 #[case("Khaki", Yellow::Khaki)]
259 #[case("#ffff00", Yellow::Yellow)]
260 #[case("ffff00", Yellow::Yellow)]
261 #[case("Yellow", Yellow::Yellow)]
262 #[case("#9acd32", Yellow::YellowGreen)]
263 #[case("9acd32", Yellow::YellowGreen)]
264 #[case("YellowGreen", Yellow::YellowGreen)]
265 #[case("#ffdab9", Yellow::PeachPuff)]
266 #[case("ffdab9", Yellow::PeachPuff)]
267 #[case("PeachPuff", Yellow::PeachPuff)]
268 #[case("#ffe4b5", Yellow::Moccasin)]
269 #[case("ffe4b5", Yellow::Moccasin)]
270 #[case("Moccasin", Yellow::Moccasin)]
271 #[case("#ffefd5", Yellow::PapayaWhip)]
272 #[case("ffefd5", Yellow::PapayaWhip)]
273 #[case("PapayaWhip", Yellow::PapayaWhip)]
274 #[case("#fffacd", Yellow::LemonChiffon)]
275 #[case("fffacd", Yellow::LemonChiffon)]
276 #[case("LemonChiffon", Yellow::LemonChiffon)]
277 #[case("#fafad2", Yellow::LightGoldenrodYellow)]
278 #[case("fafad2", Yellow::LightGoldenrodYellow)]
279 #[case("LightGoldenrodYellow", Yellow::LightGoldenrodYellow)]
280 #[case("#ffffe0", Yellow::LightYellow)]
281 #[case("ffffe0", Yellow::LightYellow)]
282 #[case("LightYellow", Yellow::LightYellow)]
283 fn test_from_str(#[case] input: &str, #[case] expected: Yellow) {
284 assert_eq!(expected, Yellow::from_str(input).unwrap())
285 }
286
287 #[rstest]
288 #[case("#ffd700", Some(Yellow::Gold))]
289 #[case("ffd700", Some(Yellow::Gold))]
290 #[case("Gold", Some(Yellow::Gold))]
291 #[case("#b8860b", Some(Yellow::DarkGoldenrod))]
292 #[case("b8860b", Some(Yellow::DarkGoldenrod))]
293 #[case("DarkGoldenRod", Some(Yellow::DarkGoldenrod))]
294 #[case("#dab500", Some(Yellow::Goldenrod))]
295 #[case("dab500", Some(Yellow::Goldenrod))]
296 #[case("GoldenRod", Some(Yellow::Goldenrod))]
297 #[case("#eee8aa", Some(Yellow::PaleGoldenrod))]
298 #[case("eee8aa", Some(Yellow::PaleGoldenrod))]
299 #[case("PaleGoldenRod", Some(Yellow::PaleGoldenrod))]
300 #[case("#bdb76b", Some(Yellow::DarkKhaki))]
301 #[case("bdb76b", Some(Yellow::DarkKhaki))]
302 #[case("DarkKhaki", Some(Yellow::DarkKhaki))]
303 #[case("#f0e68c", Some(Yellow::Khaki))]
304 #[case("f0e68c", Some(Yellow::Khaki))]
305 #[case("Khaki", Some(Yellow::Khaki))]
306 #[case("#ffff00", Some(Yellow::Yellow))]
307 #[case("ffff00", Some(Yellow::Yellow))]
308 #[case("Yellow", Some(Yellow::Yellow))]
309 #[case("#9acd32", Some(Yellow::YellowGreen))]
310 #[case("9acd32", Some(Yellow::YellowGreen))]
311 #[case("YellowGreen", Some(Yellow::YellowGreen))]
312 #[case("#ffdab9", Some(Yellow::PeachPuff))]
313 #[case("ffdab9", Some(Yellow::PeachPuff))]
314 #[case("PeachPuff", Some(Yellow::PeachPuff))]
315 #[case("#ffe4b5", Some(Yellow::Moccasin))]
316 #[case("ffe4b5", Some(Yellow::Moccasin))]
317 #[case("Moccasin", Some(Yellow::Moccasin))]
318 #[case("#ffefd5", Some(Yellow::PapayaWhip))]
319 #[case("ffefd5", Some(Yellow::PapayaWhip))]
320 #[case("PapayaWhip", Some(Yellow::PapayaWhip))]
321 #[case("#fffacd", Some(Yellow::LemonChiffon))]
322 #[case("fffacd", Some(Yellow::LemonChiffon))]
323 #[case("LemonChiffon", Some(Yellow::LemonChiffon))]
324 #[case("#fafad2", Some(Yellow::LightGoldenrodYellow))]
325 #[case("fafad2", Some(Yellow::LightGoldenrodYellow))]
326 #[case("LightGoldenrodYellow", Some(Yellow::LightGoldenrodYellow))]
327 #[case("#ffffe0", Some(Yellow::LightYellow))]
328 #[case("ffffe0", Some(Yellow::LightYellow))]
329 #[case("LightYellow", Some(Yellow::LightYellow))]
330 #[case("012345", None)]
331 fn test_name_colour(#[case] input: &str, #[case] expected: Option<Yellow>) {
332 assert_eq!(expected, Yellow::name_colour(input))
333 }
334}