Skip to main content

cgp_serde/providers/
record.rs

1use alloc::string::String;
2
3use cgp::core::field::traits::StaticString;
4use cgp::extra::field::impls::{FinalizeOptional, HasOptionalBuilder, SetOptional};
5use cgp::prelude::*;
6use serde::de::{Error, IgnoredAny, MapAccess, Visitor};
7
8use crate::components::{CanDeserializeValue, ValueDeserializer, ValueDeserializerComponent};
9use crate::types::DeserializeWithContext;
10
11pub struct DeserializeRecordFields;
12
13#[cgp_impl(DeserializeRecordFields)]
14impl<'de, Record, Builder> ValueDeserializer<'de, Record>
15where
16    Record: HasOptionalBuilder<Builder = Builder> + HasFields,
17    Record::Fields: HandleMapEntry<'de, Self, Builder>,
18    Builder: FinalizeOptional<Target = Record>,
19{
20    fn deserialize<D>(&self, deserializer: D) -> Result<Record, D::Error>
21    where
22        D: serde::Deserializer<'de>,
23    {
24        deserializer.deserialize_map(MapVisitor {
25            context: self,
26            phantom: PhantomData,
27        })
28    }
29}
30
31struct MapVisitor<'a, Context, Record> {
32    context: &'a Context,
33    phantom: PhantomData<Record>,
34}
35
36impl<'de, 'a, Context, Record, Builder> Visitor<'de> for MapVisitor<'a, Context, Record>
37where
38    Record: HasOptionalBuilder<Builder = Builder> + HasFields,
39    Record::Fields: HandleMapEntry<'de, Context, Builder>,
40    Builder: FinalizeOptional<Target = Record>,
41{
42    type Value = Record;
43
44    fn expecting(&self, formatter: &mut alloc::fmt::Formatter) -> alloc::fmt::Result {
45        write!(formatter, "map")
46    }
47
48    fn visit_map<M>(self, mut map: M) -> Result<Self::Value, M::Error>
49    where
50        M: MapAccess<'de>,
51    {
52        let mut builder = Record::optional_builder();
53
54        while let Some(key) = map.next_key::<String>()? {
55            builder = Record::Fields::handle_map_entry(&mut map, &key, self.context, builder)?;
56        }
57
58        let value = builder
59            .finalize_optional()
60            .map_err(|field| M::Error::custom(format_args!("missing field: {field}")))?;
61
62        Ok(value)
63    }
64}
65
66trait HandleMapEntry<'de, Context, Builder> {
67    fn handle_map_entry<M: MapAccess<'de>>(
68        map: &mut M,
69        key: &str,
70        context: &Context,
71        builder: Builder,
72    ) -> Result<Builder, M::Error>;
73}
74
75impl<'de, Context, Builder, Tag, Value, Tail> HandleMapEntry<'de, Context, Builder>
76    for Cons<Field<Tag, Value>, Tail>
77where
78    Tag: StaticString,
79    Tail: HandleMapEntry<'de, Context, Builder>,
80    Context: CanDeserializeValue<'de, Value>,
81    Builder: SetOptional<Tag, Value = Value>,
82{
83    fn handle_map_entry<M: MapAccess<'de>>(
84        map: &mut M,
85        key: &str,
86        context: &Context,
87        builder: Builder,
88    ) -> Result<Builder, M::Error> {
89        if key == Tag::VALUE {
90            let value = map.next_value_seed(DeserializeWithContext {
91                context,
92                phantom: PhantomData::<Value>,
93            })?;
94
95            let (replaced, builder) = builder.set_optional(PhantomData, value);
96
97            if replaced.is_some() {
98                Err(M::Error::custom(format_args!(
99                    "duplicate field: {}",
100                    Tag::VALUE,
101                )))
102            } else {
103                Ok(builder)
104            }
105        } else {
106            Tail::handle_map_entry(map, key, context, builder)
107        }
108    }
109}
110
111impl<'de, Context, Builder> HandleMapEntry<'de, Context, Builder> for Nil {
112    fn handle_map_entry<M: MapAccess<'de>>(
113        map: &mut M,
114        _key: &str,
115        _context: &Context,
116        builder: Builder,
117    ) -> Result<Builder, M::Error> {
118        map.next_value::<IgnoredAny>()?;
119
120        Ok(builder)
121    }
122}