1use serde::Serialize;
2
3#[serde(untagged)]
4#[derive(Serialize, Clone, Debug)]
5pub enum ColorWrapper {
6 S(String),
7 F(f64),
8}
9
10impl PartialEq for ColorWrapper {
11 fn eq(&self, other: &Self) -> bool {
12 let lhs = match self {
13 ColorWrapper::S(v) => v.to_owned(),
14 ColorWrapper::F(v) => format!("{}", v),
15 };
16 let rhs = match other {
17 ColorWrapper::S(v) => v.to_owned(),
18 ColorWrapper::F(v) => format!("{}", v),
19 };
20 lhs == rhs
21 }
22}
23
24pub trait Color {
25 fn to_color(&self) -> ColorWrapper;
26}
27
28#[derive(Debug)]
29pub struct Rgb {
30 r: u8,
31 g: u8,
32 b: u8,
33}
34
35impl Rgb {
36 pub fn new(r: u8, g: u8, b: u8) -> Rgb {
37 Rgb { r, g, b }
38 }
39}
40
41impl Color for Rgb {
42 fn to_color(&self) -> ColorWrapper {
43 ColorWrapper::S(format!("rgb({}, {}, {})", self.r, self.g, self.b))
44 }
45}
46
47#[derive(Debug)]
48pub struct Rgba {
49 r: u8,
50 g: u8,
51 b: u8,
52 a: f64,
53}
54
55impl Rgba {
56 pub fn new(r: u8, g: u8, b: u8, a: f64) -> Rgba {
57 Rgba { r, g, b, a }
58 }
59}
60
61impl Color for Rgba {
62 fn to_color(&self) -> ColorWrapper {
63 ColorWrapper::S(format!(
64 "rgba({}, {}, {}, {})",
65 self.r, self.g, self.b, self.a
66 ))
67 }
68}
69
70#[derive(Debug)]
72pub enum NamedColor {
73 AliceBlue,
74 AntiqueWhite,
75 Aqua,
76 Aquamarine,
77 Azure,
78 Beige,
79 Bisque,
80 Black,
81 BlancheDalmond,
82 Blue,
83 BlueViolet,
84 Brown,
85 BurlyWood,
86 CadetBlue,
87 Chartreuse,
88 Chocolate,
89 Coral,
90 CornFlowerBlue,
91 CornSilk,
92 Crimson,
93 Cyan,
94 DarkBlue,
95 DarkCyan,
96 DarkGoldenrod,
97 DarkGray,
98 DarkGreen,
99 DarkGrey,
100 DarkKhaki,
101 DarkMagenta,
102 DarkOliveGreen,
103 DarkOrange,
104 DarkOrchid,
105 DarkRed,
106 DarkSalmon,
107 DarkSeaGreen,
108 DarkSlateBlue,
109 DarkSlateGray,
110 DarkSlateGrey,
111 DarkTurquoise,
112 DarkViolet,
113 DeepPink,
114 DeepSkyBlue,
115 DimGray,
116 DimGrey,
117 DodgerBlue,
118 FireBrick,
119 FloralWhite,
120 ForestGreen,
121 Fuchsia,
122 Gainsboro,
123 GhostWhite,
124 Gold,
125 Goldenrod,
126 Gray,
127 Green,
128 GreenYellow,
129 Grey,
130 Honeydew,
131 HotPink,
132 IndianRed,
133 Indigo,
134 Ivory,
135 Khaki,
136 Lavender,
137 LavenderBlush,
138 LawnGreen,
139 LemonChiffon,
140 LightBlue,
141 LightCoral,
142 LightCyan,
143 LightGoldenrodYellow,
144 LightGray,
145 LightGreen,
146 LightGrey,
147 LightPink,
148 LightSalmon,
149 LightSeaGreen,
150 LightSkyBlue,
151 LightSlateGray,
152 LightSlateGrey,
153 LightSteelBlue,
154 LightYellow,
155 Lime,
156 LimeGreen,
157 Linen,
158 Magenta,
159 Maroon,
160 MediumAquamarine,
161 MediumBlue,
162 MediumOrchid,
163 MediumPurple,
164 MediumSeaGreen,
165 MediumSlateBlue,
166 MediumSpringGreen,
167 MediumTurquoise,
168 MediumVioletRed,
169 MidnightBlue,
170 MintCream,
171 MistyRose,
172 Moccasin,
173 NavajoWhite,
174 Navy,
175 OldLace,
176 Olive,
177 OliveDrab,
178 Orange,
179 OrangeRed,
180 Orchid,
181 PaleGoldenrod,
182 PaleGreen,
183 PaleTurquoise,
184 PaleVioletRed,
185 PapayaWhip,
186 PeachPuff,
187 Peru,
188 Pink,
189 Plum,
190 PowderBlue,
191 Purple,
192 Red,
193 RosyBrown,
194 RoyalBlue,
195 SaddleBrown,
196 Salmon,
197 SandyBrown,
198 SeaGreen,
199 Seashell,
200 Sienna,
201 Silver,
202 SkyBlue,
203 SlateBlue,
204 SlateGray,
205 SlateGrey,
206 Snow,
207 SpringGreen,
208 SteelBlue,
209 Tan,
210 Teal,
211 Thistle,
212 Tomato,
213 Turquoise,
214 Violet,
215 Wheat,
216 White,
217 WhiteSmoke,
218 Yellow,
219 YellowGreen,
220 Transparent,
221}
222
223impl Color for NamedColor {
224 fn to_color(&self) -> ColorWrapper {
225 match self {
226 NamedColor::AliceBlue => ColorWrapper::S("aliceblue".to_owned()),
227 NamedColor::AntiqueWhite => ColorWrapper::S("antiquewhite".to_owned()),
228 NamedColor::Aqua => ColorWrapper::S("aqua".to_owned()),
229 NamedColor::Aquamarine => ColorWrapper::S("aquamarine".to_owned()),
230 NamedColor::Azure => ColorWrapper::S("azure".to_owned()),
231 NamedColor::Beige => ColorWrapper::S("beige".to_owned()),
232 NamedColor::Bisque => ColorWrapper::S("bisque".to_owned()),
233 NamedColor::Black => ColorWrapper::S("black".to_owned()),
234 NamedColor::BlancheDalmond => ColorWrapper::S("blanchedalmond".to_owned()),
235 NamedColor::Blue => ColorWrapper::S("blue".to_owned()),
236 NamedColor::BlueViolet => ColorWrapper::S("blueviolet".to_owned()),
237 NamedColor::Brown => ColorWrapper::S("brown".to_owned()),
238 NamedColor::BurlyWood => ColorWrapper::S("burlywood".to_owned()),
239 NamedColor::CadetBlue => ColorWrapper::S("cadetblue".to_owned()),
240 NamedColor::Chartreuse => ColorWrapper::S("chartreuse".to_owned()),
241 NamedColor::Chocolate => ColorWrapper::S("chocolate".to_owned()),
242 NamedColor::Coral => ColorWrapper::S("coral".to_owned()),
243 NamedColor::CornFlowerBlue => ColorWrapper::S("cornflowerblue".to_owned()),
244 NamedColor::CornSilk => ColorWrapper::S("cornsilk".to_owned()),
245 NamedColor::Crimson => ColorWrapper::S("crimson".to_owned()),
246 NamedColor::Cyan => ColorWrapper::S("cyan".to_owned()),
247 NamedColor::DarkBlue => ColorWrapper::S("darkblue".to_owned()),
248 NamedColor::DarkCyan => ColorWrapper::S("darkcyan".to_owned()),
249 NamedColor::DarkGoldenrod => ColorWrapper::S("darkgoldenrod".to_owned()),
250 NamedColor::DarkGray => ColorWrapper::S("darkgray".to_owned()),
251 NamedColor::DarkGreen => ColorWrapper::S("darkgreen".to_owned()),
252 NamedColor::DarkGrey => ColorWrapper::S("darkgrey".to_owned()),
253 NamedColor::DarkKhaki => ColorWrapper::S("darkkhaki".to_owned()),
254 NamedColor::DarkMagenta => ColorWrapper::S("darkmagenta".to_owned()),
255 NamedColor::DarkOliveGreen => ColorWrapper::S("darkolivegreen".to_owned()),
256 NamedColor::DarkOrange => ColorWrapper::S("darkorange".to_owned()),
257 NamedColor::DarkOrchid => ColorWrapper::S("darkorchid".to_owned()),
258 NamedColor::DarkRed => ColorWrapper::S("darkred".to_owned()),
259 NamedColor::DarkSalmon => ColorWrapper::S("darksalmon".to_owned()),
260 NamedColor::DarkSeaGreen => ColorWrapper::S("darkseagreen".to_owned()),
261 NamedColor::DarkSlateBlue => ColorWrapper::S("darkslateblue".to_owned()),
262 NamedColor::DarkSlateGray => ColorWrapper::S("darkslategray".to_owned()),
263 NamedColor::DarkSlateGrey => ColorWrapper::S("darkslategrey".to_owned()),
264 NamedColor::DarkTurquoise => ColorWrapper::S("darkturquoise".to_owned()),
265 NamedColor::DarkViolet => ColorWrapper::S("darkviolet".to_owned()),
266 NamedColor::DeepPink => ColorWrapper::S("deeppink".to_owned()),
267 NamedColor::DeepSkyBlue => ColorWrapper::S("deepskyblue".to_owned()),
268 NamedColor::DimGray => ColorWrapper::S("dimgray".to_owned()),
269 NamedColor::DimGrey => ColorWrapper::S("dimgrey".to_owned()),
270 NamedColor::DodgerBlue => ColorWrapper::S("dodgerblue".to_owned()),
271 NamedColor::FireBrick => ColorWrapper::S("firebrick".to_owned()),
272 NamedColor::FloralWhite => ColorWrapper::S("floralwhite".to_owned()),
273 NamedColor::ForestGreen => ColorWrapper::S("forestgreen".to_owned()),
274 NamedColor::Fuchsia => ColorWrapper::S("fuchsia".to_owned()),
275 NamedColor::Gainsboro => ColorWrapper::S("gainsboro".to_owned()),
276 NamedColor::GhostWhite => ColorWrapper::S("ghostwhite".to_owned()),
277 NamedColor::Gold => ColorWrapper::S("gold".to_owned()),
278 NamedColor::Goldenrod => ColorWrapper::S("goldenrod".to_owned()),
279 NamedColor::Gray => ColorWrapper::S("gray".to_owned()),
280 NamedColor::Green => ColorWrapper::S("green".to_owned()),
281 NamedColor::GreenYellow => ColorWrapper::S("greenyellow".to_owned()),
282 NamedColor::Grey => ColorWrapper::S("grey".to_owned()),
283 NamedColor::Honeydew => ColorWrapper::S("honeydew".to_owned()),
284 NamedColor::HotPink => ColorWrapper::S("hotpink".to_owned()),
285 NamedColor::IndianRed => ColorWrapper::S("indianred".to_owned()),
286 NamedColor::Indigo => ColorWrapper::S("indigo".to_owned()),
287 NamedColor::Ivory => ColorWrapper::S("ivory".to_owned()),
288 NamedColor::Khaki => ColorWrapper::S("khaki".to_owned()),
289 NamedColor::Lavender => ColorWrapper::S("lavender".to_owned()),
290 NamedColor::LavenderBlush => ColorWrapper::S("lavenderblush".to_owned()),
291 NamedColor::LawnGreen => ColorWrapper::S("lawngreen".to_owned()),
292 NamedColor::LemonChiffon => ColorWrapper::S("lemonchiffon".to_owned()),
293 NamedColor::LightBlue => ColorWrapper::S("lightblue".to_owned()),
294 NamedColor::LightCoral => ColorWrapper::S("lightcoral".to_owned()),
295 NamedColor::LightCyan => ColorWrapper::S("lightcyan".to_owned()),
296 NamedColor::LightGoldenrodYellow => ColorWrapper::S("lightgoldenrodyellow".to_owned()),
297 NamedColor::LightGray => ColorWrapper::S("lightgray".to_owned()),
298 NamedColor::LightGreen => ColorWrapper::S("lightgreen".to_owned()),
299 NamedColor::LightGrey => ColorWrapper::S("lightgrey".to_owned()),
300 NamedColor::LightPink => ColorWrapper::S("lightpink".to_owned()),
301 NamedColor::LightSalmon => ColorWrapper::S("lightsalmon".to_owned()),
302 NamedColor::LightSeaGreen => ColorWrapper::S("lightseagreen".to_owned()),
303 NamedColor::LightSkyBlue => ColorWrapper::S("lightskyblue".to_owned()),
304 NamedColor::LightSlateGray => ColorWrapper::S("lightslategray".to_owned()),
305 NamedColor::LightSlateGrey => ColorWrapper::S("lightslategrey".to_owned()),
306 NamedColor::LightSteelBlue => ColorWrapper::S("lightsteelblue".to_owned()),
307 NamedColor::LightYellow => ColorWrapper::S("lightyellow".to_owned()),
308 NamedColor::Lime => ColorWrapper::S("lime".to_owned()),
309 NamedColor::LimeGreen => ColorWrapper::S("limegreen".to_owned()),
310 NamedColor::Linen => ColorWrapper::S("linen".to_owned()),
311 NamedColor::Magenta => ColorWrapper::S("magenta".to_owned()),
312 NamedColor::Maroon => ColorWrapper::S("maroon".to_owned()),
313 NamedColor::MediumAquamarine => ColorWrapper::S("mediumaquamarine".to_owned()),
314 NamedColor::MediumBlue => ColorWrapper::S("mediumblue".to_owned()),
315 NamedColor::MediumOrchid => ColorWrapper::S("mediumorchid".to_owned()),
316 NamedColor::MediumPurple => ColorWrapper::S("mediumpurple".to_owned()),
317 NamedColor::MediumSeaGreen => ColorWrapper::S("mediumseagreen".to_owned()),
318 NamedColor::MediumSlateBlue => ColorWrapper::S("mediumslateblue".to_owned()),
319 NamedColor::MediumSpringGreen => ColorWrapper::S("mediumspringgreen".to_owned()),
320 NamedColor::MediumTurquoise => ColorWrapper::S("mediumturquoise".to_owned()),
321 NamedColor::MediumVioletRed => ColorWrapper::S("mediumvioletred".to_owned()),
322 NamedColor::MidnightBlue => ColorWrapper::S("midnightblue".to_owned()),
323 NamedColor::MintCream => ColorWrapper::S("mintcream".to_owned()),
324 NamedColor::MistyRose => ColorWrapper::S("mistyrose".to_owned()),
325 NamedColor::Moccasin => ColorWrapper::S("moccasin".to_owned()),
326 NamedColor::NavajoWhite => ColorWrapper::S("navajowhite".to_owned()),
327 NamedColor::Navy => ColorWrapper::S("navy".to_owned()),
328 NamedColor::OldLace => ColorWrapper::S("oldlace".to_owned()),
329 NamedColor::Olive => ColorWrapper::S("olive".to_owned()),
330 NamedColor::OliveDrab => ColorWrapper::S("olivedrab".to_owned()),
331 NamedColor::Orange => ColorWrapper::S("orange".to_owned()),
332 NamedColor::OrangeRed => ColorWrapper::S("orangered".to_owned()),
333 NamedColor::Orchid => ColorWrapper::S("orchid".to_owned()),
334 NamedColor::PaleGoldenrod => ColorWrapper::S("palegoldenrod".to_owned()),
335 NamedColor::PaleGreen => ColorWrapper::S("palegreen".to_owned()),
336 NamedColor::PaleTurquoise => ColorWrapper::S("paleturquoise".to_owned()),
337 NamedColor::PaleVioletRed => ColorWrapper::S("palevioletred".to_owned()),
338 NamedColor::PapayaWhip => ColorWrapper::S("papayawhip".to_owned()),
339 NamedColor::PeachPuff => ColorWrapper::S("peachpuff".to_owned()),
340 NamedColor::Peru => ColorWrapper::S("peru".to_owned()),
341 NamedColor::Pink => ColorWrapper::S("pink".to_owned()),
342 NamedColor::Plum => ColorWrapper::S("plum".to_owned()),
343 NamedColor::PowderBlue => ColorWrapper::S("powderblue".to_owned()),
344 NamedColor::Purple => ColorWrapper::S("purple".to_owned()),
345 NamedColor::Red => ColorWrapper::S("red".to_owned()),
346 NamedColor::RosyBrown => ColorWrapper::S("rosybrown".to_owned()),
347 NamedColor::RoyalBlue => ColorWrapper::S("royalblue".to_owned()),
348 NamedColor::SaddleBrown => ColorWrapper::S("saddlebrown".to_owned()),
349 NamedColor::Salmon => ColorWrapper::S("salmon".to_owned()),
350 NamedColor::SandyBrown => ColorWrapper::S("sandybrown".to_owned()),
351 NamedColor::SeaGreen => ColorWrapper::S("seagreen".to_owned()),
352 NamedColor::Seashell => ColorWrapper::S("seashell".to_owned()),
353 NamedColor::Sienna => ColorWrapper::S("sienna".to_owned()),
354 NamedColor::Silver => ColorWrapper::S("silver".to_owned()),
355 NamedColor::SkyBlue => ColorWrapper::S("skyblue".to_owned()),
356 NamedColor::SlateBlue => ColorWrapper::S("slateblue".to_owned()),
357 NamedColor::SlateGray => ColorWrapper::S("slategray".to_owned()),
358 NamedColor::SlateGrey => ColorWrapper::S("slategrey".to_owned()),
359 NamedColor::Snow => ColorWrapper::S("snow".to_owned()),
360 NamedColor::SpringGreen => ColorWrapper::S("springgreen".to_owned()),
361 NamedColor::SteelBlue => ColorWrapper::S("steelblue".to_owned()),
362 NamedColor::Tan => ColorWrapper::S("tan".to_owned()),
363 NamedColor::Teal => ColorWrapper::S("teal".to_owned()),
364 NamedColor::Thistle => ColorWrapper::S("thistle".to_owned()),
365 NamedColor::Tomato => ColorWrapper::S("tomato".to_owned()),
366 NamedColor::Turquoise => ColorWrapper::S("turquoise".to_owned()),
367 NamedColor::Violet => ColorWrapper::S("violet".to_owned()),
368 NamedColor::Wheat => ColorWrapper::S("wheat".to_owned()),
369 NamedColor::White => ColorWrapper::S("white".to_owned()),
370 NamedColor::WhiteSmoke => ColorWrapper::S("whitesmoke".to_owned()),
371 NamedColor::Yellow => ColorWrapper::S("yellow".to_owned()),
372 NamedColor::YellowGreen => ColorWrapper::S("yellowgreen".to_owned()),
373 NamedColor::Transparent => ColorWrapper::S("transparent".to_owned()),
374 }
375 }
376}
377
378impl Color for f64 {
379 fn to_color(&self) -> ColorWrapper {
380 ColorWrapper::F(*self)
381 }
382}
383
384fn string_types_to_color_wrapper<S: AsRef<str> + std::fmt::Display + Sized>(v: S) -> ColorWrapper {
385 if v.as_ref().len() < 6 || v.as_ref().len() > 7 {
386 panic!(format!("{} is not a valid hex color!", v));
387 }
388 if v.as_ref().len() == 6 && v.as_ref().starts_with('#') {
389 panic!(format!("{} is not a valid hex color!", v));
390 }
391 if v.as_ref().len() == 7 && !v.as_ref().starts_with('#') {
392 panic!(format!("{} is not a valid hex color!", v));
393 }
394 let valid_characters = "#ABCDEF0123456789";
395 let mut s = v.as_ref().to_uppercase();
396 if s.len() == 6 {
397 s = format!("#{}", s);
398 }
399 for c in s.chars() {
400 if !valid_characters.contains(c) {
401 panic!(format!("{} is not a valid hex color!", v));
402 }
403 }
404
405 ColorWrapper::S(s)
406}
407
408impl Color for str {
411 fn to_color(&self) -> ColorWrapper {
412 string_types_to_color_wrapper(self)
413 }
414}
415
416impl Color for &str {
419 fn to_color(&self) -> ColorWrapper {
420 string_types_to_color_wrapper(self)
421 }
422}
423
424impl Color for String {
427 fn to_color(&self) -> ColorWrapper {
428 string_types_to_color_wrapper(self)
429 }
430}
431
432impl Color for &String {
435 fn to_color(&self) -> ColorWrapper {
436 string_types_to_color_wrapper(self)
437 }
438}
439
440#[cfg(test)]
441mod tests {
442 use super::*;
443
444 #[test]
445 fn hex_color_normalization_str() {
446 assert_eq!("#aabbcc".to_color(), ColorWrapper::S("#AABBCC".to_owned()));
447 assert_eq!("aabbcc".to_color(), ColorWrapper::S("#AABBCC".to_owned()));
448 assert_eq!("aaBBcc".to_color(), ColorWrapper::S("#AABBCC".to_owned()));
449 assert_eq!("FABCDe".to_color(), ColorWrapper::S("#FABCDE".to_owned()));
450 assert_eq!("123456".to_color(), ColorWrapper::S("#123456".to_owned()));
451 assert_eq!("7890EE".to_color(), ColorWrapper::S("#7890EE".to_owned()));
452 }
453
454 #[test]
455 fn hex_color_normalization_string() {
456 assert_eq!(
457 String::from("#aabbcc").to_color(),
458 ColorWrapper::S("#AABBCC".to_owned())
459 );
460 assert_eq!(
461 String::from("aabbcc").to_color(),
462 ColorWrapper::S("#AABBCC".to_owned())
463 );
464 assert_eq!(
465 String::from("aaBBcc").to_color(),
466 ColorWrapper::S("#AABBCC".to_owned())
467 );
468 assert_eq!(
469 String::from("FABCDe").to_color(),
470 ColorWrapper::S("#FABCDE".to_owned())
471 );
472 assert_eq!(
473 String::from("123456").to_color(),
474 ColorWrapper::S("#123456".to_owned())
475 );
476 assert_eq!(
477 String::from("7890EE").to_color(),
478 ColorWrapper::S("#7890EE".to_owned())
479 );
480 }
481
482 fn color_from_f64() {
483 assert_eq!(1.54.to_color(), ColorWrapper::F(1.54));
484 assert_eq!(0.1.to_color(), ColorWrapper::F(0.1));
485 }
486
487 #[test]
488 #[should_panic]
489 fn too_short_str() {
490 "abc".to_color();
491 }
492
493 #[test]
494 #[should_panic]
495 fn too_short_string() {
496 String::from("abc").to_color();
497 }
498
499 #[test]
500 #[should_panic]
501 fn too_long_str() {
502 "abcdef0".to_color();
503 }
504
505 #[test]
506 #[should_panic]
507 fn too_long_string() {
508 String::from("abcdef0").to_color();
509 }
510
511 #[test]
512 #[should_panic]
513 fn invalid_characters_str() {
514 "abdefg".to_color();
515 }
516
517 #[test]
518 #[should_panic]
519 fn invalid_characters_string() {
520 String::from("abdefg").to_color();
521 }
522}