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 fn from_array(array: [u8; N]) -> Self;
20 fn to_array(&self) -> [u8; N];
21 fn as_array(&self) -> &[u8; N];
22
23 fn as_slice(&self) -> &[u8] {
26 self.as_array().as_slice()
27 }
28 fn to_vec(&self) -> Vec<u8> {
29 self.as_slice().to_vec()
30 }
31 fn try_from_slice(slice: &[u8]) -> Result<Self, TryFromSliceError> {
32 <[u8; N]>::try_from(slice).map(Self::from_array)
33 }
34 fn try_from_vec(vec: Vec<u8>) -> Result<Self, TryFromSliceError> {
35 Self::try_from_slice(&vec)
36 }
37
38 fn from_hex(s: &str) -> Result<Self, hex::DecodeError> {
41 <[u8; N]>::from_hex(s).map(Self::from_array)
42 }
43 fn to_hex(&self) -> String {
44 hex::encode(self.as_slice())
45 }
46 fn as_hex_display(&self) -> HexDisplay<'_> {
47 hex::display(self.as_slice())
48 }
49 fn fmt_as_hex(&self, f: &mut fmt::Formatter) -> fmt::Result {
50 Display::fmt(&self.as_hex_display(), f)
51 }
52}
53
54#[macro_export]
60macro_rules! impl_byte_array {
61 ($type:ty, $n:expr) => {
62 impl ByteArray<$n> for $type {
63 fn from_array(array: [u8; $n]) -> Self {
64 Self(array)
65 }
66 fn to_array(&self) -> [u8; $n] {
67 self.0
68 }
69 fn as_array(&self) -> &[u8; $n] {
70 &self.0
71 }
72 }
73
74 impl AsRef<[u8]> for $type {
75 fn as_ref(&self) -> &[u8] {
76 self.0.as_slice()
77 }
78 }
79
80 impl AsRef<[u8; $n]> for $type {
81 fn as_ref(&self) -> &[u8; $n] {
82 &self.0
83 }
84 }
85 };
86}
87
88#[macro_export]
94macro_rules! impl_fromstr_fromhex {
95 ($type:ty, $n:expr) => {
96 impl std::str::FromStr for $type {
97 type Err = $crate::lexe_hex::hex::DecodeError;
98 fn from_str(s: &str) -> Result<Self, Self::Err> {
99 <Self as ByteArray<$n>>::from_hex(s)
100 }
101 }
102 impl $crate::lexe_hex::hex::FromHex for $type {
103 fn from_hex(
104 s: &str,
105 ) -> Result<Self, $crate::lexe_hex::hex::DecodeError> {
106 <[u8; $n]>::from_hex(s).map(Self::from_array)
107 }
108 }
109 };
110}
111
112#[macro_export]
118macro_rules! impl_debug_display_as_hex {
119 ($type:ty) => {
120 impl std::fmt::Debug for $type {
121 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
122 write!(
126 f,
127 "{}(\"{}\")",
128 stringify!($type),
129 self.as_hex_display()
130 )
131 }
132 }
133 impl std::fmt::Display for $type {
134 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
135 Self::fmt_as_hex(self, f)
136 }
137 }
138 };
139}
140
141#[macro_export]
149macro_rules! impl_debug_display_redacted {
150 ($type:ty) => {
151 impl std::fmt::Debug for $type {
152 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
153 f.write_str(concat!(stringify!($type), "(..)"))
154 }
155 }
156 impl std::fmt::Display for $type {
157 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
158 f.write_str("..")
159 }
160 }
161 };
162}
163
164#[cfg(test)]
165mod test {
166 use std::str::FromStr;
167
168 use super::*;
169
170 #[derive(Copy, Clone, Eq, PartialEq, Hash)]
171 struct MyStruct([u8; 4]);
172
173 impl_byte_array!(MyStruct, 4);
174 impl_fromstr_fromhex!(MyStruct, 4);
175 impl_debug_display_as_hex!(MyStruct);
176
177 #[derive(Copy, Clone, Eq, PartialEq, Hash)]
178 struct MySecret([u8; 4]);
179
180 impl_byte_array!(MySecret, 4);
181 impl_fromstr_fromhex!(MySecret, 4);
182 impl_debug_display_redacted!(MySecret);
183
184 #[test]
185 fn test_display_and_debug() {
186 let data = [0xde, 0xad, 0xbe, 0xef];
187
188 let my_struct = MyStruct(data);
190 assert_eq!(my_struct.to_string(), "deadbeef");
191 assert_eq!(format!("{my_struct}"), "deadbeef");
192 assert_eq!(format!("{my_struct:#}"), "deadbeef");
193 assert_eq!(format!("{:?}", my_struct), r#"MyStruct("deadbeef")"#);
194 assert_eq!(format!("{:#?}", my_struct), r#"MyStruct("deadbeef")"#);
195 assert_eq!(my_struct.as_hex_display().to_string(), "deadbeef");
196 assert_eq!(my_struct.to_hex(), "deadbeef");
197
198 let my_secret = MySecret(data);
200 assert_eq!(my_secret.to_string(), "..");
201 assert_eq!(format!("My secret is {my_secret}"), "My secret is ..");
202 assert_eq!(format!("My secret is {my_secret:#}"), "My secret is ..");
203 assert_eq!(format!("{:?}", my_secret), r#"MySecret(..)"#);
204 assert_eq!(format!("{:#?}", my_secret), r#"MySecret(..)"#);
205 }
206
207 #[test]
208 fn basic_parse() {
209 let my_struct = MyStruct::from_str("deadbeef").unwrap();
211 assert_eq!(my_struct.0, [0xde, 0xad, 0xbe, 0xef]);
212
213 let my_secret = MySecret::from_str("deadbeef").unwrap();
214 assert_eq!(my_secret.0, [0xde, 0xad, 0xbe, 0xef]);
215
216 MyStruct::from_str("invalid").unwrap_err();
218 MyStruct::from_str("deadbee").unwrap_err(); MyStruct::from_str("deadbeefff").unwrap_err(); MyStruct::from_str("wxyz").unwrap_err(); }
222}