hmap-serde 0.1.0-alpha.4

Serialize/deserialize heterogeneous maps.
Documentation
use std::fmt::Formatter;

use frunk_core::hlist::{HCons, HNil};
use serde::de::{IgnoredAny, MapAccess, Visitor};
use serde::{Deserialize, Deserializer};

use super::utils::deserialize_cow_str;

use super::convert::{IntoHListFilled, IntoHListMaybeUnfilled, MaybeUnfilled};
use super::{HLabelledMap, Labelled};

struct HLabelledMapVisitor<L: IntoHListMaybeUnfilled> {
    maybe_unfilled: L::Output,
}

impl<L: IntoHListMaybeUnfilled> Default for HLabelledMapVisitor<L> {
    fn default() -> Self {
        Self {
            maybe_unfilled: L::create(),
        }
    }
}

pub trait FillByLabel<'de> {
    fn fill_by_name<A: MapAccess<'de>>(&mut self, name: &str, map: &mut A) -> Result<(), A::Error>;
}

impl<'de, H, T> FillByLabel<'de> for HCons<MaybeUnfilled<H>, T>
where
    H: Labelled + Deserialize<'de>,
    T: FillByLabel<'de>,
{
    fn fill_by_name<A: MapAccess<'de>>(&mut self, name: &str, map: &mut A) -> Result<(), A::Error> {
        if H::KEY == name {
            self.head.fill(map.next_value()?);
            Ok(())
        } else {
            self.tail.fill_by_name(name, map)
        }
    }
}

impl<'de, H, T> FillByLabel<'de> for HCons<MaybeUnfilled<Option<H>>, T>
where
    H: Labelled + Deserialize<'de>,
    T: FillByLabel<'de>,
{
    fn fill_by_name<A: MapAccess<'de>>(&mut self, name: &str, map: &mut A) -> Result<(), A::Error> {
        if H::KEY == name {
            self.head.fill(Some(map.next_value()?));
            Ok(())
        } else {
            self.tail.fill_by_name(name, map)
        }
    }
}

impl<'de> FillByLabel<'de> for HNil {
    fn fill_by_name<A: MapAccess<'de>>(
        &mut self,
        _name: &str,
        map: &mut A,
    ) -> Result<(), A::Error> {
        let _: IgnoredAny = map.next_value()?;
        Ok(())
    }
}

impl<'de, L> Visitor<'de> for HLabelledMapVisitor<L>
where
    L: IntoHListMaybeUnfilled,
    L::Output: IntoHListFilled<L> + FillByLabel<'de>,
{
    type Value = HLabelledMap<L>;

    fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
        formatter.write_str("a labelled heterogeneous map")
    }

    fn visit_map<A>(mut self, mut map: A) -> Result<Self::Value, A::Error>
    where
        A: MapAccess<'de>,
    {
        while let Some(key) = deserialize_cow_str(&mut map)? {
            self.maybe_unfilled.fill_by_name(&*key, &mut map)?;
        }
        Ok(HLabelledMap(self.maybe_unfilled.convert()?))
    }
}

impl<'de, L> Deserialize<'de> for HLabelledMap<L>
where
    L: IntoHListMaybeUnfilled,
    L::Output: IntoHListFilled<L> + FillByLabel<'de>,
{
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        deserializer.deserialize_map(HLabelledMapVisitor::default())
    }
}