defuse_serde_utils/
hex.rs

1use derive_more::From;
2use serde::{Deserialize, Serialize};
3use serde_with::serde_as;
4
5#[cfg_attr(
6    feature = "abi",
7    serde_as(schemars = true),
8    derive(::schemars::JsonSchema),
9    schemars(transparent)
10)]
11#[cfg_attr(not(feature = "abi"), serde_as(schemars = false))]
12#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, From)]
13#[serde(bound(serialize = "T: AsRef<[u8]>", deserialize = "T: TryFrom<Vec<u8>>"))]
14/// Helper type to implement `#[derive(Serialize, Deserialize)]`,
15/// as `#[near_bindgen]` doesn't support `#[serde(...)]` attributes on method arguments
16pub struct AsHex<T>(#[serde_as(as = "::serde_with::hex::Hex")] pub T);
17
18impl<T> AsHex<T> {
19    #[inline]
20    pub fn into_inner(self) -> T {
21        self.0
22    }
23}
24
25#[cfg(test)]
26mod tests {
27    use super::*;
28    use serde_json;
29
30    #[test]
31    fn serialize_vec() {
32        let val = AsHex(vec![0xde, 0xad, 0xbe, 0xef]);
33        let json = serde_json::to_string(&val).unwrap();
34        assert_eq!(json, r#""deadbeef""#);
35    }
36
37    #[test]
38    fn deserialize_vec() {
39        let val: AsHex<Vec<u8>> = serde_json::from_str(r#""deadbeef""#).unwrap();
40        assert_eq!(val.0, vec![0xde, 0xad, 0xbe, 0xef]);
41    }
42
43    #[test]
44    fn roundtrip_vec() {
45        let original = vec![0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef];
46        let wrapped = AsHex(original.clone());
47        let json = serde_json::to_string(&wrapped).unwrap();
48        let recovered: AsHex<Vec<u8>> = serde_json::from_str(&json).unwrap();
49        assert_eq!(recovered.0, original);
50    }
51
52    #[test]
53    fn serialize_fixed_array() {
54        let val = AsHex([0xff, 0x00, 0xab]);
55        let json = serde_json::to_string(&val).unwrap();
56        assert_eq!(json, r#""ff00ab""#);
57    }
58
59    #[test]
60    fn deserialize_fixed_array() {
61        let val: AsHex<[u8; 3]> = serde_json::from_str(r#""ff00ab""#).unwrap();
62        assert_eq!(val.0, [0xff, 0x00, 0xab]);
63    }
64
65    #[test]
66    fn empty_bytes() {
67        let val = AsHex(Vec::<u8>::new());
68        let json = serde_json::to_string(&val).unwrap();
69        assert_eq!(json, r#""""#);
70
71        let recovered: AsHex<Vec<u8>> = serde_json::from_str(&json).unwrap();
72        assert!(recovered.0.is_empty());
73    }
74
75    #[test]
76    fn into_inner() {
77        let data = vec![1, 2, 3];
78        let wrapped = AsHex(data.clone());
79        assert_eq!(wrapped.into_inner(), data);
80    }
81
82    #[test]
83    fn from_impl() {
84        let data = vec![1, 2, 3];
85        let wrapped: AsHex<Vec<u8>> = data.clone().into();
86        assert_eq!(wrapped.0, data);
87    }
88
89    #[test]
90    fn deserialize_uppercase_input() {
91        let val: AsHex<Vec<u8>> = serde_json::from_str(r#""DEADBEEF""#).unwrap();
92        assert_eq!(val.0, vec![0xde, 0xad, 0xbe, 0xef]);
93    }
94
95    #[test]
96    fn deserialize_mixed_case_input() {
97        let val: AsHex<Vec<u8>> = serde_json::from_str(r#""DeAdBeEf""#).unwrap();
98        assert_eq!(val.0, vec![0xde, 0xad, 0xbe, 0xef]);
99    }
100
101    #[test]
102    fn deserialize_invalid_hex() {
103        let result = serde_json::from_str::<AsHex<Vec<u8>>>(r#""xyz""#);
104        assert!(result.is_err());
105    }
106
107    #[test]
108    fn deserialize_odd_length() {
109        let result = serde_json::from_str::<AsHex<Vec<u8>>>(r#""abc""#);
110        assert!(result.is_err());
111    }
112
113    #[test]
114    fn deserialize_wrong_length_for_fixed_array() {
115        let result = serde_json::from_str::<AsHex<[u8; 4]>>(r#""deadbeefaa""#);
116        assert!(result.is_err());
117    }
118}