serde-key-value-vec-map 0.1.0

Deserialize maps or JSON objects in serde to structs that implement the FromKeyValue trait
Documentation
use serde::{
    self,
    de::{Deserializer, MapAccess, Visitor},
    ser::{SerializeMap, Serializer},
    Deserialize, Serialize,
};
use std::{fmt, marker::PhantomData};

pub trait KeyValueLike {
    type Key;
    type Value;

    fn from_key_value(key: Self::Key, value: Self::Value) -> Self;
    fn key(&self) -> &Self::Key;
    fn value(&self) -> &Self::Value;
}

struct KeyValueVecMapVisitor<T: KeyValueLike> {
    marker: PhantomData<fn() -> Vec<T>>,
}

impl<T: KeyValueLike> KeyValueVecMapVisitor<T> {
    fn new() -> Self {
        Self {
            marker: PhantomData,
        }
    }
}

impl<'de, T: KeyValueLike, K, V> Visitor<'de> for KeyValueVecMapVisitor<T>
where
    T: KeyValueLike<Key = K, Value = V>,
    K: Deserialize<'de>,
    V: Deserialize<'de>,
{
    type Value = Vec<T>;

    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        formatter.write_str("key value map")
    }

    fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
    where
        M: MapAccess<'de>,
    {
        let mut vec = Vec::with_capacity(access.size_hint().unwrap_or(0));

        while let Some((key, value)) = access.next_entry()? {
            vec.push(T::from_key_value(key, value));
        }

        Ok(vec)
    }
}

pub fn deserialize<'de, D, T, K, V>(deserializer: D) -> Result<Vec<T>, D::Error>
where
    D: Deserializer<'de>,
    T: KeyValueLike<Key = K, Value = V>,
    K: Deserialize<'de>,
    V: Deserialize<'de>,
{
    deserializer.deserialize_map(KeyValueVecMapVisitor::new())
}

pub fn serialize<S, T, K, V>(pairs: &Vec<T>, serializer: S) -> Result<S::Ok, S::Error>
where
    S: Serializer,
    T: KeyValueLike<Key = K, Value = V>,
    K: Serialize,
    V: Serialize,
{
    let mut map = serializer.serialize_map(Some(pairs.len()))?;
    for pair in pairs {
        map.serialize_entry(pair.key(), pair.value())?;
    }
    map.end()
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_deserialize_with() {
        let json = r#"
        {
            "temperature": 40,
            "pressure": 123
        }
    "#;

        #[derive(Debug, Eq, PartialEq)]
        struct SingleMeasurement {
            name: String,
            value: u32,
        }

        impl KeyValueLike for SingleMeasurement {
            type Key = String;
            type Value = u32;
            fn from_key_value(key: Self::Key, value: Self::Value) -> Self {
                Self { name: key, value }
            }
            fn key(&self) -> &Self::Key {
                &self.name
            }
            fn value(&self) -> &Self::Value {
                &self.value
            }
        }

        #[derive(Deserialize)]
        struct S {
            #[serde(flatten)]
            #[serde(with = "crate")]
            measurements: Vec<SingleMeasurement>,
        }

        let s: S = serde_json::from_str(json).unwrap();

        assert_eq!(
            s.measurements,
            vec![
                SingleMeasurement {
                    name: "temperature".into(),
                    value: 40
                },
                SingleMeasurement {
                    name: "pressure".into(),
                    value: 123
                },
            ]
        );
    }

    #[test]
    fn test_serialize_with() {
        #[derive(Debug, Eq, PartialEq)]
        struct SingleMeasurement {
            name: String,
            value: u32,
        }

        impl KeyValueLike for SingleMeasurement {
            type Key = String;
            type Value = u32;
            fn from_key_value(key: Self::Key, value: Self::Value) -> Self {
                Self { name: key, value }
            }
            fn key(&self) -> &Self::Key {
                &self.name
            }
            fn value(&self) -> &Self::Value {
                &self.value
            }
        }

        #[derive(Serialize)]
        struct S {
            #[serde(flatten)]
            #[serde(with = "crate")]
            measurements: Vec<SingleMeasurement>,
        }

        let s = S {
            measurements: vec![
                SingleMeasurement {
                    name: "temperature".into(),
                    value: 40,
                },
                SingleMeasurement {
                    name: "pressure".into(),
                    value: 123,
                },
            ],
        };

        assert_eq!(
            &serde_json::to_string(&s).unwrap(),
            r#"{"temperature":40,"pressure":123}"#
        );
    }
}