Skip to main content

tycho_common/
serde_primitives.rs

1use hex::FromHexError;
2
3fn decode_hex_with_prefix(val: &str) -> Result<Vec<u8>, FromHexError> {
4    let mut stripped: String =
5        if let Some(stripped) = val.strip_prefix("0x") { stripped } else { val }.into();
6
7    // Check if the length of the string is odd
8    if !stripped.len().is_multiple_of(2) {
9        // If it's odd, prepend a zero
10        stripped.insert(0, '0');
11    }
12
13    hex::decode(&stripped)
14}
15
16/// serde functions for handling bytes as hex strings, such as [bytes::Bytes]
17pub mod hex_bytes {
18    use serde::{Deserialize, Deserializer, Serializer};
19
20    use super::decode_hex_with_prefix;
21
22    /// Serialize a byte vec as a hex string with 0x prefix
23    pub fn serialize<S, T>(x: T, s: S) -> Result<S::Ok, S::Error>
24    where
25        S: Serializer,
26        T: AsRef<[u8]>,
27    {
28        s.serialize_str(&format!("0x{}", hex::encode(x.as_ref())))
29    }
30
31    /// Deserialize a hex string into a byte vec
32    /// Accepts a hex string with optional 0x prefix
33    pub fn deserialize<'de, T, D>(d: D) -> Result<T, D::Error>
34    where
35        D: Deserializer<'de>,
36        T: From<Vec<u8>>,
37    {
38        let value = String::deserialize(d)?;
39        decode_hex_with_prefix(&value)
40            .map(Into::into)
41            .map_err(|e| serde::de::Error::custom(e.to_string()))
42    }
43}
44
45/// serde functions for handling Option of bytes
46pub mod hex_bytes_option {
47    use serde::{Deserialize, Deserializer, Serializer};
48
49    use super::decode_hex_with_prefix;
50
51    /// Serialize a byte vec as a Some hex string with 0x prefix
52    pub fn serialize<S, T>(x: &Option<T>, s: S) -> Result<S::Ok, S::Error>
53    where
54        S: Serializer,
55        T: AsRef<[u8]>,
56    {
57        if let Some(x) = x {
58            s.serialize_str(&format!("0x{}", hex::encode(x.as_ref())))
59        } else {
60            s.serialize_none()
61        }
62    }
63
64    /// Deserialize a hex string into a byte vec or None
65    /// Accepts a hex string with optional 0x prefix
66    pub fn deserialize<'de, T, D>(d: D) -> Result<Option<T>, D::Error>
67    where
68        D: Deserializer<'de>,
69        T: From<Vec<u8>>,
70    {
71        let value: Option<String> = Option::deserialize(d)?;
72
73        match value {
74            Some(val) => decode_hex_with_prefix(&val)
75                .map(Into::into)
76                .map(Some)
77                .map_err(|e| serde::de::Error::custom(e.to_string())),
78            None => Ok(None),
79        }
80    }
81}
82
83/// serde functions for handling HashMap with a bytes key
84pub mod hex_hashmap_key {
85    use std::collections::HashMap;
86
87    use serde::{de, ser::SerializeMap, Deserialize, Deserializer, Serialize, Serializer};
88
89    use super::decode_hex_with_prefix;
90    use crate::Bytes;
91
92    pub fn serialize<S, V>(x: &HashMap<Bytes, V>, s: S) -> Result<S::Ok, S::Error>
93    where
94        S: Serializer,
95        V: Serialize,
96    {
97        let mut map = s.serialize_map(Some(x.len()))?;
98        for (k, v) in x.iter() {
99            map.serialize_entry(&format!("{k:#x}"), v)?;
100        }
101        map.end()
102    }
103
104    pub fn deserialize<'de, V, D>(d: D) -> Result<HashMap<Bytes, V>, D::Error>
105    where
106        D: Deserializer<'de>,
107        V: Deserialize<'de>,
108    {
109        let interim = HashMap::<String, V>::deserialize(d)?;
110
111        interim
112            .into_iter()
113            .map(|(k, v)| {
114                let k = decode_hex_with_prefix(&k).map_err(|e| de::Error::custom(e.to_string()))?;
115                Ok((Bytes::from(k), v))
116            })
117            .collect::<Result<HashMap<_, _>, _>>()
118    }
119}
120
121/// serde functions for handling Vec of Bytes as hex strings
122pub mod hex_bytes_vec {
123    use serde::{Deserialize, Deserializer, Serialize, Serializer};
124
125    use super::decode_hex_with_prefix;
126
127    /// Serialize Vec<Vec<u8>> as a list of hex strings with 0x prefix
128    pub fn serialize<S>(list: &[Vec<u8>], s: S) -> Result<S::Ok, S::Error>
129    where
130        S: Serializer,
131    {
132        // Each element to hex string
133        let hex_strings: Vec<String> = list
134            .iter()
135            .map(|x| format!("0x{}", hex::encode(x)))
136            .collect();
137        hex_strings.serialize(s)
138    }
139
140    /// Deserialize a list of hex strings into Vec<Vec<u8>>
141    pub fn deserialize<'de, D>(d: D) -> Result<Vec<Vec<u8>>, D::Error>
142    where
143        D: Deserializer<'de>,
144    {
145        let hex_strings = Vec::<String>::deserialize(d)?;
146        hex_strings
147            .into_iter()
148            .map(|s| {
149                decode_hex_with_prefix(&s).map_err(|e| serde::de::Error::custom(e.to_string()))
150            })
151            .collect()
152    }
153}
154
155/// serde functions for handling HashMap with bytes value
156pub mod hex_hashmap_value {
157    use std::collections::HashMap;
158
159    use serde::{de, ser::SerializeMap, Deserialize, Deserializer, Serialize, Serializer};
160
161    use super::decode_hex_with_prefix;
162    use crate::Bytes;
163
164    pub fn serialize<S, K>(x: &HashMap<K, Bytes>, s: S) -> Result<S::Ok, S::Error>
165    where
166        S: Serializer,
167        K: Serialize,
168    {
169        let mut map = s.serialize_map(Some(x.len()))?;
170        for (k, v) in x.iter() {
171            map.serialize_entry(k, &format!("{v:#x}"))?;
172        }
173        map.end()
174    }
175
176    pub fn deserialize<'de, K, D>(d: D) -> Result<HashMap<K, Bytes>, D::Error>
177    where
178        D: Deserializer<'de>,
179        K: Deserialize<'de> + Eq + std::hash::Hash, // HashMap key trait bounds
180    {
181        let interim = HashMap::<K, String>::deserialize(d)?;
182
183        interim
184            .into_iter()
185            .map(|(k, v)| {
186                let v = decode_hex_with_prefix(&v).map_err(|e| de::Error::custom(e.to_string()))?;
187                Ok((k, Bytes::from(v)))
188            })
189            .collect::<Result<HashMap<_, _>, _>>()
190    }
191}
192
193/// serde functions for handling HashMap with a bytes key and value
194pub mod hex_hashmap_key_value {
195    use std::collections::HashMap;
196
197    use serde::{de, ser::SerializeMap, Deserialize, Deserializer, Serializer};
198
199    use super::decode_hex_with_prefix;
200    use crate::Bytes;
201
202    pub fn serialize<S>(x: &HashMap<Bytes, Bytes>, s: S) -> Result<S::Ok, S::Error>
203    where
204        S: Serializer,
205    {
206        let mut map = s.serialize_map(Some(x.len()))?;
207        for (k, v) in x.iter() {
208            map.serialize_entry(&format!("{k:#x}"), &format!("{v:#x}"))?;
209        }
210        map.end()
211    }
212
213    pub fn deserialize<'de, D>(d: D) -> Result<HashMap<Bytes, Bytes>, D::Error>
214    where
215        D: Deserializer<'de>,
216    {
217        let interim = HashMap::<String, String>::deserialize(d)?;
218        interim
219            .into_iter()
220            .map(|(k, v)| {
221                let k = decode_hex_with_prefix(&k).map_err(|e| de::Error::custom(e.to_string()))?;
222                let v = decode_hex_with_prefix(&v).map_err(|e| de::Error::custom(e.to_string()))?;
223                Ok((k.into(), v.into()))
224            })
225            .collect::<Result<HashMap<_, _>, _>>()
226    }
227}
228
229#[cfg(test)]
230mod tests {
231    use serde::{Deserialize, Serialize};
232
233    use super::*;
234
235    #[derive(Debug, Serialize, Deserialize)]
236    struct TestStruct {
237        #[serde(with = "hex_bytes")]
238        bytes: Vec<u8>,
239
240        #[serde(with = "hex_bytes_option")]
241        bytes_option: Option<Vec<u8>>,
242
243        #[serde(with = "hex_bytes_vec")]
244        bytes_vec: Vec<Vec<u8>>,
245    }
246
247    #[test]
248    fn hex_bytes_serialize_deserialize() {
249        let test_struct = TestStruct {
250            bytes: vec![0u8; 10],
251            bytes_option: Some(vec![0u8; 10]),
252            bytes_vec: vec![vec![0, 1, 2, 3], vec![0xFF, 0xAB]],
253        };
254
255        // Serialize to JSON
256        let serialized = serde_json::to_string(&test_struct).unwrap();
257        assert_eq!(
258            serialized,
259            "{\"bytes\":\"0x00000000000000000000\",\"bytes_option\":\"0x00000000000000000000\",\"bytes_vec\":[\"0x00010203\",\"0xffab\"]}"
260        );
261
262        // Deserialize from JSON
263        let deserialized: TestStruct = serde_json::from_str(&serialized).unwrap();
264        assert_eq!(deserialized.bytes, vec![0u8; 10]);
265        assert_eq!(deserialized.bytes_option, Some(vec![0u8; 10]));
266        assert_eq!(deserialized.bytes_vec, vec![vec![0, 1, 2, 3], vec![0xFF, 0xAB]]);
267    }
268
269    #[test]
270    fn hex_bytes_option_none() {
271        let test_struct =
272            TestStruct { bytes: vec![0u8; 10], bytes_option: None, bytes_vec: vec![] };
273
274        // Serialize to JSON
275        let serialized = serde_json::to_string(&test_struct).unwrap();
276        assert_eq!(
277            serialized,
278            "{\"bytes\":\"0x00000000000000000000\",\"bytes_option\":null,\"bytes_vec\":[]}"
279        );
280
281        // Deserialize from JSON
282        let deserialized: TestStruct = serde_json::from_str(&serialized).unwrap();
283        assert_eq!(deserialized.bytes, vec![0u8; 10]);
284        assert_eq!(deserialized.bytes_option, None);
285    }
286}