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 Cyan {
18 MediumAquaMarine,
19 MediumSeaGreen,
20 LightSeaGreen,
21 DarkSlateGray,
22 Teal,
23 DarkCyan,
24 Aqua,
25 Cyan,
26 LightCyan,
27 DarkTurquoise,
28 Turquoise,
29 MediumTurquoise,
30 PaleTurquoise,
31 AquaMarine,
32 Honeydew,
33}
34
35impl fmt::Display for Cyan {
36 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37 match self {
38 Self::MediumAquaMarine => write!(f, "#66CDAA"),
39 Self::MediumSeaGreen => write!(f, "#3CB371"),
40 Self::LightSeaGreen => write!(f, "#20B2AA"),
41 Self::DarkSlateGray => write!(f, "#2F4F4F"),
42 Self::Teal => write!(f, "#008080"),
43 Self::DarkCyan => write!(f, "#008B8B"),
44 Self::Aqua => write!(f, "#00FFFF"),
45 Self::Cyan => write!(f, "#00FFFF"),
46 Self::LightCyan => write!(f, "#E0FFFF"),
47 Self::DarkTurquoise => write!(f, "#00CED1"),
48 Self::Turquoise => write!(f, "#40E0D0"),
49 Self::MediumTurquoise => write!(f, "#48D1CC"),
50 Self::PaleTurquoise => write!(f, "#AFEEEE"),
51 Self::AquaMarine => write!(f, "#7FFFD4"),
52 Self::Honeydew => write!(f, "#F0FFF0"),
53 }
54 }
55}
56
57impl Cyan {
58 pub fn to_rgb(&self) -> Rgb<u8> {
74 let colour = self.to_string();
75
76 let r: u8 = u8::from_str_radix(&colour[1..3], 16).unwrap();
77 let g: u8 = u8::from_str_radix(&colour[3..5], 16).unwrap();
78 let b: u8 = u8::from_str_radix(&colour[5..7], 16).unwrap();
79
80 Rgb::new(r, g, b)
81 }
82
83 pub fn to_hex_triplet(&self, prefix: Prefix) -> String {
96 let rgb = self.to_rgb();
97
98 let prefix = match prefix {
99 Prefix::Hash => "#",
100 Prefix::None => "",
101 };
102
103 format!("{}{:02X}{:02X}{:02X}", prefix, rgb.r, rgb.g, rgb.b)
104 }
105
106 pub fn parse(name: &str) -> Option<Self> {
119 match name.to_lowercase().as_str() {
120 "#66cdaa" | "66cdaa" | "mediumaquamarine" => Some(Self::MediumAquaMarine),
121 "#3cb371" | "3cb371" | "mediumseagreen" => Some(Self::MediumSeaGreen),
122 "#20b2aa" | "20b2aa" | "lightseagreen" => Some(Self::LightSeaGreen),
123 "#2f4f4f" | "2f4f4f" | "darkslategray" => Some(Self::DarkSlateGray),
124 "#008080" | "008080" | "teal" => Some(Self::Teal),
125 "#008b8b" | "008b8b" | "darkcyan" => Some(Self::DarkCyan),
126 "#00ffff" | "00ffff" | "aqua" => Some(Self::Aqua),
127 "cyan" => Some(Self::Cyan),
128 "#e0ffff" | "e0ffff" | "lightcyan" => Some(Self::LightCyan),
129 "#00ced1" | "00ced1" | "darkturquoise" => Some(Self::DarkTurquoise),
130 "#40e0d0" | "40e0d0" | "turquoise" => Some(Self::Turquoise),
131 "#48d1cc" | "48d1cc" | "mediumturquoise" => Some(Self::MediumTurquoise),
132 "#afeeee" | "afeeee" | "paleturquoise" => Some(Self::PaleTurquoise),
133 "#7fffd4" | "7fffd4" | "aquamarine" => Some(Self::AquaMarine),
134 "#f0fff0" | "f0fff0" | "honeydew" => Some(Self::Honeydew),
135 _ => None,
136 }
137 }
138
139 pub fn random() -> Self {
151 let mut rand = StdRand::default();
152
153 match rand.next_range(0..Self::COUNT) {
154 0 => Self::MediumAquaMarine,
155 1 => Self::MediumSeaGreen,
156 2 => Self::LightSeaGreen,
157 3 => Self::DarkSlateGray,
158 4 => Self::Teal,
159 5 => Self::DarkCyan,
160 6 => Self::Aqua,
161 7 => Self::Cyan,
162 8 => Self::LightCyan,
163 9 => Self::DarkTurquoise,
164 10 => Self::Turquoise,
165 11 => Self::MediumTurquoise,
166 12 => Self::PaleTurquoise,
167 13 => Self::AquaMarine,
168 14 => Self::Honeydew,
169 _ => Self::Cyan,
170 }
171 }
172}
173
174impl FromStr for Cyan {
175 type Err = String;
176 fn from_str(s: &str) -> Result<Self, Self::Err> {
177 match Self::parse(s) {
178 Some(colour) => Ok(colour),
179 None => Err(format!("Invalid Colour: {s}")),
180 }
181 }
182}
183
184impl ExtendedColour for Cyan {}
185
186#[cfg(test)]
187mod tests {
188 use super::*;
189 use rstest::rstest;
190
191 #[rstest]
192 #[case(Cyan::MediumAquaMarine, "rgb(102,205,170)")]
193 #[case(Cyan::MediumSeaGreen, "rgb(60,179,113)")]
194 #[case(Cyan::LightSeaGreen, "rgb(32,178,170)")]
195 #[case(Cyan::DarkSlateGray, "rgb(47,79,79)")]
196 #[case(Cyan::Teal, "rgb(0,128,128)")]
197 #[case(Cyan::DarkCyan, "rgb(0,139,139)")]
198 #[case(Cyan::Aqua, "rgb(0,255,255)")]
199 #[case(Cyan::Cyan, "rgb(0,255,255)")]
200 #[case(Cyan::LightCyan, "rgb(224,255,255)")]
201 #[case(Cyan::DarkTurquoise, "rgb(0,206,209)")]
202 #[case(Cyan::Turquoise, "rgb(64,224,208)")]
203 #[case(Cyan::MediumTurquoise, "rgb(72,209,204)")]
204 #[case(Cyan::PaleTurquoise, "rgb(175,238,238)")]
205 #[case(Cyan::AquaMarine, "rgb(127,255,212)")]
206 #[case(Cyan::Honeydew, "rgb(240,255,240)")]
207 fn test_rgb_string(#[case] colour: Cyan, #[case] expected: String) {
208 let rgb_colour = colour.to_rgb();
209 let string = rgb_colour.to_string();
210
211 assert_eq!(expected, string);
212 }
213
214 #[rstest]
215 #[case(Cyan::MediumAquaMarine, "66CDAA")]
216 #[case(Cyan::MediumSeaGreen, "3CB371")]
217 #[case(Cyan::LightSeaGreen, "20B2AA")]
218 #[case(Cyan::DarkSlateGray, "2F4F4F")]
219 #[case(Cyan::Teal, "008080")]
220 #[case(Cyan::DarkCyan, "008B8B")]
221 #[case(Cyan::Aqua, "00FFFF")]
222 #[case(Cyan::Cyan, "00FFFF")]
223 #[case(Cyan::LightCyan, "E0FFFF")]
224 #[case(Cyan::DarkTurquoise, "00CED1")]
225 #[case(Cyan::Turquoise, "40E0D0")]
226 #[case(Cyan::MediumTurquoise, "48D1CC")]
227 #[case(Cyan::PaleTurquoise, "AFEEEE")]
228 #[case(Cyan::AquaMarine, "7FFFD4")]
229 #[case(Cyan::Honeydew, "F0FFF0")]
230 fn test_hex_triplet_string(
231 #[case] colour: Cyan,
232 #[values(Prefix::None, Prefix::Hash)] prefix: Prefix,
233 #[case] expected: String,
234 ) {
235 let prefix_string = match prefix {
236 Prefix::None => "".to_string(),
237 Prefix::Hash => "#".to_string(),
238 };
239
240 let expected = format!("{prefix_string}{expected}");
241
242 let hex_colour = colour.to_hex_triplet(prefix);
243
244 assert_eq!(expected, hex_colour);
245 }
246
247 #[rstest]
248 #[case("#66cdaa", Cyan::MediumAquaMarine)]
249 #[case("66cdaa", Cyan::MediumAquaMarine)]
250 #[case("mediumaquamarine", Cyan::MediumAquaMarine)]
251 #[case("#3cb371", Cyan::MediumSeaGreen)]
252 #[case("3cb371", Cyan::MediumSeaGreen)]
253 #[case("mediumseagreen", Cyan::MediumSeaGreen)]
254 #[case("#20b2aa", Cyan::LightSeaGreen)]
255 #[case("20b2aa", Cyan::LightSeaGreen)]
256 #[case("lightseagreen", Cyan::LightSeaGreen)]
257 #[case("#2f4f4f", Cyan::DarkSlateGray)]
258 #[case("2f4f4f", Cyan::DarkSlateGray)]
259 #[case("darkslategray", Cyan::DarkSlateGray)]
260 #[case("#008080", Cyan::Teal)]
261 #[case("008080", Cyan::Teal)]
262 #[case("teal", Cyan::Teal)]
263 #[case("#008b8b", Cyan::DarkCyan)]
264 #[case("008b8b", Cyan::DarkCyan)]
265 #[case("darkcyan", Cyan::DarkCyan)]
266 #[case("#00ffff", Cyan::Aqua)]
267 #[case("00ffff", Cyan::Aqua)]
268 #[case("aqua", Cyan::Aqua)]
269 #[case("cyan", Cyan::Cyan)]
270 #[case("#e0ffff", Cyan::LightCyan)]
271 #[case("e0ffff", Cyan::LightCyan)]
272 #[case("lightcyan", Cyan::LightCyan)]
273 #[case("#00ced1", Cyan::DarkTurquoise)]
274 #[case("00ced1", Cyan::DarkTurquoise)]
275 #[case("darkturquoise", Cyan::DarkTurquoise)]
276 #[case("#40e0d0", Cyan::Turquoise)]
277 #[case("40e0d0", Cyan::Turquoise)]
278 #[case("turquoise", Cyan::Turquoise)]
279 #[case("#48d1cc", Cyan::MediumTurquoise)]
280 #[case("48d1cc", Cyan::MediumTurquoise)]
281 #[case("mediumturquoise", Cyan::MediumTurquoise)]
282 #[case("#afeeee", Cyan::PaleTurquoise)]
283 #[case("afeeee", Cyan::PaleTurquoise)]
284 #[case("paleturquoise", Cyan::PaleTurquoise)]
285 #[case("#7fffd4", Cyan::AquaMarine)]
286 #[case("7fffd4", Cyan::AquaMarine)]
287 #[case("aquamarine", Cyan::AquaMarine)]
288 #[case("#f0fff0", Cyan::Honeydew)]
289 #[case("f0fff0", Cyan::Honeydew)]
290 #[case("honeydew", Cyan::Honeydew)]
291 fn test_from_str(#[case] input: &str, #[case] expected: Cyan) {
292 assert_eq!(expected, Cyan::from_str(input).unwrap())
293 }
294
295 #[rstest]
296 #[case("#66cdaa", Some(Cyan::MediumAquaMarine))]
297 #[case("66cdaa", Some(Cyan::MediumAquaMarine))]
298 #[case("mediumaquamarine", Some(Cyan::MediumAquaMarine))]
299 #[case("#3cb371", Some(Cyan::MediumSeaGreen))]
300 #[case("3cb371", Some(Cyan::MediumSeaGreen))]
301 #[case("mediumseagreen", Some(Cyan::MediumSeaGreen))]
302 #[case("#20b2aa", Some(Cyan::LightSeaGreen))]
303 #[case("20b2aa", Some(Cyan::LightSeaGreen))]
304 #[case("lightseagreen", Some(Cyan::LightSeaGreen))]
305 #[case("#2f4f4f", Some(Cyan::DarkSlateGray))]
306 #[case("2f4f4f", Some(Cyan::DarkSlateGray))]
307 #[case("darkslategray", Some(Cyan::DarkSlateGray))]
308 #[case("#008080", Some(Cyan::Teal))]
309 #[case("008080", Some(Cyan::Teal))]
310 #[case("teal", Some(Cyan::Teal))]
311 #[case("#008b8b", Some(Cyan::DarkCyan))]
312 #[case("008b8b", Some(Cyan::DarkCyan))]
313 #[case("darkcyan", Some(Cyan::DarkCyan))]
314 #[case("#00ffff", Some(Cyan::Aqua))]
315 #[case("00ffff", Some(Cyan::Aqua))]
316 #[case("aqua", Some(Cyan::Aqua))]
317 #[case("cyan", Some(Cyan::Cyan))]
318 #[case("#e0ffff", Some(Cyan::LightCyan))]
319 #[case("e0ffff", Some(Cyan::LightCyan))]
320 #[case("lightcyan", Some(Cyan::LightCyan))]
321 #[case("#00ced1", Some(Cyan::DarkTurquoise))]
322 #[case("00ced1", Some(Cyan::DarkTurquoise))]
323 #[case("darkturquoise", Some(Cyan::DarkTurquoise))]
324 #[case("#40e0d0", Some(Cyan::Turquoise))]
325 #[case("40e0d0", Some(Cyan::Turquoise))]
326 #[case("turquoise", Some(Cyan::Turquoise))]
327 #[case("#48d1cc", Some(Cyan::MediumTurquoise))]
328 #[case("48d1cc", Some(Cyan::MediumTurquoise))]
329 #[case("mediumturquoise", Some(Cyan::MediumTurquoise))]
330 #[case("#afeeee", Some(Cyan::PaleTurquoise))]
331 #[case("afeeee", Some(Cyan::PaleTurquoise))]
332 #[case("paleturquoise", Some(Cyan::PaleTurquoise))]
333 #[case("#7fffd4", Some(Cyan::AquaMarine))]
334 #[case("7fffd4", Some(Cyan::AquaMarine))]
335 #[case("aquamarine", Some(Cyan::AquaMarine))]
336 #[case("#f0fff0", Some(Cyan::Honeydew))]
337 #[case("f0fff0", Some(Cyan::Honeydew))]
338 #[case("honeydew", Some(Cyan::Honeydew))]
339 #[case("012345", None)]
340 fn test_name_colour(#[case] input: &str, #[case] expected: Option<Cyan>) {
341 assert_eq!(expected, Cyan::name_colour(input))
342 }
343}