iddqd/tri_hash_map/
serde_impls.rs

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