tycho_simulation/
serde_helpers.rs

1/// serde functions for handling bytes as hex strings, such as [bytes::Bytes]
2pub mod hex_bytes {
3    use serde::{Deserialize, Deserializer, Serializer};
4
5    /// Serialize a byte vec as a hex string with 0x prefix
6    pub fn serialize<S, T>(x: T, s: S) -> Result<S::Ok, S::Error>
7    where
8        S: Serializer,
9        T: AsRef<[u8]>,
10    {
11        s.serialize_str(&format!("0x{encoded}", encoded = hex::encode(x.as_ref())))
12    }
13
14    /// Deserialize a hex string into a byte vec
15    /// Accepts a hex string with optional 0x prefix
16    pub fn deserialize<'de, T, D>(d: D) -> Result<T, D::Error>
17    where
18        D: Deserializer<'de>,
19        T: From<Vec<u8>>,
20    {
21        let value = String::deserialize(d)?;
22        if let Some(value) = value.strip_prefix("0x") {
23            hex::decode(value)
24        } else {
25            hex::decode(&value)
26        }
27        .map(Into::into)
28        .map_err(|e| serde::de::Error::custom(e.to_string()))
29    }
30}
31
32/// serde functions for handling Option of bytes
33pub mod hex_bytes_option {
34    use serde::{Deserialize, Deserializer, Serializer};
35
36    /// Serialize a byte vec as a Some hex string with 0x prefix
37    pub fn serialize<S, T>(x: &Option<T>, s: S) -> Result<S::Ok, S::Error>
38    where
39        S: Serializer,
40        T: AsRef<[u8]>,
41    {
42        if let Some(x) = x {
43            s.serialize_str(&format!("0x{encoded}", encoded = hex::encode(x.as_ref())))
44        } else {
45            s.serialize_none()
46        }
47    }
48
49    /// Deserialize a hex string into a byte vec or None
50    /// Accepts a hex string with optional 0x prefix
51    pub fn deserialize<'de, T, D>(d: D) -> Result<Option<T>, D::Error>
52    where
53        D: Deserializer<'de>,
54        T: From<Vec<u8>>,
55    {
56        let value: Option<String> = Option::deserialize(d)?;
57
58        match value {
59            Some(val) => {
60                let val = if let Some(stripped) = val.strip_prefix("0x") { stripped } else { &val };
61                hex::decode(val)
62                    .map(Into::into)
63                    .map(Some)
64                    .map_err(|e| serde::de::Error::custom(e.to_string()))
65            }
66            None => Ok(None),
67        }
68    }
69}
70
71#[cfg(test)]
72mod tests {
73    use serde::{Deserialize, Serialize};
74    use serde_json;
75
76    use super::*;
77
78    #[derive(Debug, Serialize, Deserialize)]
79    struct TestStruct {
80        #[serde(with = "hex_bytes")]
81        bytes: Vec<u8>,
82
83        #[serde(with = "hex_bytes_option")]
84        bytes_option: Option<Vec<u8>>,
85    }
86
87    #[test]
88    fn hex_bytes_serialize_deserialize() {
89        let test_struct = TestStruct { bytes: vec![0u8; 10], bytes_option: Some(vec![0u8; 10]) };
90
91        // Serialize to JSON
92        let serialized = serde_json::to_string(&test_struct).unwrap();
93        assert_eq!(
94            serialized,
95            "{\"bytes\":\"0x00000000000000000000\",\"bytes_option\":\"0x00000000000000000000\"}"
96        );
97
98        // Deserialize from JSON
99        let deserialized: TestStruct = serde_json::from_str(&serialized).unwrap();
100        assert_eq!(deserialized.bytes, vec![0u8; 10]);
101        assert_eq!(deserialized.bytes_option, Some(vec![0u8; 10]));
102    }
103
104    #[test]
105    fn hex_bytes_option_none() {
106        let test_struct = TestStruct { bytes: vec![0u8; 10], bytes_option: None };
107
108        // Serialize to JSON
109        let serialized = serde_json::to_string(&test_struct).unwrap();
110        assert_eq!(serialized, "{\"bytes\":\"0x00000000000000000000\",\"bytes_option\":null}");
111
112        // Deserialize from JSON
113        let deserialized: TestStruct = serde_json::from_str(&serialized).unwrap();
114        assert_eq!(deserialized.bytes, vec![0u8; 10]);
115        assert_eq!(deserialized.bytes_option, None);
116    }
117}