1use std::fmt::{self, Debug, Display, Formatter};
2use std::num::ParseIntError;
3use std::str::FromStr;
4use thiserror::Error;
5
6#[derive(Clone, Debug, Eq, PartialEq)]
9pub enum ColorFormat {
10 Rgb,
12 Hsv,
14}
15
16impl ColorFormat {
17 fn as_str(&self) -> &'static str {
18 match self {
19 Self::Rgb => "rgb",
20 Self::Hsv => "hsv",
21 }
22 }
23}
24
25impl Display for ColorFormat {
26 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
27 f.write_str(self.as_str())
28 }
29}
30
31pub trait Color {
32 fn format() -> ColorFormat;
33}
34
35#[derive(Clone, Debug, Error, Eq, PartialEq)]
37#[error("Failed to parse color.")]
38pub struct ParseColorError();
39
40impl From<ParseIntError> for ParseColorError {
41 fn from(_: ParseIntError) -> Self {
42 ParseColorError()
43 }
44}
45
46#[derive(Clone, Debug, Eq, PartialEq)]
48pub struct ColorRgb {
49 pub r: u8,
51 pub g: u8,
53 pub b: u8,
55}
56
57impl ColorRgb {
58 pub fn new(r: u8, g: u8, b: u8) -> Self {
60 ColorRgb { r, g, b }
61 }
62}
63
64impl Display for ColorRgb {
65 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
66 write!(f, "{},{},{}", self.r, self.g, self.b)
67 }
68}
69
70impl FromStr for ColorRgb {
71 type Err = ParseColorError;
72
73 fn from_str(s: &str) -> Result<Self, Self::Err> {
74 let parts: Vec<_> = s.split(',').collect();
75 if let [r, g, b] = parts.as_slice() {
76 Ok(ColorRgb {
77 r: r.parse()?,
78 g: g.parse()?,
79 b: b.parse()?,
80 })
81 } else {
82 Err(ParseColorError())
83 }
84 }
85}
86
87impl Color for ColorRgb {
88 fn format() -> ColorFormat {
89 ColorFormat::Rgb
90 }
91}
92
93#[derive(Clone, Debug, Eq, PartialEq)]
95pub struct ColorHsv {
96 pub h: u16,
98 pub s: u8,
100 pub v: u8,
102}
103
104impl ColorHsv {
105 pub fn new(h: u16, s: u8, v: u8) -> Self {
107 assert!(h <= 360);
108 assert!(s <= 100);
109 assert!(v <= 100);
110 ColorHsv { h, s, v }
111 }
112}
113
114impl Display for ColorHsv {
115 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
116 write!(f, "{},{},{}", self.h, self.s, self.v)
117 }
118}
119
120impl FromStr for ColorHsv {
121 type Err = ParseColorError;
122
123 fn from_str(s: &str) -> Result<Self, Self::Err> {
124 let parts: Vec<_> = s.split(',').collect();
125 if let [h, s, v] = parts.as_slice() {
126 let h = h.parse()?;
127 let s = s.parse()?;
128 let v = v.parse()?;
129 if h <= 360 && s <= 100 && v <= 100 {
130 return Ok(ColorHsv { h, s, v });
131 }
132 }
133 Err(ParseColorError())
134 }
135}
136
137impl Color for ColorHsv {
138 fn format() -> ColorFormat {
139 ColorFormat::Hsv
140 }
141}
142
143#[cfg(test)]
144mod tests {
145 use super::*;
146
147 #[test]
148 fn color_rgb_to_from_string() {
149 let color = ColorRgb::new(111, 222, 42);
150 assert_eq!(color.to_string().parse(), Ok(color));
151 }
152
153 #[test]
154 fn color_hsv_to_from_string() {
155 let color = ColorHsv::new(231, 88, 77);
156 assert_eq!(color.to_string().parse(), Ok(color));
157 }
158
159 #[test]
160 fn color_rgb_parse_invalid() {
161 assert_eq!("".parse::<ColorRgb>(), Err(ParseColorError()));
162 assert_eq!("1,2".parse::<ColorRgb>(), Err(ParseColorError()));
163 assert_eq!("1,2,3,4".parse::<ColorRgb>(), Err(ParseColorError()));
164 assert_eq!("1,2,256".parse::<ColorRgb>(), Err(ParseColorError()));
165 assert_eq!("1,256,3".parse::<ColorRgb>(), Err(ParseColorError()));
166 assert_eq!("256,2,3".parse::<ColorRgb>(), Err(ParseColorError()));
167 assert_eq!("1,-2,3".parse::<ColorRgb>(), Err(ParseColorError()));
168 }
169
170 #[test]
171 fn color_hsv_parse_invalid() {
172 assert_eq!("".parse::<ColorHsv>(), Err(ParseColorError()));
173 assert_eq!("1,2".parse::<ColorHsv>(), Err(ParseColorError()));
174 assert_eq!("1,2,3,4".parse::<ColorHsv>(), Err(ParseColorError()));
175 assert_eq!("1,2,101".parse::<ColorHsv>(), Err(ParseColorError()));
176 assert_eq!("1,101,3".parse::<ColorHsv>(), Err(ParseColorError()));
177 assert_eq!("361,2,3".parse::<ColorHsv>(), Err(ParseColorError()));
178 assert_eq!("1,-2,3".parse::<ColorHsv>(), Err(ParseColorError()));
179 }
180}