Skip to main content

tango/
prop.rs

1//! Code for parsing and serializing device, class or free 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 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 values.
20pub trait FromProp: Sized {
21    /// Parse this 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!("Property holds no value to convert to ", stringify!($ty)))),
51                    1 => {
52                        let value = values.iter().next().expect("one value");
53                        value.parse::<$ty>()
54                            .map_err(|e| Error::new(ErrorKind::InvalidProp)
55                                .with_description(format!(concat!("Failed to parse property value {:?} as ", stringify!($ty)), value))
56                                .with_source(Box::new(e)))
57                    }
58                    n @ 2.. => Err(Error::new(ErrorKind::InvalidProp)
59                        .with_description(format!(concat!("Property holds {} values where one ", stringify!($ty), " was expected"), n))),
60                }
61            }
62        }
63        
64        impl FromProp for Vec<$ty> {
65            fn from_prop(values: &Spectrum<str>) -> Result<Self> {
66                let mut ret = Vec::with_capacity(values.len());
67                for val in values.iter() {
68                    ret.push(val.parse::<$ty>()
69                        .map_err(|e| Error::new(ErrorKind::InvalidProp)
70                            .with_description(format!(concat!("Failed to parse property value {:?} as ", stringify!($ty)), val))
71                            .with_source(Box::new(e)))?);
72                }
73                Ok(ret)
74            }
75        }
76
77    };
78}
79
80impl_prop_for_parse!(bool);
81impl_prop_for_parse!(u8);
82impl_prop_for_parse!(i8);
83impl_prop_for_parse!(u16);
84impl_prop_for_parse!(i16);
85impl_prop_for_parse!(u32);
86impl_prop_for_parse!(i32);
87impl_prop_for_parse!(u64);
88impl_prop_for_parse!(i64);
89impl_prop_for_parse!(f32);
90impl_prop_for_parse!(f64);
91
92// =========================== //
93//   String implementations    //
94// =========================== //
95
96impl<'d> IntoProp<'d> for Cow<'d, str> {
97    fn into_prop(self) -> impl IntoIterator<Item = Cow<'d, str>> {
98        Some(self)
99    }
100}
101
102impl<'d> IntoProp<'d> for &'d str {
103    fn into_prop(self) -> impl IntoIterator<Item = Cow<'d, str>> {
104        Cow::<str>::Borrowed(self).into_prop()
105    }
106}
107
108impl<'d> IntoProp<'d> for String {
109    fn into_prop(self) -> impl IntoIterator<Item = Cow<'d, str>> {
110        Cow::<str>::Owned(self).into_prop()
111    }
112}
113
114impl<'d> IntoProp<'d> for Vec<String> {
115    fn into_prop(self) -> impl IntoIterator<Item = Cow<'d, str>> {
116        self.into_iter().map(Cow::Owned)
117    }
118}
119
120impl<'d> IntoProp<'d> for &'d String {
121    fn into_prop(self) -> impl IntoIterator<Item = Cow<'d, str>> {
122        Cow::<str>::Borrowed(self).into_prop()
123    }
124}
125
126impl FromProp for String {
127    fn from_prop(values: &Spectrum<str>) -> Result<Self> {
128        let mut buf = String::new();
129        for val in values.iter() {
130            if !buf.is_empty() {
131                buf.push('\n');
132            }
133            buf.push_str(val);
134        }
135        Ok(buf)
136    }
137}
138
139impl FromProp for Vec<String> {
140    fn from_prop(values: &Spectrum<str>) -> Result<Self> {
141        Ok(values.iter().map(str::to_string).collect())
142    }
143}
144
145/// The values as they are, sharing the listing they are a slice of.
146impl FromProp for Spectrum<str> {
147    fn from_prop(values: &Spectrum<str>) -> Result<Self> {
148        Ok(values.clone())
149    }
150}