1use crate::types::Error;
2use std::{convert::TryFrom, fmt, sync::Arc};
3
4#[derive(Clone, Debug, PartialEq)]
9pub enum Value {
10 Bool(bool),
13
14 Int(i32),
21
22 Flt(f64),
25
26 Str(Arc<str>),
33
34 Color(palette::LinSrgba<u8>),
36}
37
38impl Value {
39 pub fn is_same_type(&self, o: &Value) -> bool {
40 matches!(
41 (self, o),
42 (Value::Bool(_), Value::Bool(_))
43 | (Value::Int(_), Value::Int(_))
44 | (Value::Flt(_), Value::Flt(_))
45 | (Value::Str(_), Value::Str(_))
46 | (Value::Color(_), Value::Color(_))
47 )
48 }
49}
50
51impl fmt::Display for Value {
52 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53 match self {
54 Value::Bool(v) => write!(f, "{}", v),
55 Value::Int(v) => write!(f, "{}", v),
56 Value::Flt(v) => write!(f, "{}", v),
57 Value::Str(v) => write!(f, "\"{}\"", v),
58 Value::Color(v) => {
59 write!(f, "\"#{:02x}{:02x}{:02x}", v.red, v.green, v.blue)?;
60 if v.alpha < 255 {
61 write!(f, "{:02x}", v.alpha)?;
62 }
63 write!(f, "\"")
64 }
65 }
66 }
67}
68
69impl TryFrom<Value> for bool {
70 type Error = Error;
71
72 fn try_from(value: Value) -> Result<Self, Self::Error> {
73 if let Value::Bool(v) = value {
74 Ok(v)
75 } else {
76 Err(Error::TypeError)
77 }
78 }
79}
80
81impl From<bool> for Value {
82 fn from(value: bool) -> Self {
83 Value::Bool(value)
84 }
85}
86
87impl TryFrom<Value> for i32 {
88 type Error = Error;
89
90 fn try_from(value: Value) -> Result<Self, Self::Error> {
91 if let Value::Int(v) = value {
92 return Ok(v);
93 }
94 Err(Error::TypeError)
95 }
96}
97
98impl From<i32> for Value {
99 fn from(value: i32) -> Self {
100 Value::Int(value)
101 }
102}
103
104impl TryFrom<Value> for i16 {
105 type Error = Error;
106
107 fn try_from(value: Value) -> Result<Self, Self::Error> {
108 if let Value::Int(v) = value {
109 if let Ok(v) = i16::try_from(v) {
110 return Ok(v);
111 }
112 }
113 Err(Error::TypeError)
114 }
115}
116
117impl From<i16> for Value {
118 fn from(value: i16) -> Self {
119 Value::Int(i32::from(value))
120 }
121}
122
123impl TryFrom<Value> for u16 {
124 type Error = Error;
125
126 fn try_from(value: Value) -> Result<Self, Self::Error> {
127 if let Value::Int(v) = value {
128 if let Ok(v) = u16::try_from(v) {
129 return Ok(v);
130 }
131 }
132 Err(Error::TypeError)
133 }
134}
135
136impl From<u16> for Value {
137 fn from(value: u16) -> Self {
138 Value::Int(i32::from(value))
139 }
140}
141
142impl TryFrom<Value> for f64 {
143 type Error = Error;
144
145 fn try_from(value: Value) -> Result<Self, Self::Error> {
146 if let Value::Flt(v) = value {
147 Ok(v)
148 } else {
149 Err(Error::TypeError)
150 }
151 }
152}
153
154impl From<f64> for Value {
155 fn from(value: f64) -> Self {
156 Value::Flt(value)
157 }
158}
159
160impl TryFrom<Value> for String {
161 type Error = Error;
162
163 fn try_from(value: Value) -> Result<Self, Self::Error> {
164 if let Value::Str(v) = value {
165 Ok(v.to_string())
166 } else {
167 Err(Error::TypeError)
168 }
169 }
170}
171
172impl TryFrom<Value> for Arc<str> {
173 type Error = Error;
174
175 fn try_from(value: Value) -> Result<Self, Self::Error> {
176 if let Value::Str(v) = value {
177 Ok(v)
178 } else {
179 Err(Error::TypeError)
180 }
181 }
182}
183
184impl From<String> for Value {
185 fn from(value: String) -> Self {
186 Value::Str(value.into())
187 }
188}
189
190impl From<Arc<str>> for Value {
191 fn from(value: Arc<str>) -> Self {
192 Value::Str(value)
193 }
194}
195
196impl From<&str> for Value {
197 fn from(value: &str) -> Self {
198 Value::Str(value.into())
199 }
200}
201
202impl From<palette::LinSrgba<u8>> for Value {
203 fn from(value: palette::LinSrgba<u8>) -> Self {
204 Value::Color(value)
205 }
206}
207
208impl TryFrom<Value> for palette::LinSrgba<u8> {
209 type Error = Error;
210
211 fn try_from(value: Value) -> Result<Self, Self::Error> {
212 if let Value::Color(v) = value {
213 Ok(v)
214 } else {
215 Err(Error::TypeError)
216 }
217 }
218}
219
220fn parse_color(s: &[u8]) -> Option<Value> {
228 let mut result = 0u32;
229
230 for ii in s {
231 if ii.is_ascii_digit() {
232 result = (result << 4) + (ii - b'0') as u32;
233 } else if (b'A'..=b'F').contains(ii) {
234 result = (result << 4) + (ii - b'A' + 10) as u32;
235 } else if (b'a'..=b'f').contains(ii) {
236 result = (result << 4) + (ii - b'a' + 10) as u32;
237 } else {
238 return None;
239 }
240 }
241
242 if s.len() == 6 {
243 result = (result << 8) + 255;
244 }
245
246 Some(Value::Color(palette::LinSrgba::new(
247 (result >> 24) as u8,
248 (result >> 16) as u8,
249 (result >> 8) as u8,
250 result as u8,
251 )))
252}
253
254impl TryFrom<&toml::value::Value> for Value {
255 type Error = Error;
256
257 fn try_from(value: &toml::value::Value) -> Result<Self, Self::Error> {
258 match value {
259 toml::value::Value::Boolean(v) => Ok(Value::Bool(*v)),
260 toml::value::Value::Integer(v) => i32::try_from(*v)
261 .map(Value::Int)
262 .map_err(|_| Error::TypeError),
263 toml::value::Value::Float(v) => Ok(Value::Flt(*v)),
264 toml::value::Value::String(v) => match v.as_bytes() {
265 tmp @ &[b'#', _, _, _, _, _, _]
266 | tmp @ &[b'#', _, _, _, _, _, _, _, _] => {
267 if let Some(v) = parse_color(&tmp[1..]) {
268 Ok(v)
269 } else {
270 Ok(Value::Str(v.to_owned().into()))
271 }
272 }
273 _ => Ok(Value::Str(v.to_owned().into())),
274 },
275 _ => Err(Error::TypeError),
276 }
277 }
278}
279
280#[cfg(test)]
281mod tests {
282 use super::*;
283 use std::convert::TryFrom;
284
285 #[test]
286 fn test_device_values_to() {
287 assert_eq!("false", format!("{}", Value::Bool(false)));
288 assert_eq!("true", format!("{}", Value::Bool(true)));
289
290 assert_eq!("0", format!("{}", Value::Int(0)));
291 assert_eq!("1", format!("{}", Value::Int(1)));
292 assert_eq!("-1", format!("{}", Value::Int(-1)));
293 assert_eq!("-2147483648", format!("{}", Value::Int(-0x80000000)));
294 assert_eq!("2147483647", format!("{}", Value::Int(0x7fffffff)));
295
296 assert_eq!(
297 "\"#010203\"",
298 format!("{}", Value::Color(palette::LinSrgba::new(1, 2, 3, 255)))
299 );
300 assert_eq!(
301 "\"#01020304\"",
302 format!("{}", Value::Color(palette::LinSrgba::new(1, 2, 3, 4)))
303 );
304 }
305
306 #[test]
307 fn test_device_values_from() {
308 assert_eq!(Value::Bool(true), Value::from(true));
309 assert_eq!(Value::Bool(false), Value::from(false));
310
311 assert_eq!(Value::Int(0), Value::from(0i32));
312 assert_eq!(Value::Int(-1), Value::from(-1i32));
313 assert_eq!(Value::Int(2), Value::from(2i32));
314 assert_eq!(Value::Int(-3), Value::from(-3i16));
315 assert_eq!(Value::Int(4), Value::from(4u16));
316
317 assert_eq!(Value::Flt(5.0), Value::from(5.0f64));
318
319 assert_eq!(Value::Str("hello".into()), "hello".into());
320
321 for ii in 1..=255u8 {
324 let r: u8 = ii;
325 let g: u8 = ii ^ 0xa5u8;
326 let b: u8 = 255u8 - ii;
327 let a: u8 = ii ^ 0x81u8;
328
329 assert_eq!(
330 Value::Color(palette::LinSrgba::new(r, g, b, a)),
331 Value::from(palette::LinSrgba::new(r, g, b, a))
332 );
333 }
334 }
335
336 #[test]
337 fn test_device_values_tryfrom() {
338 assert_eq!(bool::try_from(Value::Bool(true)), Ok(true));
341 assert!(bool::try_from(Value::Int(0)).is_err());
342 assert!(bool::try_from(Value::Flt(0.0)).is_err());
343 assert!(bool::try_from(Value::Str("hello".into())).is_err());
344
345 assert!(i32::try_from(Value::Bool(true)).is_err());
348 assert_eq!(i32::try_from(Value::Int(0x7fffffffi32)), Ok(0x7fffffffi32));
349 assert_eq!(
350 i32::try_from(Value::Int(-0x80000000i32)),
351 Ok(-0x80000000i32)
352 );
353 assert!(i32::try_from(Value::Flt(0.0)).is_err());
354 assert!(i32::try_from(Value::Str("hello".into())).is_err());
355
356 assert!(i16::try_from(Value::Bool(true)).is_err());
359 assert_eq!(i16::try_from(Value::Int(0x7fffi32)), Ok(0x7fffi16));
360 assert_eq!(i16::try_from(Value::Int(-0x8000i32)), Ok(-0x8000i16));
361 assert!(i16::try_from(Value::Int(0x8000i32)).is_err());
362 assert!(i16::try_from(Value::Int(-0x8000i32 - 1i32)).is_err());
363 assert!(i16::try_from(Value::Flt(0.0)).is_err());
364 assert!(i16::try_from(Value::Str("hello".into())).is_err());
365
366 assert!(u16::try_from(Value::Bool(true)).is_err());
369 assert_eq!(u16::try_from(Value::Int(0xffffi32)), Ok(0xffffu16));
370 assert_eq!(u16::try_from(Value::Int(0i32)), Ok(0u16));
371 assert!(u16::try_from(Value::Int(0x10000i32)).is_err());
372 assert!(u16::try_from(Value::Int(-1i32)).is_err());
373 assert!(u16::try_from(Value::Flt(0.0)).is_err());
374 assert!(u16::try_from(Value::Str("hello".into())).is_err());
375 }
376
377 #[test]
378 fn test_toml_value_tryfrom() {
379 assert_eq!(
380 Value::try_from(&toml::value::Value::Boolean(true)),
381 Ok(Value::Bool(true))
382 );
383 assert_eq!(
384 Value::try_from(&toml::value::Value::Boolean(false)),
385 Ok(Value::Bool(false))
386 );
387
388 assert_eq!(
389 Value::try_from(&toml::value::Value::Integer(0)),
390 Ok(Value::Int(0))
391 );
392 assert_eq!(
393 Value::try_from(&toml::value::Value::Integer(10)),
394 Ok(Value::Int(10))
395 );
396 assert_eq!(
397 Value::try_from(&toml::value::Value::Integer(-10)),
398 Ok(Value::Int(-10))
399 );
400 assert_eq!(
401 Value::try_from(&toml::value::Value::Integer(0x7fffffff)),
402 Ok(Value::Int(0x7fffffff))
403 );
404 assert_eq!(
405 Value::try_from(&toml::value::Value::Integer(-0x80000000)),
406 Ok(Value::Int(-0x80000000))
407 );
408 assert!(
409 Value::try_from(&toml::value::Value::Integer(-0x80000001)).is_err(),
410 );
411 assert!(
412 Value::try_from(&toml::value::Value::Integer(0x80000000)).is_err(),
413 );
414
415 assert_eq!(
416 Value::try_from(&toml::value::Value::Float(0.0)),
417 Ok(Value::Flt(0.0))
418 );
419 assert_eq!(
420 Value::try_from(&toml::value::Value::Float(10.0)),
421 Ok(Value::Flt(10.0))
422 );
423 assert_eq!(
424 Value::try_from(&toml::value::Value::Float(-10.0)),
425 Ok(Value::Flt(-10.0))
426 );
427
428 assert_eq!(
429 Value::try_from(&toml::value::Value::String("hello".into())),
430 Ok(Value::Str("hello".into()))
431 );
432
433 assert_eq!(
434 Value::try_from(&toml::value::Value::String("#".into())),
435 Ok(Value::Str("#".into()))
436 );
437 assert_eq!(
438 Value::try_from(&toml::value::Value::String("#1".into())),
439 Ok(Value::Str("#1".into()))
440 );
441 assert_eq!(
442 Value::try_from(&toml::value::Value::String("#12".into())),
443 Ok(Value::Str("#12".into()))
444 );
445 assert_eq!(
446 Value::try_from(&toml::value::Value::String("#123".into())),
447 Ok(Value::Str("#123".into()))
448 );
449 assert_eq!(
450 Value::try_from(&toml::value::Value::String("#1234".into())),
451 Ok(Value::Str("#1234".into()))
452 );
453 assert_eq!(
454 Value::try_from(&toml::value::Value::String("#12345".into())),
455 Ok(Value::Str("#12345".into()))
456 );
457 assert_eq!(
458 Value::try_from(&toml::value::Value::String("#1234567".into())),
459 Ok(Value::Str("#1234567".into()))
460 );
461 assert_eq!(
462 Value::try_from(&toml::value::Value::String("#123456789".into())),
463 Ok(Value::Str("#123456789".into()))
464 );
465 assert_eq!(
466 Value::try_from(&toml::value::Value::String("#1234567z".into())),
467 Ok(Value::Str("#1234567z".into()))
468 );
469
470 for ii in 1..=255u8 {
474 let r: u8 = ii;
475 let g: u8 = ii ^ 0xa5u8;
476 let b: u8 = 255u8 - ii;
477 let a: u8 = ii ^ 0xc3u8;
478
479 assert_eq!(
480 Value::try_from(&toml::value::Value::String(format!(
481 "#{:02x}{:02x}{:02x}",
482 r, g, b
483 )))
484 .unwrap(),
485 Value::Color(palette::LinSrgba::new(r, g, b, 255))
486 );
487 assert_eq!(
488 Value::try_from(&toml::value::Value::String(format!(
489 "#{:02X}{:02X}{:02X}",
490 r, g, b
491 )))
492 .unwrap(),
493 Value::Color(palette::LinSrgba::new(r, g, b, 255))
494 );
495 assert_eq!(
496 Value::try_from(&toml::value::Value::String(format!(
497 "#{:02x}{:02x}{:02x}{:02x}",
498 r, g, b, a
499 )))
500 .unwrap(),
501 Value::Color(palette::LinSrgba::new(r, g, b, a))
502 );
503 assert_eq!(
504 Value::try_from(&toml::value::Value::String(format!(
505 "#{:02X}{:02X}{:02X}{:02X}",
506 r, g, b, a
507 )))
508 .unwrap(),
509 Value::Color(palette::LinSrgba::new(r, g, b, a))
510 );
511 }
512
513 assert!(Value::try_from(&toml::value::Value::Datetime(
514 toml::value::Datetime {
515 date: None,
516 time: None,
517 offset: None
518 }
519 ))
520 .is_err());
521 assert!(Value::try_from(&toml::value::Value::Array(vec![])).is_err());
522 assert!(Value::try_from(&toml::value::Value::Table(
523 toml::map::Map::new()
524 ))
525 .is_err());
526 }
527}