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}