Skip to main content

tango/
prop.rs

1//! Code for parsing and serializing device or global property values.
2//! 
3//! The parsing code is specific because a property value is only represented as an
4//! array of strings in Tango.
5
6use std::borrow::Cow;
7
8use crate::error::{Result, Error, ErrorKind};
9use crate::value::Spectrum;
10
11
12/// A trait for converting some type into property'd values.
13pub trait IntoProp<'d> {
14    /// Push this property value into the given arrays of string values, this should not
15    /// alter the property value itself.
16    fn into_prop(self) -> impl IntoIterator<Item = Cow<'d, str>>;
17}
18
19/// A trait for parsing a type from some property'd values.
20pub trait FromProp: Sized {
21    /// Parse this property'd type from a series of string values.
22    fn from_prop(values: &Spectrum<str>) -> Result<Self>;
23}
24
25// =========================== //
26//   Default implementations   //
27// =========================== //
28
29macro_rules! impl_prop_for_parse {
30    ( $ty:ty ) => {
31        
32        impl<'d> IntoProp<'d> for $ty {
33            #[inline]
34            fn into_prop(self) -> impl IntoIterator<Item = Cow<'d, str>> {
35                Some(Cow::<str>::Owned(format!("{self}")))
36            }
37        }
38        
39        impl<'d> IntoProp<'d> for &'_ $ty {
40            #[inline]
41            fn into_prop(self) -> impl IntoIterator<Item = Cow<'d, str>> {
42                (*self).into_prop()
43            }
44        }
45        
46        impl FromProp for $ty {
47            fn from_prop(values: &Spectrum<str>) -> Result<Self> {
48                match values.len() {
49                    0 => Err(Error::new(ErrorKind::InvalidProp)
50                        .with_description(concat!("No value in the property, could not convert it to ", stringify!($ty)))),
51                    1 => values.iter().next().expect("one value").parse::<$ty>()
52                        .map_err(|e| Error::new(ErrorKind::InvalidProp)
53                            .with_description(concat!("Failed to parse property value to ", stringify!($ty)))
54                            .with_source(Box::new(e))),
55                    2.. => Err(Error::new(ErrorKind::InvalidProp)
56                        .with_description(concat!("More than one value in the property, could not convert it to ", stringify!($ty)))),
57                }
58            }
59        }
60        
61        impl FromProp for Vec<$ty> {
62            fn from_prop(values: &Spectrum<str>) -> Result<Self> {
63                let mut ret = Vec::with_capacity(values.len());
64                for val in values.iter() {
65                    ret.push(val.parse::<$ty>()
66                        .map_err(|e| Error::new(ErrorKind::InvalidProp)
67                            .with_description(concat!("Failed to parse property value to ", stringify!($ty)))
68                            .with_source(Box::new(e)))?);
69                }
70                Ok(ret)
71            }
72        }
73
74    };
75}
76
77impl_prop_for_parse!(bool);
78impl_prop_for_parse!(u8);
79impl_prop_for_parse!(i8);
80impl_prop_for_parse!(u16);
81impl_prop_for_parse!(i16);
82impl_prop_for_parse!(u32);
83impl_prop_for_parse!(i32);
84impl_prop_for_parse!(u64);
85impl_prop_for_parse!(i64);
86impl_prop_for_parse!(f32);
87impl_prop_for_parse!(f64);
88
89// =========================== //
90//   String implementations    //
91// =========================== //
92
93impl<'d> IntoProp<'d> for Cow<'d, str> {
94    fn into_prop(self) -> impl IntoIterator<Item = Cow<'d, str>> {
95        Some(self)
96    }
97}
98
99impl<'d> IntoProp<'d> for &'d str {
100    fn into_prop(self) -> impl IntoIterator<Item = Cow<'d, str>> {
101        Cow::<str>::Borrowed(self).into_prop()
102    }
103}
104
105impl<'d> IntoProp<'d> for String {
106    fn into_prop(self) -> impl IntoIterator<Item = Cow<'d, str>> {
107        Cow::<str>::Owned(self).into_prop()
108    }
109}
110
111impl<'d> IntoProp<'d> for Vec<String> {
112    fn into_prop(self) -> impl IntoIterator<Item = Cow<'d, str>> {
113        self.into_iter().map(Cow::Owned)
114    }
115}
116
117impl<'d> IntoProp<'d> for &'d String {
118    fn into_prop(self) -> impl IntoIterator<Item = Cow<'d, str>> {
119        Cow::<str>::Borrowed(self).into_prop()
120    }
121}
122
123impl FromProp for String {
124    fn from_prop(values: &Spectrum<str>) -> Result<Self> {
125        let mut buf = String::new();
126        for val in values.iter() {
127            if !buf.is_empty() {
128                buf.push('\n');
129            }
130            buf.push_str(val);
131        }
132        Ok(buf)
133    }
134}
135
136impl FromProp for Vec<String> {
137    fn from_prop(values: &Spectrum<str>) -> Result<Self> {
138        Ok(values.iter().map(str::to_string).collect())
139    }
140}
141
142/// The values as they are, sharing the listing they are a slice of.
143impl FromProp for Spectrum<str> {
144    fn from_prop(values: &Spectrum<str>) -> Result<Self> {
145        Ok(values.clone())
146    }
147}