iddqd/id_ord_map/
serde_impls.rs

1use super::{IdOrdItem, IdOrdMap};
2use core::{fmt, marker::PhantomData};
3use serde_core::{
4    Deserialize, Deserializer, Serialize, Serializer,
5    de::{MapAccess, SeqAccess, Visitor},
6    ser::{SerializeMap, SerializeSeq},
7};
8
9/// An `IdOrdMap` serializes to the list of items. Items are serialized in
10/// order of their keys.
11///
12/// Serializing as a list of items rather than as a map works around the lack of
13/// non-string keys in formats like JSON.
14///
15/// # Examples
16///
17/// ```
18/// use iddqd::{IdOrdItem, IdOrdMap, id_upcast};
19/// # use iddqd_test_utils::serde_json;
20/// use serde::{Deserialize, Serialize};
21///
22/// #[derive(Debug, Serialize)]
23/// struct Item {
24///     id: u32,
25///     name: String,
26///     email: String,
27/// }
28///
29/// // This is a complex key, so it can't be a JSON map key.
30/// #[derive(Eq, PartialEq, PartialOrd, Ord)]
31/// struct ComplexKey<'a> {
32///     id: u32,
33///     email: &'a str,
34/// }
35///
36/// impl IdOrdItem for Item {
37///     type Key<'a> = ComplexKey<'a>;
38///     fn key(&self) -> Self::Key<'_> {
39///         ComplexKey { id: self.id, email: &self.email }
40///     }
41///     id_upcast!();
42/// }
43///
44/// let mut map = IdOrdMap::<Item>::new();
45/// map.insert_unique(Item {
46///     id: 1,
47///     name: "Alice".to_string(),
48///     email: "alice@example.com".to_string(),
49/// })
50/// .unwrap();
51///
52/// // The map is serialized as a list of items in order of their keys.
53/// let serialized = serde_json::to_string(&map).unwrap();
54/// assert_eq!(
55///     serialized,
56///     r#"[{"id":1,"name":"Alice","email":"alice@example.com"}]"#,
57/// );
58/// ```
59impl<T: IdOrdItem> Serialize for IdOrdMap<T>
60where
61    T: Serialize,
62{
63    fn serialize<S: Serializer>(
64        &self,
65        serializer: S,
66    ) -> Result<S::Ok, S::Error> {
67        let mut seq = serializer.serialize_seq(Some(self.len()))?;
68        for item in self {
69            seq.serialize_element(item)?;
70        }
71        seq.end()
72    }
73}
74
75/// The `Deserialize` impl deserializes from either a sequence or a map of items,
76/// rebuilding the indexes and producing an error if there are any duplicates.
77///
78/// The `fmt::Debug` bound on `T` ensures better error reporting.
79impl<'de, T: IdOrdItem + fmt::Debug> Deserialize<'de> for IdOrdMap<T>
80where
81    T: Deserialize<'de>,
82{
83    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
84    where
85        D: Deserializer<'de>,
86    {
87        deserializer.deserialize_any(SeqVisitor { _marker: PhantomData })
88    }
89}
90
91struct SeqVisitor<T> {
92    _marker: PhantomData<fn() -> T>,
93}
94
95impl<'de, T> Visitor<'de> for SeqVisitor<T>
96where
97    T: IdOrdItem + Deserialize<'de> + fmt::Debug,
98{
99    type Value = IdOrdMap<T>;
100
101    fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
102        formatter
103            .write_str("a sequence or map of items representing an IdOrdMap")
104    }
105
106    fn visit_seq<Access>(
107        self,
108        mut seq: Access,
109    ) -> Result<Self::Value, Access::Error>
110    where
111        Access: SeqAccess<'de>,
112    {
113        let mut map = match seq.size_hint() {
114            Some(size) => IdOrdMap::with_capacity(size),
115            None => IdOrdMap::new(),
116        };
117
118        while let Some(element) = seq.next_element()? {
119            map.insert_unique(element)
120                .map_err(serde_core::de::Error::custom)?;
121        }
122
123        Ok(map)
124    }
125
126    fn visit_map<Access>(
127        self,
128        mut map_access: Access,
129    ) -> Result<Self::Value, Access::Error>
130    where
131        Access: MapAccess<'de>,
132    {
133        let mut map = IdOrdMap::new();
134
135        while let Some((_, value)) =
136            map_access.next_entry::<serde_core::de::IgnoredAny, T>()?
137        {
138            map.insert_unique(value).map_err(serde_core::de::Error::custom)?;
139        }
140
141        Ok(map)
142    }
143}
144
145/// Marker type for [`IdOrdMap`] serialized as a map, for use with serde's
146/// `with` attribute.
147///
148/// # Examples
149///
150/// Use with serde's `with` attribute:
151///
152/// ```
153/// use iddqd::{IdOrdItem, IdOrdMap, id_ord_map::IdOrdMapAsMap, id_upcast};
154/// use serde::{Deserialize, Serialize};
155///
156/// #[derive(Debug, Serialize, Deserialize)]
157/// struct Item {
158///     id: u32,
159///     name: String,
160/// }
161///
162/// impl IdOrdItem for Item {
163///     type Key<'a> = u32;
164///     fn key(&self) -> Self::Key<'_> {
165///         self.id
166///     }
167///     id_upcast!();
168/// }
169///
170/// #[derive(Serialize, Deserialize)]
171/// struct Config {
172///     #[serde(with = "IdOrdMapAsMap")]
173///     items: IdOrdMap<Item>,
174/// }
175/// ```
176///
177/// # Requirements
178///
179/// - For serialization, the key type must implement [`Serialize`].
180/// - For JSON serialization, the key should be string-like or convertible to a string key.
181pub struct IdOrdMapAsMap<T> {
182    _marker: PhantomData<fn() -> T>,
183}
184
185struct MapVisitorAsMap<T> {
186    _marker: PhantomData<fn() -> T>,
187}
188
189impl<'de, T> Visitor<'de> for MapVisitorAsMap<T>
190where
191    T: IdOrdItem + Deserialize<'de> + fmt::Debug,
192{
193    type Value = IdOrdMap<T>;
194
195    fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
196        formatter.write_str("a map with items representing an IdOrdMap")
197    }
198
199    fn visit_map<Access>(
200        self,
201        mut map_access: Access,
202    ) -> Result<Self::Value, Access::Error>
203    where
204        Access: MapAccess<'de>,
205    {
206        let mut map = IdOrdMap::new();
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}