Skip to main content

iddqd/id_ord_map/
serde_impls.rs

1use super::{IdOrdItem, IdOrdMap};
2use crate::support::size_hint::cautious;
3use core::{fmt, marker::PhantomData};
4use serde_core::{
5    Deserialize, Deserializer, Serialize, Serializer,
6    de::{MapAccess, SeqAccess, Visitor},
7    ser::{SerializeMap, SerializeSeq},
8};
9
10/// An `IdOrdMap` serializes to the list of items. Items are serialized in
11/// order of their keys.
12///
13/// Serializing as a list of items rather than as a map works around the lack of
14/// non-string keys in formats like JSON.
15///
16/// # Examples
17///
18/// ```
19/// use iddqd::{IdOrdItem, IdOrdMap, id_upcast};
20/// # use iddqd_test_utils::serde_json;
21/// use serde::{Deserialize, Serialize};
22///
23/// #[derive(Debug, Serialize)]
24/// struct Item {
25///     id: u32,
26///     name: String,
27///     email: String,
28/// }
29///
30/// // This is a complex key, so it can't be a JSON map key.
31/// #[derive(Eq, PartialEq, PartialOrd, Ord)]
32/// struct ComplexKey<'a> {
33///     id: u32,
34///     email: &'a str,
35/// }
36///
37/// impl IdOrdItem for Item {
38///     type Key<'a> = ComplexKey<'a>;
39///     fn key(&self) -> Self::Key<'_> {
40///         ComplexKey { id: self.id, email: &self.email }
41///     }
42///     id_upcast!();
43/// }
44///
45/// let mut map = IdOrdMap::<Item>::new();
46/// map.insert_unique(Item {
47///     id: 1,
48///     name: "Alice".to_string(),
49///     email: "alice@example.com".to_string(),
50/// })
51/// .unwrap();
52///
53/// // The map is serialized as a list of items in order of their keys.
54/// let serialized = serde_json::to_string(&map).unwrap();
55/// assert_eq!(
56///     serialized,
57///     r#"[{"id":1,"name":"Alice","email":"alice@example.com"}]"#,
58/// );
59/// ```
60impl<T: IdOrdItem> Serialize for IdOrdMap<T>
61where
62    T: Serialize,
63{
64    fn serialize<S: Serializer>(
65        &self,
66        serializer: S,
67    ) -> Result<S::Ok, S::Error> {
68        let mut seq = serializer.serialize_seq(Some(self.len()))?;
69        for item in self {
70            seq.serialize_element(item)?;
71        }
72        seq.end()
73    }
74}
75
76/// The `Deserialize` impl deserializes from either a sequence or a map of items,
77/// rebuilding the indexes and producing an error if there are any duplicates.
78///
79/// The `fmt::Debug` bound on `T` ensures better error reporting.
80impl<'de, T: IdOrdItem + fmt::Debug> Deserialize<'de> for IdOrdMap<T>
81where
82    T: Deserialize<'de>,
83{
84    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
85    where
86        D: Deserializer<'de>,
87    {
88        deserializer.deserialize_any(SeqVisitor { _marker: PhantomData })
89    }
90}
91
92struct SeqVisitor<T> {
93    _marker: PhantomData<fn() -> T>,
94}
95
96impl<'de, T> Visitor<'de> for SeqVisitor<T>
97where
98    T: IdOrdItem + Deserialize<'de> + fmt::Debug,
99{
100    type Value = IdOrdMap<T>;
101
102    fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
103        formatter
104            .write_str("a sequence or map of items representing an IdOrdMap")
105    }
106
107    fn visit_seq<Access>(
108        self,
109        mut seq: Access,
110    ) -> Result<Self::Value, Access::Error>
111    where
112        Access: SeqAccess<'de>,
113    {
114        let mut map = IdOrdMap::with_capacity(cautious::<T>(seq.size_hint()));
115
116        while let Some(element) = seq.next_element()? {
117            map.insert_unique(element)
118                .map_err(serde_core::de::Error::custom)?;
119        }
120
121        Ok(map)
122    }
123
124    fn visit_map<Access>(
125        self,
126        mut map_access: Access,
127    ) -> Result<Self::Value, Access::Error>
128    where
129        Access: MapAccess<'de>,
130    {
131        let mut map =
132            IdOrdMap::with_capacity(cautious::<T>(map_access.size_hint()));
133
134        while let Some((_, value)) =
135            map_access.next_entry::<serde_core::de::IgnoredAny, T>()?
136        {
137            map.insert_unique(value).map_err(serde_core::de::Error::custom)?;
138        }
139
140        Ok(map)
141    }
142}
143
144/// Marker type for [`IdOrdMap`] serialized as a map, for use with serde's
145/// `with` attribute.
146///
147/// # Examples
148///
149/// Use with serde's `with` attribute:
150///
151/// ```
152/// use iddqd::{IdOrdItem, IdOrdMap, id_ord_map::IdOrdMapAsMap, id_upcast};
153/// use serde::{Deserialize, Serialize};
154///
155/// #[derive(Debug, Serialize, Deserialize)]
156/// struct Item {
157///     id: u32,
158///     name: String,
159/// }
160///
161/// impl IdOrdItem for Item {
162///     type Key<'a> = u32;
163///     fn key(&self) -> Self::Key<'_> {
164///         self.id
165///     }
166///     id_upcast!();
167/// }
168///
169/// #[derive(Serialize, Deserialize)]
170/// struct Config {
171///     #[serde(with = "IdOrdMapAsMap")]
172///     items: IdOrdMap<Item>,
173/// }
174/// ```
175///
176/// # Requirements
177///
178/// - For serialization, the key type must implement [`Serialize`].
179/// - For JSON serialization, the key should be string-like or convertible to a string key.
180pub struct IdOrdMapAsMap<T> {
181    _marker: PhantomData<fn() -> T>,
182}
183
184struct MapVisitorAsMap<T> {
185    _marker: PhantomData<fn() -> T>,
186}
187
188impl<'de, T> Visitor<'de> for MapVisitorAsMap<T>
189where
190    T: IdOrdItem + Deserialize<'de> + fmt::Debug,
191{
192    type Value = IdOrdMap<T>;
193
194    fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
195        formatter.write_str("a map with items representing an IdOrdMap")
196    }
197
198    fn visit_map<Access>(
199        self,
200        mut map_access: Access,
201    ) -> Result<Self::Value, Access::Error>
202    where
203        Access: MapAccess<'de>,
204    {
205        let mut map =
206            IdOrdMap::with_capacity(cautious::<T>(map_access.size_hint()));
207
208        while let Some((_, value)) =
209            map_access.next_entry::<serde_core::de::IgnoredAny, T>()?
210        {
211            map.insert_unique(value).map_err(serde_core::de::Error::custom)?;
212        }
213
214        Ok(map)
215    }
216}
217
218impl<T> IdOrdMapAsMap<T> {
219    /// Serializes an `IdOrdMap` as a JSON object/map using `key()` as keys.
220    pub fn serialize<'a, Ser>(
221        map: &IdOrdMap<T>,
222        serializer: Ser,
223    ) -> Result<Ser::Ok, Ser::Error>
224    where
225        T: 'a + IdOrdItem + Serialize,
226        T::Key<'a>: Serialize,
227        Ser: Serializer,
228    {
229        let mut ser_map = serializer.serialize_map(Some(map.len()))?;
230        for item in map.iter() {
231            // SAFETY:
232            //
233            // * Lifetime extension: for a type T and two lifetime params 'a and
234            //   'b, T<'a> and T<'b> aren't guaranteed to have the same layout,
235            //   but (a) that is true today and (b) it would be shocking and
236            //   break half the Rust ecosystem if that were to change in the
237            //   future.
238            // * We only use key within the scope of this block before
239            //   immediately dropping it. In particular, ser_map.serialize_entry
240            //   serializes the key without holding a reference to it.
241            let key1 = unsafe {
242                core::mem::transmute::<T::Key<'_>, T::Key<'a>>(item.key())
243            };
244            ser_map.serialize_entry(&key1, item)?;
245        }
246        ser_map.end()
247    }
248
249    /// Deserializes an `IdOrdMap` from a JSON object/map.
250    pub fn deserialize<'de, D>(deserializer: D) -> Result<IdOrdMap<T>, D::Error>
251    where
252        T: IdOrdItem + Deserialize<'de> + fmt::Debug,
253        D: Deserializer<'de>,
254    {
255        deserializer.deserialize_map(MapVisitorAsMap { _marker: PhantomData })
256    }
257}