twitch_types/
color.rs

1use std::borrow::Cow;
2
3use serde::{Deserialize, Serialize};
4
5manual_braid! {
6    /// A color in hex
7    pub struct HexColor;
8    pub struct HexColorRef;
9}
10impl_extra!(no_arb, HexColor, HexColorRef);
11
12#[cfg(feature = "arbitrary")]
13impl<'a> arbitrary::Arbitrary<'a> for HexColor {
14    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
15        let mut buff = [0u8; 3];
16        u.fill_buffer(&mut buff)?;
17        Ok(HexColor::from(format!(
18            "#{:02X}{:02X}{:02X}",
19            buff[0], buff[1], buff[2]
20        )))
21    }
22}
23
24/// Colors a user can have
25#[derive(Debug, PartialEq, Eq, Clone, serde_derive::Deserialize)]
26#[serde(field_identifier, rename_all = "snake_case")]
27pub enum NamedUserColor<'a> {
28    /// Blue
29    Blue,
30    /// Blue Violet
31    BlueViolet,
32    /// Cadet Blue
33    CadetBlue,
34    /// Chocolate
35    Chocolate,
36    /// Coral
37    Coral,
38    /// Dodger Blue
39    DodgerBlue,
40    /// Firebrick
41    Firebrick,
42    /// Golden Rod
43    GoldenRod,
44    /// Green
45    Green,
46    /// Hot Pink
47    HotPink,
48    /// Orange Red
49    OrangeRed,
50    /// Red
51    Red,
52    /// Sea Green
53    SeaGreen,
54    /// Spring Green
55    SpringGreen,
56    /// Yellow Green
57    YellowGreen,
58    /// A hex color
59    #[serde(borrow = "'a")]
60    Hex(Cow<'a, HexColorRef>),
61}
62
63#[cfg(feature = "arbitrary")]
64impl<'a> arbitrary::Arbitrary<'a> for NamedUserColor<'a> {
65    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
66        Ok(match u.int_in_range(0..=15)? {
67            0 => NamedUserColor::Blue,
68            1 => NamedUserColor::BlueViolet,
69            2 => NamedUserColor::CadetBlue,
70            3 => NamedUserColor::Chocolate,
71            4 => NamedUserColor::Coral,
72            5 => NamedUserColor::DodgerBlue,
73            6 => NamedUserColor::Firebrick,
74            7 => NamedUserColor::GoldenRod,
75            8 => NamedUserColor::Green,
76            9 => NamedUserColor::HotPink,
77            10 => NamedUserColor::OrangeRed,
78            11 => NamedUserColor::Red,
79            12 => NamedUserColor::SeaGreen,
80            13 => NamedUserColor::SpringGreen,
81            14 => NamedUserColor::YellowGreen,
82            15 => NamedUserColor::Hex(arbitrary::Arbitrary::arbitrary(u)?),
83            _ => unreachable!(),
84        })
85    }
86
87    fn size_hint(depth: usize) -> (usize, Option<usize>) {
88        arbitrary::size_hint::or_all(&[
89            arbitrary::size_hint::and_all(&[]),
90            <Cow<'a, HexColorRef> as arbitrary::Arbitrary>::size_hint(depth),
91        ])
92    }
93}
94
95#[cfg(feature = "zerofrom")]
96impl<'zf, 'zf_inner> zerofrom::ZeroFrom<'zf, NamedUserColor<'zf_inner>> for NamedUserColor<'zf> {
97    fn zero_from(this: &'zf NamedUserColor<'zf_inner>) -> Self {
98        match *this {
99            NamedUserColor::Blue => NamedUserColor::Blue,
100            NamedUserColor::BlueViolet => NamedUserColor::BlueViolet,
101            NamedUserColor::CadetBlue => NamedUserColor::CadetBlue,
102            NamedUserColor::Chocolate => NamedUserColor::Chocolate,
103            NamedUserColor::Coral => NamedUserColor::Coral,
104            NamedUserColor::DodgerBlue => NamedUserColor::DodgerBlue,
105            NamedUserColor::Firebrick => NamedUserColor::Firebrick,
106            NamedUserColor::GoldenRod => NamedUserColor::GoldenRod,
107            NamedUserColor::Green => NamedUserColor::Green,
108            NamedUserColor::HotPink => NamedUserColor::HotPink,
109            NamedUserColor::OrangeRed => NamedUserColor::OrangeRed,
110            NamedUserColor::Red => NamedUserColor::Red,
111            NamedUserColor::SeaGreen => NamedUserColor::SeaGreen,
112            NamedUserColor::SpringGreen => NamedUserColor::SpringGreen,
113            NamedUserColor::YellowGreen => NamedUserColor::YellowGreen,
114            NamedUserColor::Hex(ref __binding_0) => {
115                NamedUserColor::Hex(<Cow<'zf, HexColorRef> as zerofrom::ZeroFrom<
116                    'zf,
117                    Cow<'zf_inner, HexColorRef>,
118                >>::zero_from(__binding_0))
119            }
120        }
121    }
122}
123
124impl<'a> NamedUserColor<'a> {
125    /// Creates a owned [NamedUserColor<'static>](NamedUserColor) from a borrowed [NamedUserColor<'a>](NamedUserColor)
126    pub fn to_owned(&self) -> NamedUserColor<'static> {
127        match self {
128            NamedUserColor::Blue => NamedUserColor::Blue,
129            NamedUserColor::BlueViolet => NamedUserColor::BlueViolet,
130            NamedUserColor::CadetBlue => NamedUserColor::CadetBlue,
131            NamedUserColor::Chocolate => NamedUserColor::Chocolate,
132            NamedUserColor::Coral => NamedUserColor::Coral,
133            NamedUserColor::DodgerBlue => NamedUserColor::DodgerBlue,
134            NamedUserColor::Firebrick => NamedUserColor::Firebrick,
135            NamedUserColor::GoldenRod => NamedUserColor::GoldenRod,
136            NamedUserColor::Green => NamedUserColor::Green,
137            NamedUserColor::HotPink => NamedUserColor::HotPink,
138            NamedUserColor::OrangeRed => NamedUserColor::OrangeRed,
139            NamedUserColor::Red => NamedUserColor::Red,
140            NamedUserColor::SeaGreen => NamedUserColor::SeaGreen,
141            NamedUserColor::SpringGreen => NamedUserColor::SpringGreen,
142            NamedUserColor::YellowGreen => NamedUserColor::YellowGreen,
143            NamedUserColor::Hex(hex) => NamedUserColor::Hex(hex.as_ref().to_owned().into()),
144        }
145    }
146
147    /// All named colors
148    pub fn all() -> &'static [NamedUserColor<'static>] {
149        &[
150            NamedUserColor::Blue,
151            NamedUserColor::BlueViolet,
152            NamedUserColor::CadetBlue,
153            NamedUserColor::Chocolate,
154            NamedUserColor::Coral,
155            NamedUserColor::DodgerBlue,
156            NamedUserColor::Firebrick,
157            NamedUserColor::GoldenRod,
158            NamedUserColor::Green,
159            NamedUserColor::HotPink,
160            NamedUserColor::OrangeRed,
161            NamedUserColor::Red,
162            NamedUserColor::SeaGreen,
163            NamedUserColor::SpringGreen,
164            NamedUserColor::YellowGreen,
165        ]
166    }
167
168    /// Return this color in [hex](HexColor)
169    pub fn as_hex(&'a self) -> &'a HexColorRef {
170        match self {
171            NamedUserColor::Blue => HexColorRef::from_static("#0000FF"),
172            NamedUserColor::BlueViolet => HexColorRef::from_static("#8A2BE2"),
173            NamedUserColor::CadetBlue => HexColorRef::from_static("#5F9EA0"),
174            NamedUserColor::Chocolate => HexColorRef::from_static("#D2691E"),
175            NamedUserColor::Coral => HexColorRef::from_static("#FF7F50"),
176            NamedUserColor::DodgerBlue => HexColorRef::from_static("#1E90FF"),
177            NamedUserColor::Firebrick => HexColorRef::from_static("#B22222"),
178            NamedUserColor::GoldenRod => HexColorRef::from_static("#DAA520"),
179            NamedUserColor::Green => HexColorRef::from_static("#008000"),
180            NamedUserColor::HotPink => HexColorRef::from_static("#FF69B4"),
181            NamedUserColor::OrangeRed => HexColorRef::from_static("#FF4500"),
182            NamedUserColor::Red => HexColorRef::from_static("#FF0000"),
183            NamedUserColor::SeaGreen => HexColorRef::from_static("#2E8B57"),
184            NamedUserColor::SpringGreen => HexColorRef::from_static("#00FF7F"),
185            NamedUserColor::YellowGreen => HexColorRef::from_static("#ADFF2F"),
186            NamedUserColor::Hex(hex) => hex,
187        }
188    }
189}
190
191impl Serialize for NamedUserColor<'_> {
192    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
193    where S: serde::Serializer {
194        serializer.serialize_str(match self {
195            NamedUserColor::Blue => "blue",
196            NamedUserColor::BlueViolet => "blue_violet",
197            NamedUserColor::CadetBlue => "cadet_blue",
198            NamedUserColor::Chocolate => "chocolate",
199            NamedUserColor::Coral => "coral",
200            NamedUserColor::DodgerBlue => "dodger_blue",
201            NamedUserColor::Firebrick => "firebrick",
202            NamedUserColor::GoldenRod => "golden_rod",
203            NamedUserColor::Green => "green",
204            NamedUserColor::HotPink => "hot_pink",
205            NamedUserColor::OrangeRed => "orange_red",
206            NamedUserColor::Red => "red",
207            NamedUserColor::SeaGreen => "sea_green",
208            NamedUserColor::SpringGreen => "spring_green",
209            NamedUserColor::YellowGreen => "yellow_green",
210            NamedUserColor::Hex(o) => o.as_str(),
211        })
212    }
213}
214
215impl<'a> TryFrom<&'a str> for NamedUserColor<'a> {
216    type Error = serde::de::value::Error;
217
218    fn try_from(s: &'a str) -> Result<Self, Self::Error> {
219        use serde::de::IntoDeserializer;
220
221        NamedUserColor::deserialize(s.into_deserializer())
222    }
223}
224
225impl<'a> From<Cow<'a, HexColorRef>> for NamedUserColor<'a> {
226    fn from(color: Cow<'a, HexColorRef>) -> Self { NamedUserColor::Hex(color) }
227}
228
229impl From<HexColor> for NamedUserColor<'_> {
230    fn from(color: HexColor) -> Self { NamedUserColor::Hex(color.into()) }
231}
232
233impl std::fmt::Display for NamedUserColor<'_> {
234    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.serialize(f) }
235}
236
237#[cfg(test)]
238#[test]
239fn color() {
240    let colors = ["red", "hot_pink", "#9146FF"];
241    let check = vec![
242        NamedUserColor::Red,
243        NamedUserColor::HotPink,
244        NamedUserColor::Hex(HexColorRef::from_static("#9146FF").into()),
245    ];
246    assert_eq!(
247        check,
248        colors
249            .into_iter()
250            .map(|c: &str| c.try_into())
251            .collect::<Result<Vec<NamedUserColor>, serde::de::value::Error>>()
252            .unwrap()
253    );
254    assert_eq!(
255        check.iter().map(|c| c.to_string()).collect::<Vec<_>>(),
256        colors
257    );
258}