alloy_serde/other/
mod.rs

1//! Support for capturing other fields.
2
3use alloc::{collections::BTreeMap, format, string::String};
4use core::{
5    fmt,
6    ops::{Deref, DerefMut},
7};
8use serde::{de::DeserializeOwned, Deserialize, Serialize};
9use serde_json::Value;
10
11#[cfg(any(test, feature = "arbitrary"))]
12mod arbitrary_;
13
14/// Generic type for capturing additional fields when deserializing structs.
15///
16/// For example, the [optimism `eth_getTransactionByHash` request][optimism] returns additional
17/// fields that this type will capture instead.
18///
19/// Use `deserialize_as` or `deserialize_into` with a struct that captures the unknown fields, or
20/// deserialize the individual fields manually with `get_deserialized`.
21///
22/// This type must be used with [`#[serde(flatten)]`][flatten].
23///
24/// [optimism]: https://docs.alchemy.com/alchemy/apis/optimism/eth-gettransactionbyhash
25/// [flatten]: https://serde.rs/field-attrs.html#flatten
26#[derive(Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
27#[serde(transparent)]
28pub struct OtherFields {
29    inner: BTreeMap<String, serde_json::Value>,
30}
31
32impl OtherFields {
33    /// Creates a new [`OtherFields`] instance.
34    pub const fn new(inner: BTreeMap<String, serde_json::Value>) -> Self {
35        Self { inner }
36    }
37
38    /// Inserts a given value as serialized [`serde_json::Value`] into the map.
39    pub fn insert_value(&mut self, key: String, value: impl Serialize) -> serde_json::Result<()> {
40        self.inner.insert(key, serde_json::to_value(value)?);
41        Ok(())
42    }
43
44    /// Inserts a given value as serialized [`serde_json::Value`] into the map and returns the
45    /// updated instance.
46    pub fn with_value(mut self, key: String, value: impl Serialize) -> serde_json::Result<Self> {
47        self.insert_value(key, value)?;
48        Ok(self)
49    }
50
51    /// Deserialized this type into another container type.
52    pub fn deserialize_as<T: DeserializeOwned>(&self) -> serde_json::Result<T> {
53        serde_json::from_value(Value::Object(self.inner.clone().into_iter().collect()))
54    }
55
56    /// Deserialized this type into another container type.
57    pub fn deserialize_into<T: DeserializeOwned>(self) -> serde_json::Result<T> {
58        serde_json::from_value(serde_json::Value::Object(self.inner.into_iter().collect()))
59    }
60
61    /// Returns the deserialized value of the field, if it exists.
62    /// Deserializes the value with the given closure
63    pub fn get_with<F, V>(&self, key: impl AsRef<str>, with: F) -> Option<V>
64    where
65        F: FnOnce(serde_json::Value) -> V,
66    {
67        self.inner.get(key.as_ref()).cloned().map(with)
68    }
69
70    /// Returns the deserialized value of the field, if it exists
71    pub fn get_deserialized<V: DeserializeOwned>(
72        &self,
73        key: impl AsRef<str>,
74    ) -> Option<serde_json::Result<V>> {
75        self.get_with(key, serde_json::from_value)
76    }
77
78    /// Returns the deserialized value of the field.
79    ///
80    /// Returns an error if the field is missing
81    pub fn try_get_deserialized<V: DeserializeOwned>(
82        &self,
83        key: impl AsRef<str>,
84    ) -> serde_json::Result<V> {
85        let key = key.as_ref();
86        self.get_deserialized(key)
87            .ok_or_else(|| serde::de::Error::custom(format!("Missing field `{key}`")))?
88    }
89
90    /// Removes the deserialized value of the field, if it exists
91    ///
92    /// **Note:** this will also remove the value if deserializing it resulted in an error
93    pub fn remove_deserialized<V: DeserializeOwned>(
94        &mut self,
95        key: impl AsRef<str>,
96    ) -> Option<serde_json::Result<V>> {
97        self.inner.remove(key.as_ref()).map(serde_json::from_value)
98    }
99
100    /// Removes the deserialized value of the field, if it exists.
101    /// Deserializes the value with the given closure
102    ///
103    /// **Note:** this will also remove the value if deserializing it resulted in an error
104    pub fn remove_with<F, V>(&mut self, key: impl AsRef<str>, with: F) -> Option<V>
105    where
106        F: FnOnce(serde_json::Value) -> V,
107    {
108        self.inner.remove(key.as_ref()).map(with)
109    }
110
111    /// Removes the deserialized value of the field, if it exists and also returns the key
112    ///
113    /// **Note:** this will also remove the value if deserializing it resulted in an error
114    pub fn remove_entry_deserialized<V: DeserializeOwned>(
115        &mut self,
116        key: impl AsRef<str>,
117    ) -> Option<(String, serde_json::Result<V>)> {
118        self.inner
119            .remove_entry(key.as_ref())
120            .map(|(key, value)| (key, serde_json::from_value(value)))
121    }
122}
123
124impl fmt::Debug for OtherFields {
125    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
126        f.write_str("OtherFields ")?;
127        self.inner.fmt(f)
128    }
129}
130
131impl TryFrom<serde_json::Value> for OtherFields {
132    type Error = serde_json::Error;
133
134    fn try_from(value: serde_json::Value) -> Result<Self, Self::Error> {
135        serde_json::from_value(value).map(Self::new)
136    }
137}
138
139impl<K> FromIterator<(K, serde_json::Value)> for OtherFields
140where
141    K: Into<String>,
142{
143    fn from_iter<T: IntoIterator<Item = (K, serde_json::Value)>>(iter: T) -> Self {
144        Self { inner: iter.into_iter().map(|(key, value)| (key.into(), value)).collect() }
145    }
146}
147
148impl Deref for OtherFields {
149    type Target = BTreeMap<String, serde_json::Value>;
150
151    #[inline]
152    fn deref(&self) -> &BTreeMap<String, serde_json::Value> {
153        self.as_ref()
154    }
155}
156
157impl DerefMut for OtherFields {
158    fn deref_mut(&mut self) -> &mut Self::Target {
159        &mut self.inner
160    }
161}
162
163impl AsRef<BTreeMap<String, serde_json::Value>> for OtherFields {
164    fn as_ref(&self) -> &BTreeMap<String, serde_json::Value> {
165        &self.inner
166    }
167}
168
169impl IntoIterator for OtherFields {
170    type Item = (String, serde_json::Value);
171    type IntoIter = alloc::collections::btree_map::IntoIter<String, serde_json::Value>;
172
173    fn into_iter(self) -> Self::IntoIter {
174        self.inner.into_iter()
175    }
176}
177
178impl<'a> IntoIterator for &'a OtherFields {
179    type Item = (&'a String, &'a serde_json::Value);
180    type IntoIter = alloc::collections::btree_map::Iter<'a, String, serde_json::Value>;
181
182    fn into_iter(self) -> Self::IntoIter {
183        self.as_ref().iter()
184    }
185}
186
187/// An extension to a struct that allows to capture additional fields when deserializing.
188///
189/// See [`OtherFields`] for more information.
190#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize)]
191#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
192pub struct WithOtherFields<T> {
193    /// The inner struct.
194    #[serde(flatten)]
195    pub inner: T,
196    /// All fields not present in the inner struct.
197    #[serde(flatten)]
198    pub other: OtherFields,
199}
200
201impl<T, U> AsRef<U> for WithOtherFields<T>
202where
203    T: AsRef<U>,
204{
205    fn as_ref(&self) -> &U {
206        self.inner.as_ref()
207    }
208}
209
210impl<T> WithOtherFields<T> {
211    /// Creates a new [`WithOtherFields`] instance.
212    pub fn new(inner: T) -> Self {
213        Self { inner, other: Default::default() }
214    }
215
216    /// Consumes the type and returns the wrapped value.
217    pub fn into_inner(self) -> T {
218        self.inner
219    }
220
221    /// Returns the wrapped value.
222    pub const fn inner(&self) -> &T {
223        &self.inner
224    }
225}
226
227impl<T> Deref for WithOtherFields<T> {
228    type Target = T;
229
230    fn deref(&self) -> &Self::Target {
231        &self.inner
232    }
233}
234
235impl<T> DerefMut for WithOtherFields<T> {
236    fn deref_mut(&mut self) -> &mut Self::Target {
237        &mut self.inner
238    }
239}
240
241impl<'de, T> Deserialize<'de> for WithOtherFields<T>
242where
243    T: Deserialize<'de> + Serialize,
244{
245    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
246    where
247        D: serde::Deserializer<'de>,
248    {
249        #[derive(Deserialize)]
250        struct WithOtherFieldsHelper<T> {
251            #[serde(flatten)]
252            inner: T,
253            #[serde(flatten)]
254            other: OtherFields,
255        }
256
257        let mut helper = WithOtherFieldsHelper::deserialize(deserializer)?;
258        // remove all fields present in the inner struct from the other fields, this is to avoid
259        // duplicate fields in the catch all other fields because serde flatten does not exclude
260        // already deserialized fields when deserializing the other fields.
261        if let Value::Object(map) =
262            serde_json::to_value(&helper.inner).map_err(serde::de::Error::custom)?
263        {
264            for key in map.keys() {
265                helper.other.remove(key);
266            }
267        }
268
269        Ok(Self { inner: helper.inner, other: helper.other })
270    }
271}
272
273#[cfg(test)]
274mod tests {
275    use super::*;
276    use alloc::string::ToString;
277    use rand::Rng;
278    use serde_json::json;
279    use similar_asserts::assert_eq;
280
281    #[test]
282    fn other_fields_arbitrary() {
283        let mut bytes = [0u8; 1024];
284        rand::thread_rng().fill(bytes.as_mut_slice());
285
286        let _ = arbitrary::Unstructured::new(&bytes).arbitrary::<OtherFields>().unwrap();
287    }
288
289    #[test]
290    fn test_correct_other() {
291        #[derive(Serialize, Deserialize)]
292        struct Inner {
293            a: u64,
294        }
295
296        #[derive(Serialize, Deserialize)]
297        struct InnerWrapper {
298            #[serde(flatten)]
299            inner: Inner,
300        }
301
302        let with_other: WithOtherFields<InnerWrapper> =
303            serde_json::from_str("{\"a\": 1, \"b\": 2}").unwrap();
304        assert_eq!(with_other.inner.inner.a, 1);
305        assert_eq!(
306            with_other.other,
307            OtherFields::new(BTreeMap::from_iter([("b".to_string(), serde_json::json!(2))]))
308        );
309    }
310
311    #[test]
312    fn test_with_other_fields_serialization() {
313        #[derive(Serialize, Deserialize, PartialEq, Debug)]
314        struct Inner {
315            a: u64,
316            b: String,
317        }
318
319        let inner = Inner { a: 42, b: "Hello".to_string() };
320        let mut other = BTreeMap::new();
321        other.insert("extra".to_string(), json!(99));
322
323        let with_other = WithOtherFields { inner, other: OtherFields::new(other.clone()) };
324        let serialized = serde_json::to_string(&with_other).unwrap();
325
326        let expected = r#"{"a":42,"b":"Hello","extra":99}"#;
327        assert_eq!(serialized, expected);
328    }
329
330    #[test]
331    fn test_remove_and_access_other_fields() {
332        #[derive(Serialize, Deserialize, PartialEq, Debug)]
333        struct Inner {
334            a: u64,
335            b: String,
336        }
337
338        let json_data = r#"{"a":42,"b":"Hello","extra":99, "another": "test"}"#;
339        let mut with_other: WithOtherFields<Inner> = serde_json::from_str(json_data).unwrap();
340
341        assert_eq!(with_other.other.inner.get("extra"), Some(&json!(99)));
342        assert_eq!(with_other.other.inner.get("another"), Some(&json!("test")));
343
344        with_other.other.remove("extra");
345        assert!(!with_other.other.inner.contains_key("extra"));
346    }
347
348    #[test]
349    fn test_deserialize_as() {
350        let mut map = BTreeMap::new();
351        map.insert("a".to_string(), json!(1));
352        let other_fields = OtherFields::new(map);
353        let deserialized: Result<BTreeMap<String, u64>, _> = other_fields.deserialize_as();
354        assert_eq!(deserialized.unwrap().get("a"), Some(&1));
355    }
356
357    #[test]
358    fn test_deserialize_into() {
359        let mut map = BTreeMap::new();
360        map.insert("a".to_string(), json!(1));
361        let other_fields = OtherFields::new(map);
362        let deserialized: Result<BTreeMap<String, u64>, _> = other_fields.deserialize_into();
363        assert_eq!(deserialized.unwrap().get("a"), Some(&1));
364    }
365
366    #[test]
367    fn test_get_with() {
368        let mut map = BTreeMap::new();
369        map.insert("key".to_string(), json!(42));
370        let other_fields = OtherFields::new(map);
371        let value: Option<u64> = other_fields.get_with("key", |v| v.as_u64().unwrap());
372        assert_eq!(value, Some(42));
373    }
374
375    #[test]
376    fn test_get_deserialized() {
377        let mut map = BTreeMap::new();
378        map.insert("key".to_string(), json!(42));
379        let other_fields = OtherFields::new(map);
380        let value: Option<serde_json::Result<u64>> = other_fields.get_deserialized("key");
381        assert_eq!(value.unwrap().unwrap(), 42);
382    }
383
384    #[test]
385    fn test_remove_deserialized() {
386        let mut map = BTreeMap::new();
387        map.insert("key".to_string(), json!(42));
388        let mut other_fields = OtherFields::new(map);
389        let value: Option<serde_json::Result<u64>> = other_fields.remove_deserialized("key");
390        assert_eq!(value.unwrap().unwrap(), 42);
391        assert!(!other_fields.inner.contains_key("key"));
392    }
393
394    #[test]
395    fn test_remove_with() {
396        let mut map = BTreeMap::new();
397        map.insert("key".to_string(), json!(42));
398        let mut other_fields = OtherFields::new(map);
399        let value: Option<u64> = other_fields.remove_with("key", |v| v.as_u64().unwrap());
400        assert_eq!(value, Some(42));
401        assert!(!other_fields.inner.contains_key("key"));
402    }
403
404    #[test]
405    fn test_remove_entry_deserialized() {
406        let mut map = BTreeMap::new();
407        map.insert("key".to_string(), json!(42));
408        let mut other_fields = OtherFields::new(map);
409        let entry: Option<(String, serde_json::Result<u64>)> =
410            other_fields.remove_entry_deserialized("key");
411        assert!(entry.is_some());
412        let (key, value) = entry.unwrap();
413        assert_eq!(key, "key");
414        assert_eq!(value.unwrap(), 42);
415        assert!(!other_fields.inner.contains_key("key"));
416    }
417
418    #[test]
419    fn test_try_from_value() {
420        let json_value = json!({ "key": "value" });
421        let other_fields = OtherFields::try_from(json_value).unwrap();
422        assert_eq!(other_fields.inner.get("key").unwrap(), &json!("value"));
423    }
424
425    #[test]
426    fn test_into_iter() {
427        let mut map = BTreeMap::new();
428        map.insert("key1".to_string(), json!("value1"));
429        map.insert("key2".to_string(), json!("value2"));
430        let other_fields = OtherFields::new(map.clone());
431
432        let iterated_map: BTreeMap<_, _> = other_fields.into_iter().collect();
433        assert_eq!(iterated_map, map);
434    }
435}