1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
/// A 24-bit triplet for hex colors. Defaults to *White* `(0xFF,0xFF,0xFF)`
#[derive(Debug, PartialEq, Copy, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct RGB(pub u8, pub u8, pub u8);

impl Default for RGB {
    /// Default color of #FFFFFF (White)
    fn default() -> Self {
        RGB(255, 255, 255)
    }
}

impl std::fmt::Display for RGB {
    /// Formats the RGB as #RRGGBB (in hex)
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let Self(r, g, b) = self;
        write!(f, "#{:02X}{:02X}{:02X}", r, g, b)
    }
}

impl From<&str> for RGB {
    /// Tries to parse an RGB from the str, defaulting if invalid
    fn from(s: &str) -> Self {
        RGB::from_hex(s)
    }
}

impl From<String> for RGB {
    /// Tries to parse an RGB from the String, defaulting if invalid
    fn from(s: String) -> Self {
        RGB::from_hex(&s)
    }
}

impl RGB {
    /// Tries to parse a string (`'#FFFFFF'` or `'FFFFFF'`) into the RGB,
    /// `default`s if it can't
    pub fn from_hex(input: &str) -> Self {
        let input = input.trim();
        let input = match (input.chars().next(), input.len()) {
            (Some('#'), 7) => &input[1..],
            (_, 6) => input,
            _ => return Self::default(),
        };

        u32::from_str_radix(&input, 16)
            .and_then(|s| {
                Ok(RGB(
                    ((s >> 16) & 0xFF) as u8,
                    ((s >> 8) & 0xFF) as u8,
                    (s & 0xFF) as u8,
                ))
            })
            .unwrap_or_default()
    }

    /// Returns the `red` field
    pub fn red(self) -> u8 {
        self.0
    }

    /// Returns the `green` field
    pub fn green(self) -> u8 {
        self.1
    }

    /// Returns the `blue` field
    pub fn blue(self) -> u8 {
        self.2
    }
}

impl From<Twitch> for RGB {
    /// Tries to turn the [`TwitchColor`](./enum.TwitchColor.html) color into an [`RGB`](./struct.RGB.html)
    ///
    /// If the color is, somehow, unknown, it'll use [`RGB::default`](./struct.RGB.html#method.default)
    fn from(color: Twitch) -> Self {
        if let Twitch::Turbo(rgb) = color {
            return rgb;
        }

        twitch_colors()
            .iter()
            .find(|(c, _)| *c == color)
            .map(|&(_, rgb)| rgb)
            .unwrap_or_default()
    }
}

/// These are the default Twitch colors
#[derive(Debug, PartialEq, Copy, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Twitch {
    /// RGB (hex): #0000FF
    Blue,
    /// RGB (hex): #8A2BE2
    BlueViolet,
    /// RGB (hex): #5F9EA0
    CadetBlue,
    /// RGB (hex): #D2691E
    Chocolate,
    /// RGB (hex): #FF7F50
    Coral,
    /// RGB (hex): #1E90FF
    DodgerBlue,
    /// RGB (hex): #B22222
    Firebrick,
    /// RGB (hex): #DAA520
    GoldenRod,
    /// RGB (hex): #008000
    Green,
    /// RGB (hex): #FF69B4
    HotPink,
    /// RGB (hex): #FF4500
    OrangeRed,
    /// RGB (hex): #FF0000
    Red,
    /// RGB (hex): #2E8B57
    SeaGreen,
    /// RGB (hex): #00FF7F
    SpringGreen,
    /// RGB (hex): #ADFF2F
    YellowGreen,
    /// Turbo colors are custom user-selected colors
    Turbo(RGB),
}

impl Default for Twitch {
    /// Defaults to Twitch::Turbo(RGB(0xFF,0xFF,0xFF))
    fn default() -> Self {
        Twitch::Turbo(RGB::default())
    }
}

impl From<&str> for Twitch {
    /// Tries to parse the twitch color name from a string, or as a #RRGGBB/RRGGBB string
    ///
    /// view source to see valid strings
    fn from(input: &str) -> Self {
        use Twitch::*;
        match input {
            "Blue" | "blue" => Blue,
            "BlueViolet" | "blue_violet" | "blueviolet" | "blue violet" => BlueViolet,
            "CadetBlue" | "cadet_blue" | "cadetblue" | "cadet blue" => CadetBlue,
            "Chocolate" | "chocolate" => Chocolate,
            "Coral" | "coral" => Coral,
            "DodgerBlue" | "dodger_blue" | "dodgerblue" | "dodger blue" => DodgerBlue,
            "Firebrick" | "firebrick" => Firebrick,
            "GoldenRod" | "golden_rod" | "goldenrod" | "golden rod" => GoldenRod,
            "Green" | "green" => Green,
            "HotPink" | "hot_pink" | "hotpink" | "hot pink" => HotPink,
            "OrangeRed" | "orange_red" | "orangered" | "orange red" => OrangeRed,
            "Red" | "red" => Red,
            "SeaGreen" | "sea_green" | "seagreen" | "sea green" => SeaGreen,
            "SpringGreen" | "spring_green" | "springgreen" | "spring green" => SpringGreen,
            "YellowGreen" | "yellow_green" | "yellowgreen" | "yellow green" => YellowGreen,
            s => Turbo(RGB::from_hex(s)),
        }
    }
}

impl From<RGB> for Twitch {
    /// Tries to turn the RGB Color into a Twitch Color
    ///
    /// Defaults to a Turbo(RGB(0xFF,0xFF,0xFF))
    fn from(rgb: RGB) -> Self {
        twitch_colors()
            .iter()
            .find(|(_, color)| *color == rgb)
            .map(|&(c, _)| c)
            .unwrap_or_else(|| Twitch::Turbo(rgb))
    }
}

impl std::fmt::Display for Twitch {
    /// Gets the Twitch color name as a string, as those listed on the Twitch site
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        use Twitch::*;
        match self {
            Blue => write!(f, "Blue"),
            BlueViolet => write!(f, "BlueViolet"),
            CadetBlue => write!(f, "CadetBlue"),
            Chocolate => write!(f, "Chocolate"),
            Coral => write!(f, "Coral"),
            DodgerBlue => write!(f, "DodgerBlue"),
            Firebrick => write!(f, "Firebrick"),
            GoldenRod => write!(f, "GoldenRod"),
            Green => write!(f, "Green"),
            HotPink => write!(f, "HotPink"),
            OrangeRed => write!(f, "OrangeRed"),
            Red => write!(f, "Red"),
            SeaGreen => write!(f, "SeaGreen"),
            SpringGreen => write!(f, "SpringGreen"),
            YellowGreen => write!(f, "YellowGreen"),
            Turbo(rgb) => write!(f, "{}", rgb),
        }
    }
}

/// A helper method that returns a const array of [`TwitchColor`](./enum.TwitchColor.html) colors to [`RGB`](./struct.RGB.html)
pub const fn twitch_colors() -> [(Twitch, RGB); 15] {
    use Twitch::*;
    [
        (Blue, RGB(0x00, 0x00, 0xFF)),
        (BlueViolet, RGB(0x8A, 0x2B, 0xE2)),
        (CadetBlue, RGB(0x5F, 0x9E, 0xA0)),
        (Chocolate, RGB(0xD2, 0x69, 0x1E)),
        (Coral, RGB(0xFF, 0x7F, 0x50)),
        (DodgerBlue, RGB(0x1E, 0x90, 0xFF)),
        (Firebrick, RGB(0xB2, 0x22, 0x22)),
        (GoldenRod, RGB(0xDA, 0xA5, 0x20)),
        (Green, RGB(0x00, 0x80, 0x00)),
        (HotPink, RGB(0xFF, 0x69, 0xB4)),
        (OrangeRed, RGB(0xFF, 0x45, 0x00)),
        (Red, RGB(0xFF, 0x00, 0x00)),
        (SeaGreen, RGB(0x2E, 0x8B, 0x57)),
        (SpringGreen, RGB(0x00, 0xFF, 0x7F)),
        (YellowGreen, RGB(0xAD, 0xFF, 0x2F)),
    ]
}