1use crate::types::Datatype;
2use std::fmt::{self, Debug, Display, Formatter};
3use std::num::ParseIntError;
4use std::str::FromStr;
5use thiserror::Error;
6
7#[derive(Clone, Debug, Error, Eq, PartialEq)]
9pub enum ValueError {
10 #[error("Value not yet known.")]
12 Unknown,
13 #[error("Expected value of type {expected} but was {actual}.")]
16 WrongDatatype {
17 expected: Datatype,
19 actual: Datatype,
21 },
22 #[error("Invalid or unexpected format {format}.")]
25 WrongFormat {
26 format: String,
28 },
29 #[error("Parsing {value} as datatype {datatype} failed.")]
31 ParseFailed {
32 value: String,
34 datatype: Datatype,
36 },
37}
38
39pub trait Value: ToString + FromStr {
41 fn datatype() -> Datatype;
43
44 fn valid_for(datatype: Option<Datatype>, format: &Option<String>) -> Result<(), ValueError> {
51 if let Some(actual) = datatype {
54 let expected = Self::datatype();
55 if actual != expected {
56 return Err(ValueError::WrongDatatype { expected, actual });
57 }
58 }
59
60 if let Some(format) = format {
61 Self::valid_for_format(format)
62 } else {
63 Ok(())
64 }
65 }
66
67 fn valid_for_format(_format: &str) -> Result<(), ValueError> {
71 Ok(())
72 }
73}
74
75impl Value for i64 {
76 fn datatype() -> Datatype {
77 Datatype::Integer
78 }
79}
80
81impl Value for f64 {
82 fn datatype() -> Datatype {
83 Datatype::Float
84 }
85}
86
87impl Value for bool {
88 fn datatype() -> Datatype {
89 Datatype::Boolean
90 }
91}
92
93impl Value for String {
95 fn datatype() -> Datatype {
96 Datatype::String
97 }
98}
99
100#[derive(Clone, Debug, Eq, PartialEq)]
103pub enum ColorFormat {
104 Rgb,
106 Hsv,
108}
109
110impl ColorFormat {
111 fn as_str(&self) -> &'static str {
112 match self {
113 Self::Rgb => "rgb",
114 Self::Hsv => "hsv",
115 }
116 }
117}
118
119impl FromStr for ColorFormat {
120 type Err = ValueError;
121
122 fn from_str(s: &str) -> Result<Self, Self::Err> {
123 match s {
124 "rgb" => Ok(Self::Rgb),
125 "hsv" => Ok(Self::Hsv),
126 _ => Err(ValueError::WrongFormat {
127 format: s.to_owned(),
128 }),
129 }
130 }
131}
132
133impl Display for ColorFormat {
134 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
135 f.write_str(self.as_str())
136 }
137}
138
139pub trait Color: Value {
140 fn format() -> ColorFormat;
141}
142
143impl<T: Color> Value for T {
144 fn datatype() -> Datatype {
145 Datatype::Color
146 }
147
148 fn valid_for_format(format: &str) -> Result<(), ValueError> {
149 if format == Self::format().as_str() {
150 Ok(())
151 } else {
152 Err(ValueError::WrongFormat {
153 format: format.to_owned(),
154 })
155 }
156 }
157}
158
159#[derive(Clone, Debug, Error, Eq, PartialEq)]
161#[error("Failed to parse color.")]
162pub struct ParseColorError();
163
164impl From<ParseIntError> for ParseColorError {
165 fn from(_: ParseIntError) -> Self {
166 ParseColorError()
167 }
168}
169
170#[derive(Clone, Debug, Eq, PartialEq)]
172pub struct ColorRgb {
173 pub r: u8,
175 pub g: u8,
177 pub b: u8,
179}
180
181impl ColorRgb {
182 pub fn new(r: u8, g: u8, b: u8) -> Self {
184 ColorRgb { r, g, b }
185 }
186}
187
188impl Display for ColorRgb {
189 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
190 write!(f, "{},{},{}", self.r, self.g, self.b)
191 }
192}
193
194impl FromStr for ColorRgb {
195 type Err = ParseColorError;
196
197 fn from_str(s: &str) -> Result<Self, Self::Err> {
198 let parts: Vec<_> = s.split(',').collect();
199 if let [r, g, b] = parts.as_slice() {
200 Ok(ColorRgb {
201 r: r.parse()?,
202 g: g.parse()?,
203 b: b.parse()?,
204 })
205 } else {
206 Err(ParseColorError())
207 }
208 }
209}
210
211impl Color for ColorRgb {
212 fn format() -> ColorFormat {
213 ColorFormat::Rgb
214 }
215}
216
217#[derive(Clone, Debug, Eq, PartialEq)]
219pub struct ColorHsv {
220 pub h: u16,
222 pub s: u8,
224 pub v: u8,
226}
227
228impl ColorHsv {
229 pub fn new(h: u16, s: u8, v: u8) -> Self {
231 assert!(h <= 360);
232 assert!(s <= 100);
233 assert!(v <= 100);
234 ColorHsv { h, s, v }
235 }
236}
237
238impl Display for ColorHsv {
239 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
240 write!(f, "{},{},{}", self.h, self.s, self.v)
241 }
242}
243
244impl FromStr for ColorHsv {
245 type Err = ParseColorError;
246
247 fn from_str(s: &str) -> Result<Self, Self::Err> {
248 let parts: Vec<_> = s.split(',').collect();
249 if let [h, s, v] = parts.as_slice() {
250 let h = h.parse()?;
251 let s = s.parse()?;
252 let v = v.parse()?;
253 if h <= 360 && s <= 100 && v <= 100 {
254 return Ok(ColorHsv { h, s, v });
255 }
256 }
257 Err(ParseColorError())
258 }
259}
260
261impl Color for ColorHsv {
262 fn format() -> ColorFormat {
263 ColorFormat::Hsv
264 }
265}
266
267#[derive(Clone, Debug, Eq, PartialEq, Hash)]
271pub struct EnumValue(String);
272
273impl EnumValue {
274 pub fn new(s: &str) -> Self {
275 assert!(!s.is_empty());
276 EnumValue(s.to_owned())
277 }
278}
279
280#[derive(Clone, Debug, Error, Eq, PartialEq)]
282#[error("Empty string is not a valid enum value.")]
283pub struct ParseEnumError();
284
285impl FromStr for EnumValue {
286 type Err = ParseEnumError;
287
288 fn from_str(s: &str) -> Result<Self, Self::Err> {
289 if s.is_empty() {
290 Err(ParseEnumError())
291 } else {
292 Ok(EnumValue::new(s))
293 }
294 }
295}
296
297impl Display for EnumValue {
298 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
299 f.write_str(&self.0)
300 }
301}
302
303impl Value for EnumValue {
304 fn datatype() -> Datatype {
305 Datatype::Enum
306 }
307}
308
309#[cfg(test)]
310mod tests {
311 use super::*;
312
313 #[test]
314 fn color_rgb_to_from_string() {
315 let color = ColorRgb::new(111, 222, 42);
316 assert_eq!(color.to_string().parse(), Ok(color));
317 }
318
319 #[test]
320 fn color_hsv_to_from_string() {
321 let color = ColorHsv::new(231, 88, 77);
322 assert_eq!(color.to_string().parse(), Ok(color));
323 }
324
325 #[test]
326 fn color_rgb_parse_invalid() {
327 assert_eq!("".parse::<ColorRgb>(), Err(ParseColorError()));
328 assert_eq!("1,2".parse::<ColorRgb>(), Err(ParseColorError()));
329 assert_eq!("1,2,3,4".parse::<ColorRgb>(), Err(ParseColorError()));
330 assert_eq!("1,2,256".parse::<ColorRgb>(), Err(ParseColorError()));
331 assert_eq!("1,256,3".parse::<ColorRgb>(), Err(ParseColorError()));
332 assert_eq!("256,2,3".parse::<ColorRgb>(), Err(ParseColorError()));
333 assert_eq!("1,-2,3".parse::<ColorRgb>(), Err(ParseColorError()));
334 }
335
336 #[test]
337 fn color_hsv_parse_invalid() {
338 assert_eq!("".parse::<ColorHsv>(), Err(ParseColorError()));
339 assert_eq!("1,2".parse::<ColorHsv>(), Err(ParseColorError()));
340 assert_eq!("1,2,3,4".parse::<ColorHsv>(), Err(ParseColorError()));
341 assert_eq!("1,2,101".parse::<ColorHsv>(), Err(ParseColorError()));
342 assert_eq!("1,101,3".parse::<ColorHsv>(), Err(ParseColorError()));
343 assert_eq!("361,2,3".parse::<ColorHsv>(), Err(ParseColorError()));
344 assert_eq!("1,-2,3".parse::<ColorHsv>(), Err(ParseColorError()));
345 }
346}