named_colour/ext/
brown.rs

1//! Extended named colours providing shades collected in enums for the main colour
2//!
3
4use 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/// Shades of brown
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, EnumCount)]
16#[allow(missing_docs)]
17pub enum Brown {
18    SaddleBrown,
19    Sienna,
20    Chocolate,
21    Peru,
22    SandyBrown,
23    BurlyWood,
24    Tan,
25    RosyBrown,
26}
27
28impl fmt::Display for Brown {
29    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30        match self {
31            Self::SaddleBrown => write!(f, "#8B4513"),
32            Self::Sienna => write!(f, "#A0522D"),
33            Self::Chocolate => write!(f, "#D2691E"),
34            Self::Peru => write!(f, "#CD853F"),
35            Self::SandyBrown => write!(f, "#F4A460"),
36            Self::BurlyWood => write!(f, "#DEB887"),
37            Self::Tan => write!(f, "#D2B48C"),
38            Self::RosyBrown => write!(f, "#BC8F8F"),
39        }
40    }
41}
42
43impl Brown {
44    /// Display the colour name as an RGB Tuple
45    ///
46    /// ## Example
47    ///
48    ///```
49    /// # use named_colour::ext::Brown;
50    /// # fn main() {
51    ///    let colour = Brown::Peru;
52    ///    let rgb_colour = colour.to_rgb();
53    ///
54    ///    let string = rgb_colour.to_string();
55    ///    assert_eq!("rgb(205,133,63)", string);
56    ///
57    ///  # }
58    ///```
59    pub fn to_rgb(&self) -> Rgb<u8> {
60        let colour = self.to_string();
61
62        let r: u8 = u8::from_str_radix(&colour[1..3], 16).unwrap();
63        let g: u8 = u8::from_str_radix(&colour[3..5], 16).unwrap();
64        let b: u8 = u8::from_str_radix(&colour[5..7], 16).unwrap();
65
66        Rgb::new(r, g, b)
67    }
68
69    /// Display the colour name as an RGB Tuple
70    ///
71    /// ## Example
72    ///
73    ///```
74    /// # use named_colour::ext::Brown;
75    /// # use named_colour::Prefix;
76    ///    let colour = Brown::SandyBrown;
77    ///
78    ///     assert_eq!("#F4A460", colour.to_hex_triplet(Prefix::Hash));
79    ///
80    ///```
81    pub fn to_hex_triplet(&self, prefix: Prefix) -> String {
82        let rgb = self.to_rgb();
83
84        let prefix = match prefix {
85            Prefix::Hash => "#",
86            Prefix::None => "",
87        };
88
89        format!("{}{:02X}{:02X}{:02X}", prefix, rgb.r, rgb.g, rgb.b)
90    }
91
92    /// Parse a colour from string
93    ///
94    /// ## Example
95    ///
96    /// ```
97    /// # use named_colour::ext::Brown;
98    ///     let colour = Brown::SandyBrown;
99    ///     assert_eq!(Some(Brown::SandyBrown), Brown::parse("sandybrown"));    
100    /// ```    
101    ///
102    pub fn parse(name: &str) -> Option<Self> {
103        match name.to_lowercase().as_str() {
104            "#8b4513" | "8b4513" | "saddlebrown" => Some(Self::SaddleBrown),
105            "#a0522d" | "a0522d" | "sienna" => Some(Self::Sienna),
106            "#d2691e" | "d2691e" | "chocolate" => Some(Self::Chocolate),
107            "#cd853f" | "cd853f" | "peru" => Some(Self::Peru),
108            "#f4a460" | "f4a460" | "sandybrown" => Some(Self::SandyBrown),
109            "#deb887" | "deb887" | "burlywood" => Some(Self::BurlyWood),
110            "#d2b48c" | "d2b48c" | "tan" => Some(Self::Tan),
111            "#bc8f8f" | "bc8f8f" | "rosybrown" => Some(Self::RosyBrown),
112            _ => None,
113        }
114    }
115
116    /// Generate a random colour
117    ///     
118    /// ## Example
119    ///
120    ///```
121    /// # use named_colour::ext::Brown;
122    /// # fn main() {
123    ///    let colour = Brown::random();
124    ///
125    /// # }
126    /// ```
127    pub fn random() -> Self {
128        let mut rand = StdRand::default();
129
130        match rand.next_range(0..Self::COUNT) {
131            0 => Self::SaddleBrown,
132            1 => Self::Sienna,
133            2 => Self::Chocolate,
134            3 => Self::Peru,
135            4 => Self::SandyBrown,
136            5 => Self::BurlyWood,
137            6 => Self::Tan,
138            7 => Self::RosyBrown,
139            _ => Self::Peru,
140        }
141    }
142}
143
144impl FromStr for Brown {
145    type Err = String;
146    fn from_str(s: &str) -> Result<Self, Self::Err> {
147        match Self::parse(s) {
148            Some(colour) => Ok(colour),
149            None => Err(format!("Invalid Colour: {s}")),
150        }
151    }
152}
153
154impl ExtendedColour for Brown {}
155
156#[cfg(test)]
157mod tests {
158    use super::*;
159    use rstest::rstest;
160
161    #[rstest]
162    #[case(Brown::SaddleBrown, "rgb(139,69,19)")]
163    #[case(Brown::Sienna, "rgb(160,82,45)")]
164    #[case(Brown::Chocolate, "rgb(210,105,30)")]
165    #[case(Brown::Peru, "rgb(205,133,63)")]
166    #[case(Brown::SandyBrown, "rgb(244,164,96)")]
167    #[case(Brown::BurlyWood, "rgb(222,184,135)")]
168    #[case(Brown::Tan, "rgb(210,180,140)")]
169    #[case(Brown::RosyBrown, "rgb(188,143,143)")]
170    fn test_rgb_string(#[case] colour: Brown, #[case] expected: String) {
171        let rgb_colour = colour.to_rgb();
172        let string = rgb_colour.to_string();
173
174        assert_eq!(expected, string);
175    }
176
177    #[rstest]
178    #[case(Brown::SaddleBrown, "8B4513")]
179    #[case(Brown::Sienna, "A0522D")]
180    #[case(Brown::Chocolate, "D2691E")]
181    #[case(Brown::Peru, "CD853F")]
182    #[case(Brown::SandyBrown, "F4A460")]
183    #[case(Brown::BurlyWood, "DEB887")]
184    #[case(Brown::Tan, "D2B48C")]
185    #[case(Brown::RosyBrown, "BC8F8F")]
186    fn test_hex_triplet_string(
187        #[case] colour: Brown,
188        #[values(Prefix::None, Prefix::Hash)] prefix: Prefix,
189        #[case] expected: String,
190    ) {
191        let prefix_string = match prefix {
192            Prefix::None => "".to_string(),
193            Prefix::Hash => "#".to_string(),
194        };
195
196        let expected = format!("{prefix_string}{expected}");
197
198        let hex_colour = colour.to_hex_triplet(prefix);
199
200        assert_eq!(expected, hex_colour);
201    }
202
203    #[rstest]
204    #[case("#8b4513", Brown::SaddleBrown)]
205    #[case("#a0522d", Brown::Sienna)]
206    #[case("#d2691e", Brown::Chocolate)]
207    #[case("#cd853f", Brown::Peru)]
208    #[case("#f4a460", Brown::SandyBrown)]
209    #[case("#deb887", Brown::BurlyWood)]
210    #[case("#d2b48c", Brown::Tan)]
211    #[case("#bc8f8f", Brown::RosyBrown)]
212    #[case("8b4513", Brown::SaddleBrown)]
213    #[case("a0522d", Brown::Sienna)]
214    #[case("d2691e", Brown::Chocolate)]
215    #[case("cd853f", Brown::Peru)]
216    #[case("f4a460", Brown::SandyBrown)]
217    #[case("deb887", Brown::BurlyWood)]
218    #[case("d2b48c", Brown::Tan)]
219    #[case("bc8f8f", Brown::RosyBrown)]
220    #[case("saddlebrown", Brown::SaddleBrown)]
221    #[case("sienna", Brown::Sienna)]
222    #[case("chocolate", Brown::Chocolate)]
223    #[case("peru", Brown::Peru)]
224    #[case("sandybrown", Brown::SandyBrown)]
225    #[case("burlywood", Brown::BurlyWood)]
226    #[case("tan", Brown::Tan)]
227    #[case("rosybrown", Brown::RosyBrown)]
228    fn test_from_str(#[case] input: &str, #[case] expected: Brown) {
229        assert_eq!(expected, Brown::from_str(input).unwrap())
230    }
231
232    #[rstest]
233    #[case("#8b4513", Some(Brown::SaddleBrown))]
234    #[case("#a0522d", Some(Brown::Sienna))]
235    #[case("#d2691e", Some(Brown::Chocolate))]
236    #[case("#cd853f", Some(Brown::Peru))]
237    #[case("#f4a460", Some(Brown::SandyBrown))]
238    #[case("#deb887", Some(Brown::BurlyWood))]
239    #[case("#d2b48c", Some(Brown::Tan))]
240    #[case("#bc8f8f", Some(Brown::RosyBrown))]
241    #[case("8b4513", Some(Brown::SaddleBrown))]
242    #[case("a0522d", Some(Brown::Sienna))]
243    #[case("d2691e", Some(Brown::Chocolate))]
244    #[case("cd853f", Some(Brown::Peru))]
245    #[case("f4a460", Some(Brown::SandyBrown))]
246    #[case("deb887", Some(Brown::BurlyWood))]
247    #[case("d2b48c", Some(Brown::Tan))]
248    #[case("bc8f8f", Some(Brown::RosyBrown))]
249    #[case("saddlebrown", Some(Brown::SaddleBrown))]
250    #[case("sienna", Some(Brown::Sienna))]
251    #[case("chocolate", Some(Brown::Chocolate))]
252    #[case("peru", Some(Brown::Peru))]
253    #[case("sandybrown", Some(Brown::SandyBrown))]
254    #[case("burlywood", Some(Brown::BurlyWood))]
255    #[case("tan", Some(Brown::Tan))]
256    #[case("rosybrown", Some(Brown::RosyBrown))]
257    #[case("012345", None)]
258    fn test_name_colour(#[case] input: &str, #[case] expected: Option<Brown>) {
259        assert_eq!(expected, Brown::name_colour(input))
260    }
261}