plotly_patched/common/
color.rs

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// https://www.w3.org/TR/css-color-3/#svg-color
71#[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
408// Implementations split intentionally; otherwise there is a conflict with the f64 implementation
409// as it `may` implement AsRef<str> in the future.
410impl Color for str {
411    fn to_color(&self) -> ColorWrapper {
412        string_types_to_color_wrapper(self)
413    }
414}
415
416// Implementations split intentionally; otherwise there is a conflict with the f64 implementation
417// as it `may` implement AsRef<str> in the future.
418impl Color for &str {
419    fn to_color(&self) -> ColorWrapper {
420        string_types_to_color_wrapper(self)
421    }
422}
423
424// Implementations split intentionally; otherwise there is a conflict with the f64 implementation
425// as it `may` implement AsRef<str> in the future.
426impl Color for String {
427    fn to_color(&self) -> ColorWrapper {
428        string_types_to_color_wrapper(self)
429    }
430}
431
432// Implementations split intentionally; otherwise there is a conflict with the f64 implementation
433// as it `may` implement AsRef<str> in the future.
434impl 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}