rstango 0.3.0

Implementation of the Tango distributed control system
Documentation
//! Code for parsing and serializing device or global property values.
//! 
//! The parsing code is specific because a property value is only represented as an
//! array of strings in Tango.

use std::borrow::Cow;

use crate::error::{Result, Error, ErrorKind};
use crate::value::Spectrum;


/// A trait for converting some type into property'd values.
pub trait IntoProp<'d> {
    /// Push this property value into the given arrays of string values, this should not
    /// alter the property value itself.
    fn into_prop(self) -> impl IntoIterator<Item = Cow<'d, str>>;
}

/// A trait for parsing a type from some property'd values.
pub trait FromProp: Sized {
    /// Parse this property'd type from a series of string values.
    fn from_prop(values: &Spectrum<str>) -> Result<Self>;
}

// =========================== //
//   Default implementations   //
// =========================== //

macro_rules! impl_prop_for_parse {
    ( $ty:ty ) => {
        
        impl<'d> IntoProp<'d> for $ty {
            #[inline]
            fn into_prop(self) -> impl IntoIterator<Item = Cow<'d, str>> {
                Some(Cow::<str>::Owned(format!("{self}")))
            }
        }
        
        impl<'d> IntoProp<'d> for &'_ $ty {
            #[inline]
            fn into_prop(self) -> impl IntoIterator<Item = Cow<'d, str>> {
                (*self).into_prop()
            }
        }
        
        impl FromProp for $ty {
            fn from_prop(values: &Spectrum<str>) -> Result<Self> {
                match values.len() {
                    0 => Err(Error::new(ErrorKind::InvalidProp)
                        .with_description(concat!("No value in the property, could not convert it to ", stringify!($ty)))),
                    1 => values.iter().next().expect("one value").parse::<$ty>()
                        .map_err(|e| Error::new(ErrorKind::InvalidProp)
                            .with_description(concat!("Failed to parse property value to ", stringify!($ty)))
                            .with_source(Box::new(e))),
                    2.. => Err(Error::new(ErrorKind::InvalidProp)
                        .with_description(concat!("More than one value in the property, could not convert it to ", stringify!($ty)))),
                }
            }
        }
        
        impl FromProp for Vec<$ty> {
            fn from_prop(values: &Spectrum<str>) -> Result<Self> {
                let mut ret = Vec::with_capacity(values.len());
                for val in values.iter() {
                    ret.push(val.parse::<$ty>()
                        .map_err(|e| Error::new(ErrorKind::InvalidProp)
                            .with_description(concat!("Failed to parse property value to ", stringify!($ty)))
                            .with_source(Box::new(e)))?);
                }
                Ok(ret)
            }
        }

    };
}

impl_prop_for_parse!(bool);
impl_prop_for_parse!(u8);
impl_prop_for_parse!(i8);
impl_prop_for_parse!(u16);
impl_prop_for_parse!(i16);
impl_prop_for_parse!(u32);
impl_prop_for_parse!(i32);
impl_prop_for_parse!(u64);
impl_prop_for_parse!(i64);
impl_prop_for_parse!(f32);
impl_prop_for_parse!(f64);

// =========================== //
//   String implementations    //
// =========================== //

impl<'d> IntoProp<'d> for Cow<'d, str> {
    fn into_prop(self) -> impl IntoIterator<Item = Cow<'d, str>> {
        Some(self)
    }
}

impl<'d> IntoProp<'d> for &'d str {
    fn into_prop(self) -> impl IntoIterator<Item = Cow<'d, str>> {
        Cow::<str>::Borrowed(self).into_prop()
    }
}

impl<'d> IntoProp<'d> for String {
    fn into_prop(self) -> impl IntoIterator<Item = Cow<'d, str>> {
        Cow::<str>::Owned(self).into_prop()
    }
}

impl<'d> IntoProp<'d> for Vec<String> {
    fn into_prop(self) -> impl IntoIterator<Item = Cow<'d, str>> {
        self.into_iter().map(Cow::Owned)
    }
}

impl<'d> IntoProp<'d> for &'d String {
    fn into_prop(self) -> impl IntoIterator<Item = Cow<'d, str>> {
        Cow::<str>::Borrowed(self).into_prop()
    }
}

impl FromProp for String {
    fn from_prop(values: &Spectrum<str>) -> Result<Self> {
        let mut buf = String::new();
        for val in values.iter() {
            if !buf.is_empty() {
                buf.push('\n');
            }
            buf.push_str(val);
        }
        Ok(buf)
    }
}

impl FromProp for Vec<String> {
    fn from_prop(values: &Spectrum<str>) -> Result<Self> {
        Ok(values.iter().map(str::to_string).collect())
    }
}

/// The values as they are, sharing the listing they are a slice of.
impl FromProp for Spectrum<str> {
    fn from_prop(values: &Spectrum<str>) -> Result<Self> {
        Ok(values.clone())
    }
}