defuse_serde_utils/
hex.rs

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