Skip to main content

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}