defuse_serde_utils/
hex.rs1use 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>>"))]
14pub 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}