serde-cs 0.2.4

Serde serialization/deserialization for camma separated lists
Documentation
use serde::de;
use serde::ser;

use std::str::FromStr;
use std::{fmt, vec};

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CS<T>(pub Vec<T>);

impl<T> Default for CS<T> {
    #[inline]
    fn default() -> Self {
        Self(Default::default())
    }
}

impl<T> AsRef<[T]> for CS<T> {
    #[inline]
    fn as_ref(&self) -> &[T] {
        &self.0
    }
}

impl<T> From<Vec<T>> for CS<T> {
    #[inline]
    fn from(v: Vec<T>) -> Self {
        Self(v)
    }
}

impl<T> CS<T> {
    #[inline]
    pub fn into_inner(self) -> Vec<T> {
        self.0
    }

    #[inline]
    pub fn to_inner(&self) -> &Vec<T> {
        &self.0
    }

    #[inline]
    pub fn to_inner_mut(&mut self) -> &mut Vec<T> {
        &mut self.0
    }
}

impl<T: FromStr> FromStr for CS<T> {
    type Err = T::Err;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        s.split(',')
            .filter(|s| !s.is_empty())
            .map(T::from_str)
            .collect::<Result<Vec<_>, _>>()
            .map(Self)
    }
}

impl<T> IntoIterator for CS<T> {
    type Item = T;
    type IntoIter = vec::IntoIter<T>;

    fn into_iter(self) -> Self::IntoIter {
        self.0.into_iter()
    }
}

impl<T: fmt::Display> fmt::Display for CS<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut it = self.0.iter();
        if let Some(v) = it.next() {
            <T as fmt::Display>::fmt(v, f)?;
        }

        for v in it {
            write!(f, ",{}", v)?
        }

        Ok(())
    }
}

impl<T: fmt::Display> ser::Serialize for CS<T> {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: ser::Serializer,
    {
        serializer.serialize_str(&self.to_string())
    }
}

impl<'de, T> de::Deserialize<'de> for CS<T>
where
    T: FromStr,
    T::Err: fmt::Display,
{
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: de::Deserializer<'de>,
    {
        use std::marker::PhantomData;

        struct CsVisitor<T>(PhantomData<T>);

        impl<'de, T> de::Visitor<'de> for CsVisitor<T>
        where
            T: FromStr,
            T::Err: fmt::Display,
        {
            type Value = CS<T>;

            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                formatter.write_str("comma separeted list")
            }

            fn visit_str<E>(self, values: &str) -> Result<Self::Value, E>
            where
                E: de::Error,
            {
                values.parse().map_err(de::Error::custom)
            }
        }

        deserializer.deserialize_str(CsVisitor(PhantomData))
    }
}

#[cfg(test)]
mod tests {
    use super::CS;
    type CsTest = CS<u32>;

    fn assert_ok_from_str(s: &str, expected: Vec<u32>) {
        let cs: Result<CsTest, _> = s.parse();
        assert!(matches!(cs, Ok(v) if v == CS(expected)))
    }

    fn assert_err_from_str(s: &str) {
        let cs: Result<CsTest, _> = s.parse();
        assert!(cs.is_err())
    }

    #[test]
    fn from_str() {
        assert_ok_from_str("", vec![]);
        assert_ok_from_str(",,,,", vec![]);

        assert_ok_from_str("1", vec![1]);
        assert_ok_from_str(",1", vec![1]);
        assert_ok_from_str("1,", vec![1]);
        assert_ok_from_str(",,,1,", vec![1]);

        assert_ok_from_str("1,2", vec![1, 2]);
        assert_ok_from_str("1,2,3,4,5", vec![1, 2, 3, 4, 5]);
        assert_ok_from_str("1,,,,,2", vec![1, 2]);
        assert_ok_from_str(",,,1,,,,,2,,,,,", vec![1, 2]);

        assert_err_from_str("-1");
        assert_err_from_str("1,a,");
    }

    fn assert_to_string(values: Vec<u32>, expected: &str) {
        let cs = CS(values).to_string();
        assert_eq!(cs, expected);
    }

    #[test]
    fn to_string() {
        assert_to_string(vec![], "");
        assert_to_string(vec![1], "1");
        assert_to_string(vec![1, 2], "1,2");
        assert_to_string(vec![1, 2, 3, 4, 5], "1,2,3,4,5");
    }

    fn assert_ok_des(s: &str, expected: Vec<u32>) {
        let cs: Result<CsTest, _> = serde_json::from_str(s);
        assert!(matches!(cs, Ok(v) if v == CS(expected)))
    }

    fn assert_err_des(s: &str) {
        let cs: Result<CsTest, _> = serde_json::from_str(s);
        assert!(cs.is_err())
    }

    #[test]
    fn deserialize() {
        assert_ok_des(r#""""#, vec![]);
        assert_ok_des(r#"",,,,""#, vec![]);

        assert_ok_des(r#""1""#, vec![1]);
        assert_ok_des(r#"",1""#, vec![1]);
        assert_ok_des(r#""1,""#, vec![1]);
        assert_ok_des(r#"",,,1,""#, vec![1]);

        assert_ok_des(r#""1,2""#, vec![1, 2]);
        assert_ok_des(r#""1,2,3,4,5""#, vec![1, 2, 3, 4, 5]);
        assert_ok_des(r#""1,,,,,2""#, vec![1, 2]);
        assert_ok_des(r#"",,,1,,,,,2,,,,,""#, vec![1, 2]);

        assert_err_des(r#""-1""#);
        assert_err_des(r#""1,a,""#);
    }

    fn assert_ser(values: Vec<u32>, expected: &str) {
        let cs = serde_json::to_string(&CS(values));
        assert!(matches!(cs, Ok(v) if v == expected))
    }

    #[test]
    fn serialize() {
        assert_ser(vec![], r#""""#);
        assert_ser(vec![1], r#""1""#);
        assert_ser(vec![1, 2], r#""1,2""#);
        assert_ser(vec![1, 2, 3, 4, 5], r#""1,2,3,4,5""#);
    }
}