1use std::{
2 array::TryFromSliceError,
3 fmt::{self, Debug, Display},
4 hash::Hash,
5};
6
7#[doc(hidden)]
11pub use lexe_hex;
12use lexe_hex::hex::{self, FromHex, HexDisplay};
13
14pub trait ByteArray<const N: usize>: Copy + Debug + Eq + Hash + Sized {
17 const LEN: usize = N;
19
20 fn from_array(array: [u8; N]) -> Self;
23 fn to_array(&self) -> [u8; N];
24 fn as_array(&self) -> &[u8; N];
25
26 fn as_slice(&self) -> &[u8] {
29 self.as_array().as_slice()
30 }
31 fn to_vec(&self) -> Vec<u8> {
32 self.as_slice().to_vec()
33 }
34 fn try_from_slice(slice: &[u8]) -> Result<Self, TryFromSliceError> {
35 <[u8; N]>::try_from(slice).map(Self::from_array)
36 }
37 fn try_from_vec(vec: Vec<u8>) -> Result<Self, TryFromSliceError> {
38 Self::try_from_slice(&vec)
39 }
40
41 fn from_hex(s: &str) -> Result<Self, hex::DecodeError> {
44 <[u8; N]>::from_hex(s).map(Self::from_array)
45 }
46 fn to_hex(&self) -> String {
47 hex::encode(self.as_slice())
48 }
49 fn as_hex_display(&self) -> HexDisplay<'_> {
50 hex::display(self.as_slice())
51 }
52 fn fmt_as_hex(&self, f: &mut fmt::Formatter) -> fmt::Result {
53 Display::fmt(&self.as_hex_display(), f)
54 }
55}
56
57#[macro_export]
63macro_rules! impl_byte_array {
64 ($type:ty, $n:expr) => {
65 impl $crate::ByteArray<$n> for $type {
66 fn from_array(array: [u8; $n]) -> Self {
67 Self(array)
68 }
69 fn to_array(&self) -> [u8; $n] {
70 self.0
71 }
72 fn as_array(&self) -> &[u8; $n] {
73 &self.0
74 }
75 }
76
77 impl std::convert::AsRef<[u8]> for $type {
78 fn as_ref(&self) -> &[u8] {
79 self.0.as_slice()
80 }
81 }
82
83 impl std::convert::AsRef<[u8; $n]> for $type {
84 fn as_ref(&self) -> &[u8; $n] {
85 &self.0
86 }
87 }
88 };
89}
90
91#[macro_export]
97macro_rules! impl_fromstr_fromhex {
98 ($type:ty, $n:expr) => {
99 impl std::str::FromStr for $type {
100 type Err = $crate::lexe_hex::hex::DecodeError;
101 fn from_str(s: &str) -> Result<Self, Self::Err> {
102 $crate::ByteArray::<$n>::from_hex(s)
103 }
104 }
105 impl $crate::lexe_hex::hex::FromHex for $type {
106 fn from_hex(
107 s: &str,
108 ) -> Result<Self, $crate::lexe_hex::hex::DecodeError> {
109 <[u8; $n]>::from_hex(s).map(Self::from_array)
110 }
111 }
112 };
113}
114
115#[macro_export]
121macro_rules! impl_debug_display_as_hex {
122 ($type:ty) => {
123 impl std::fmt::Debug for $type {
124 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
125 write!(
129 f,
130 "{}(\"{}\")",
131 stringify!($type),
132 $crate::ByteArray::as_hex_display(self)
133 )
134 }
135 }
136 impl std::fmt::Display for $type {
137 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
138 $crate::ByteArray::fmt_as_hex(self, f)
139 }
140 }
141 };
142}
143
144#[macro_export]
152macro_rules! impl_debug_display_redacted {
153 ($type:ty) => {
154 impl std::fmt::Debug for $type {
155 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
156 f.write_str(concat!(stringify!($type), "(..)"))
157 }
158 }
159 impl std::fmt::Display for $type {
160 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
161 f.write_str("..")
162 }
163 }
164 };
165}
166
167#[cfg(test)]
168mod test {
169 use std::str::FromStr;
170
171 use super::*;
172
173 #[derive(Copy, Clone, Eq, PartialEq, Hash)]
174 struct MyStruct([u8; 4]);
175
176 impl_byte_array!(MyStruct, 4);
177 impl_fromstr_fromhex!(MyStruct, 4);
178 impl_debug_display_as_hex!(MyStruct);
179
180 #[derive(Copy, Clone, Eq, PartialEq, Hash)]
181 struct MySecret([u8; 4]);
182
183 impl_byte_array!(MySecret, 4);
184 impl_fromstr_fromhex!(MySecret, 4);
185 impl_debug_display_redacted!(MySecret);
186
187 #[test]
188 fn test_display_and_debug() {
189 let data = [0xde, 0xad, 0xbe, 0xef];
190
191 let my_struct = MyStruct(data);
193 assert_eq!(my_struct.to_string(), "deadbeef");
194 assert_eq!(format!("{my_struct}"), "deadbeef");
195 assert_eq!(format!("{my_struct:#}"), "deadbeef");
196 assert_eq!(format!("{:?}", my_struct), r#"MyStruct("deadbeef")"#);
197 assert_eq!(format!("{:#?}", my_struct), r#"MyStruct("deadbeef")"#);
198 assert_eq!(my_struct.as_hex_display().to_string(), "deadbeef");
199 assert_eq!(my_struct.to_hex(), "deadbeef");
200
201 let my_secret = MySecret(data);
203 assert_eq!(my_secret.to_string(), "..");
204 assert_eq!(format!("My secret is {my_secret}"), "My secret is ..");
205 assert_eq!(format!("My secret is {my_secret:#}"), "My secret is ..");
206 assert_eq!(format!("{:?}", my_secret), r#"MySecret(..)"#);
207 assert_eq!(format!("{:#?}", my_secret), r#"MySecret(..)"#);
208 }
209
210 #[test]
211 fn basic_parse() {
212 let my_struct = MyStruct::from_str("deadbeef").unwrap();
214 assert_eq!(my_struct.0, [0xde, 0xad, 0xbe, 0xef]);
215
216 let my_secret = MySecret::from_str("deadbeef").unwrap();
217 assert_eq!(my_secret.0, [0xde, 0xad, 0xbe, 0xef]);
218
219 MyStruct::from_str("invalid").unwrap_err();
221 MyStruct::from_str("deadbee").unwrap_err(); MyStruct::from_str("deadbeefff").unwrap_err(); MyStruct::from_str("wxyz").unwrap_err(); }
225}