iddqd/tri_hash_map/serde_impls.rs
1use crate::{
2 DefaultHashBuilder, TriHashItem, TriHashMap,
3 support::{
4 alloc::{Allocator, Global},
5 size_hint::cautious,
6 },
7};
8use core::{fmt, hash::BuildHasher, marker::PhantomData};
9use serde_core::{
10 Deserialize, Deserializer, Serialize, Serializer,
11 de::{MapAccess, SeqAccess, Visitor},
12 ser::SerializeMap,
13};
14
15/// A `TriHashMap` serializes to the list of items. Items are serialized in
16/// arbitrary order.
17///
18/// Serializing as a list of items rather than as a map works around the lack of
19/// non-string keys in formats like JSON.
20///
21/// To serialize as a map instead, see [`TriHashMapAsMap`].
22///
23/// # Examples
24///
25/// ```
26/// # #[cfg(feature = "default-hasher")] {
27/// use iddqd::{TriHashItem, TriHashMap, tri_upcast};
28/// # use iddqd_test_utils::serde_json;
29/// use serde::{Deserialize, Serialize};
30///
31/// #[derive(Debug, Serialize)]
32/// struct Item {
33/// id: u32,
34/// name: String,
35/// email: String,
36/// value: usize,
37/// }
38///
39/// // This is a complex key, so it can't be a JSON map key.
40/// #[derive(Eq, Hash, PartialEq)]
41/// struct ComplexKey<'a> {
42/// name: &'a str,
43/// email: &'a str,
44/// }
45///
46/// impl TriHashItem for Item {
47/// type K1<'a> = u32;
48/// type K2<'a> = &'a str;
49/// type K3<'a> = ComplexKey<'a>;
50/// fn key1(&self) -> Self::K1<'_> {
51/// self.id
52/// }
53/// fn key2(&self) -> Self::K2<'_> {
54/// &self.name
55/// }
56/// fn key3(&self) -> Self::K3<'_> {
57/// ComplexKey { name: &self.name, email: &self.email }
58/// }
59/// tri_upcast!();
60/// }
61///
62/// let mut map = TriHashMap::<Item>::new();
63/// map.insert_unique(Item {
64/// id: 1,
65/// name: "Alice".to_string(),
66/// email: "alice@example.com".to_string(),
67/// value: 42,
68/// })
69/// .unwrap();
70///
71/// // The map is serialized as a list of items.
72/// let serialized = serde_json::to_string(&map).unwrap();
73/// assert_eq!(
74/// serialized,
75/// r#"[{"id":1,"name":"Alice","email":"alice@example.com","value":42}]"#,
76/// );
77/// # }
78/// ```
79impl<T: TriHashItem, S: Clone + BuildHasher, A: Allocator> Serialize
80 for TriHashMap<T, S, A>
81where
82 T: Serialize,
83{
84 fn serialize<Ser: Serializer>(
85 &self,
86 serializer: Ser,
87 ) -> Result<Ser::Ok, Ser::Error> {
88 // Serialize just the items -- don't serialize the indexes. We'll
89 // rebuild the indexes on deserialization.
90 self.items.serialize(serializer)
91 }
92}
93
94/// The `Deserialize` impl for `TriHashMap` deserializes from either a sequence
95/// or a map of items, then rebuilds the indexes and produces an error if there
96/// are any duplicates.
97///
98/// In case a map is deserialized, the key is not deserialized or verified
99/// against the value. (In general, verification is not possible because the key
100/// type has a lifetime parameter embedded in it.)
101///
102/// The `fmt::Debug` bound on `T` ensures better error reporting.
103impl<
104 'de,
105 T: TriHashItem + fmt::Debug,
106 S: Clone + BuildHasher + Default,
107 A: Default + Clone + Allocator,
108> Deserialize<'de> for TriHashMap<T, S, A>
109where
110 T: Deserialize<'de>,
111{
112 fn deserialize<D: Deserializer<'de>>(
113 deserializer: D,
114 ) -> Result<Self, D::Error> {
115 deserializer.deserialize_any(SeqVisitor {
116 _marker: PhantomData,
117 hasher: S::default(),
118 alloc: A::default(),
119 })
120 }
121}
122impl<
123 'de,
124 T: TriHashItem + fmt::Debug + Deserialize<'de>,
125 S: Clone + BuildHasher,
126 A: Clone + Allocator,
127> TriHashMap<T, S, A>
128{
129 /// Deserializes from a list of items, allocating new storage within the
130 /// provided allocator.
131 pub fn deserialize_in<D: Deserializer<'de>>(
132 deserializer: D,
133 alloc: A,
134 ) -> Result<Self, D::Error>
135 where
136 S: Default,
137 {
138 deserializer.deserialize_any(SeqVisitor {
139 _marker: PhantomData,
140 hasher: S::default(),
141 alloc,
142 })
143 }
144
145 /// Deserializes from a list of items, with the given hasher, using the
146 /// default allocator.
147 pub fn deserialize_with_hasher<D: Deserializer<'de>>(
148 deserializer: D,
149 hasher: S,
150 ) -> Result<Self, D::Error>
151 where
152 A: Default,
153 {
154 deserializer.deserialize_any(SeqVisitor {
155 _marker: PhantomData,
156 hasher,
157 alloc: A::default(),
158 })
159 }
160
161 /// Deserializes from a list of items, with the given hasher, and allocating
162 /// new storage within the provided allocator.
163 pub fn deserialize_with_hasher_in<D: Deserializer<'de>>(
164 deserializer: D,
165 hasher: S,
166 alloc: A,
167 ) -> Result<Self, D::Error> {
168 // First, deserialize the items.
169 deserializer.deserialize_any(SeqVisitor {
170 _marker: PhantomData,
171 hasher,
172 alloc,
173 })
174 }
175}
176
177struct SeqVisitor<T, S, A> {
178 _marker: PhantomData<fn() -> T>,
179 hasher: S,
180 alloc: A,
181}
182
183impl<'de, T, S, A> Visitor<'de> for SeqVisitor<T, S, A>
184where
185 T: TriHashItem + Deserialize<'de> + fmt::Debug,
186 S: Clone + BuildHasher,
187 A: Clone + Allocator,
188{
189 type Value = TriHashMap<T, S, A>;
190
191 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
192 formatter
193 .write_str("a sequence or map of items representing a TriHashMap")
194 }
195
196 fn visit_seq<Access>(
197 self,
198 mut seq: Access,
199 ) -> Result<Self::Value, Access::Error>
200 where
201 Access: SeqAccess<'de>,
202 {
203 let mut map = TriHashMap::with_capacity_and_hasher_in(
204 cautious::<T>(seq.size_hint()),
205 self.hasher,
206 self.alloc,
207 );
208
209 while let Some(element) = seq.next_element()? {
210 map.insert_unique(element)
211 .map_err(serde_core::de::Error::custom)?;
212 }
213
214 Ok(map)
215 }
216
217 fn visit_map<Access>(
218 self,
219 mut map_access: Access,
220 ) -> Result<Self::Value, Access::Error>
221 where
222 Access: MapAccess<'de>,
223 {
224 let mut map = TriHashMap::with_capacity_and_hasher_in(
225 cautious::<T>(map_access.size_hint()),
226 self.hasher,
227 self.alloc,
228 );
229
230 while let Some((_, value)) =
231 map_access.next_entry::<serde_core::de::IgnoredAny, T>()?
232 {
233 map.insert_unique(value).map_err(serde_core::de::Error::custom)?;
234 }
235
236 Ok(map)
237 }
238}
239
240/// Marker type for [`TriHashMap`] serialized as a map, for use with serde's
241/// `with` attribute.
242///
243/// The key type [`Self::K1`](TriHashItem::K1) is used as the map key.
244///
245/// # Examples
246///
247/// Use with serde's `with` attribute:
248///
249/// ```
250/// # #[cfg(feature = "default-hasher")] {
251/// use iddqd::{
252/// TriHashItem, TriHashMap, tri_hash_map::TriHashMapAsMap, tri_upcast,
253/// };
254/// use serde::{Deserialize, Serialize};
255///
256/// #[derive(Debug, Serialize, Deserialize)]
257/// struct Item {
258/// id: u32,
259/// name: String,
260/// email: String,
261/// }
262///
263/// impl TriHashItem for Item {
264/// type K1<'a> = u32;
265/// type K2<'a> = &'a str;
266/// type K3<'a> = &'a str;
267/// fn key1(&self) -> Self::K1<'_> {
268/// self.id
269/// }
270/// fn key2(&self) -> Self::K2<'_> {
271/// &self.name
272/// }
273/// fn key3(&self) -> Self::K3<'_> {
274/// &self.email
275/// }
276/// tri_upcast!();
277/// }
278///
279/// #[derive(Serialize, Deserialize)]
280/// struct Config {
281/// #[serde(with = "TriHashMapAsMap")]
282/// items: TriHashMap<Item>,
283/// }
284/// # }
285/// ```
286///
287/// # Requirements
288///
289/// - For serialization, the key type `K1` must implement [`Serialize`].
290/// - For JSON serialization, `K1` should be string-like or convertible to a string key.
291pub struct TriHashMapAsMap<T, S = DefaultHashBuilder, A: Allocator = Global> {
292 #[expect(clippy::type_complexity)]
293 _marker: PhantomData<fn() -> (T, S, A)>,
294}
295
296struct MapVisitorAsMap<T, S, A> {
297 _marker: PhantomData<fn() -> T>,
298 hasher: S,
299 alloc: A,
300}
301
302impl<'de, T, S, A> Visitor<'de> for MapVisitorAsMap<T, S, A>
303where
304 T: TriHashItem + Deserialize<'de> + fmt::Debug,
305 S: Clone + BuildHasher,
306 A: Clone + Allocator,
307{
308 type Value = TriHashMap<T, S, A>;
309
310 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
311 formatter.write_str("a map with items representing a TriHashMap")
312 }
313
314 fn visit_map<Access>(
315 self,
316 mut map_access: Access,
317 ) -> Result<Self::Value, Access::Error>
318 where
319 Access: MapAccess<'de>,
320 {
321 let mut map = TriHashMap::with_capacity_and_hasher_in(
322 cautious::<T>(map_access.size_hint()),
323 self.hasher,
324 self.alloc,
325 );
326
327 while let Some((_, value)) =
328 map_access.next_entry::<serde_core::de::IgnoredAny, T>()?
329 {
330 map.insert_unique(value).map_err(serde_core::de::Error::custom)?;
331 }
332
333 Ok(map)
334 }
335}
336
337impl<T, S, A> TriHashMapAsMap<T, S, A>
338where
339 S: Clone + BuildHasher,
340 A: Allocator,
341{
342 /// Serializes a `TriHashMap` as a JSON object/map using `key1()` as keys.
343 pub fn serialize<'a, Ser>(
344 map: &TriHashMap<T, S, A>,
345 serializer: Ser,
346 ) -> Result<Ser::Ok, Ser::Error>
347 where
348 T: 'a + TriHashItem + Serialize,
349 T::K1<'a>: Serialize,
350 Ser: Serializer,
351 {
352 let mut ser_map = serializer.serialize_map(Some(map.len()))?;
353 for item in map.iter() {
354 let key1 = item.key1();
355 // SAFETY:
356 //
357 // * Lifetime extension: for a type T and two lifetime params 'a and
358 // 'b, T<'a> and T<'b> aren't guaranteed to have the same layout,
359 // but (a) that is true today and (b) it would be shocking and
360 // break half the Rust ecosystem if that were to change in the
361 // future.
362 // * We only use key within the scope of this block before
363 // immediately dropping it. In particular, ser_map.serialize_entry
364 // serializes the key without holding a reference to it.
365 let key1 =
366 unsafe { core::mem::transmute::<T::K1<'_>, T::K1<'a>>(key1) };
367 ser_map.serialize_entry(&key1, item)?;
368 }
369 ser_map.end()
370 }
371
372 /// Deserializes a `TriHashMap` from a JSON object/map.
373 pub fn deserialize<'de, D>(
374 deserializer: D,
375 ) -> Result<TriHashMap<T, S, A>, D::Error>
376 where
377 T: TriHashItem + Deserialize<'de> + fmt::Debug,
378 S: Default,
379 A: Clone + Default,
380 D: Deserializer<'de>,
381 {
382 deserializer.deserialize_map(MapVisitorAsMap {
383 _marker: PhantomData,
384 hasher: S::default(),
385 alloc: A::default(),
386 })
387 }
388}