cgp-serde 0.2.0

CGP-enabled serde implementation
Documentation
use alloc::string::String;

use cgp::core::field::traits::StaticString;
use cgp::extra::field::impls::{FinalizeOptional, HasOptionalBuilder, SetOptional};
use cgp::prelude::*;
use serde::de::{Error, IgnoredAny, MapAccess, Visitor};

use crate::components::{CanDeserializeValue, ValueDeserializer, ValueDeserializerComponent};
use crate::types::DeserializeWithContext;

pub struct DeserializeRecordFields;

#[cgp_impl(DeserializeRecordFields)]
impl<'de, Record, Builder> ValueDeserializer<'de, Record>
where
    Record: HasOptionalBuilder<Builder = Builder> + HasFields,
    Record::Fields: HandleMapEntry<'de, Self, Builder>,
    Builder: FinalizeOptional<Target = Record>,
{
    fn deserialize<D>(&self, deserializer: D) -> Result<Record, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        deserializer.deserialize_map(MapVisitor {
            context: self,
            phantom: PhantomData,
        })
    }
}

struct MapVisitor<'a, Context, Record> {
    context: &'a Context,
    phantom: PhantomData<Record>,
}

impl<'de, 'a, Context, Record, Builder> Visitor<'de> for MapVisitor<'a, Context, Record>
where
    Record: HasOptionalBuilder<Builder = Builder> + HasFields,
    Record::Fields: HandleMapEntry<'de, Context, Builder>,
    Builder: FinalizeOptional<Target = Record>,
{
    type Value = Record;

    fn expecting(&self, formatter: &mut alloc::fmt::Formatter) -> alloc::fmt::Result {
        write!(formatter, "map")
    }

    fn visit_map<M>(self, mut map: M) -> Result<Self::Value, M::Error>
    where
        M: MapAccess<'de>,
    {
        let mut builder = Record::optional_builder();

        while let Some(key) = map.next_key::<String>()? {
            builder = Record::Fields::handle_map_entry(&mut map, &key, self.context, builder)?;
        }

        let value = builder
            .finalize_optional()
            .map_err(|field| M::Error::custom(format_args!("missing field: {field}")))?;

        Ok(value)
    }
}

trait HandleMapEntry<'de, Context, Builder> {
    fn handle_map_entry<M: MapAccess<'de>>(
        map: &mut M,
        key: &str,
        context: &Context,
        builder: Builder,
    ) -> Result<Builder, M::Error>;
}

impl<'de, Context, Builder, Tag, Value, Tail> HandleMapEntry<'de, Context, Builder>
    for Cons<Field<Tag, Value>, Tail>
where
    Tag: StaticString,
    Tail: HandleMapEntry<'de, Context, Builder>,
    Context: CanDeserializeValue<'de, Value>,
    Builder: SetOptional<Tag, Value = Value>,
{
    fn handle_map_entry<M: MapAccess<'de>>(
        map: &mut M,
        key: &str,
        context: &Context,
        builder: Builder,
    ) -> Result<Builder, M::Error> {
        if key == Tag::VALUE {
            let value = map.next_value_seed(DeserializeWithContext {
                context,
                phantom: PhantomData::<Value>,
            })?;

            let (replaced, builder) = builder.set_optional(PhantomData, value);

            if replaced.is_some() {
                Err(M::Error::custom(format_args!(
                    "duplicate field: {}",
                    Tag::VALUE,
                )))
            } else {
                Ok(builder)
            }
        } else {
            Tail::handle_map_entry(map, key, context, builder)
        }
    }
}

impl<'de, Context, Builder> HandleMapEntry<'de, Context, Builder> for Nil {
    fn handle_map_entry<M: MapAccess<'de>>(
        map: &mut M,
        _key: &str,
        _context: &Context,
        builder: Builder,
    ) -> Result<Builder, M::Error> {
        map.next_value::<IgnoredAny>()?;

        Ok(builder)
    }
}