1use std::fmt;
4
5pub fn encode(bytes: &[u8]) -> String {
16 bytes.iter().map(|b| format!("{:02x}", b)).collect()
17}
18
19#[cfg(any(test, feature = "testing"))]
34pub fn decode(s: &str) -> Result<Vec<u8>, DecodeError> {
35 if !s.len().is_multiple_of(2) {
36 return Err(DecodeError::OddLength);
37 }
38 (0..s.len())
39 .step_by(2)
40 .map(|i| u8::from_str_radix(&s[i..i + 2], 16).map_err(|_| DecodeError::InvalidChar))
41 .collect()
42}
43
44#[cfg(any(test, feature = "testing"))]
46#[derive(Debug, Clone, PartialEq, Eq)]
47pub enum DecodeError {
48 OddLength,
50 InvalidChar,
52}
53
54pub struct Bytes<'a>(pub &'a [u8]);
68
69impl fmt::Debug for Bytes<'_> {
70 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71 for b in self.0 {
72 write!(f, "{:02x}", b)?;
73 }
74 Ok(())
75 }
76}
77
78impl fmt::Display for Bytes<'_> {
79 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80 fmt::Debug::fmt(self, f)
81 }
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87
88 #[test]
89 fn test_bytes_display() {
90 let data = [0xde, 0xad, 0xbe, 0xef];
91 let hex = Bytes(&data);
92 assert_eq!(format!("{}", hex), "deadbeef");
93 }
94
95 #[test]
96 fn test_bytes_debug() {
97 let data = [0x00, 0xff, 0x42];
98 let hex = Bytes(&data);
99 assert_eq!(format!("{:?}", hex), "00ff42");
100 }
101
102 #[test]
103 fn test_bytes_empty() {
104 let data: [u8; 0] = [];
105 let hex = Bytes(&data);
106 assert_eq!(format!("{}", hex), "");
107 }
108
109 #[test]
110 fn test_encode_basic() {
111 assert_eq!(encode(b"Hello world!"), "48656c6c6f20776f726c6421");
112 assert_eq!(encode(&[0x01, 0x02, 0x03, 0x0f, 0x10]), "0102030f10");
113 }
114
115 #[test]
116 fn test_encode_empty() {
117 assert_eq!(encode(&[]), "");
118 }
119
120 #[test]
121 fn test_encode_all_bytes() {
122 assert_eq!(encode(&[0x00]), "00");
123 assert_eq!(encode(&[0xff]), "ff");
124 assert_eq!(encode(&[0x00, 0xff]), "00ff");
125 }
126
127 #[test]
128 fn test_decode_basic() {
129 assert_eq!(decode("48656c6c6f20776f726c6421").unwrap(), b"Hello world!");
130 assert_eq!(
131 decode("0102030f10").unwrap(),
132 vec![0x01, 0x02, 0x03, 0x0f, 0x10]
133 );
134 }
135
136 #[test]
137 fn test_decode_empty() {
138 assert_eq!(decode("").unwrap(), Vec::<u8>::new());
139 }
140
141 #[test]
142 fn test_decode_mixed_case() {
143 assert_eq!(decode("deadbeef").unwrap(), vec![0xde, 0xad, 0xbe, 0xef]);
144 assert_eq!(decode("DEADBEEF").unwrap(), vec![0xde, 0xad, 0xbe, 0xef]);
145 assert_eq!(decode("DeAdBeEf").unwrap(), vec![0xde, 0xad, 0xbe, 0xef]);
146 }
147
148 #[test]
149 fn test_decode_odd_length_error() {
150 assert_eq!(decode("1"), Err(DecodeError::OddLength));
151 assert_eq!(decode("123"), Err(DecodeError::OddLength));
152 assert_eq!(decode("12345"), Err(DecodeError::OddLength));
153 }
154
155 #[test]
156 fn test_decode_invalid_char_error() {
157 assert_eq!(decode("gg"), Err(DecodeError::InvalidChar));
158 assert_eq!(decode("0g"), Err(DecodeError::InvalidChar));
159 assert_eq!(decode("g0"), Err(DecodeError::InvalidChar));
160 assert_eq!(decode("xx"), Err(DecodeError::InvalidChar));
161 assert_eq!(decode(" "), Err(DecodeError::InvalidChar));
162 }
163
164 #[test]
165 fn test_roundtrip() {
166 let original = vec![
167 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd,
168 0xee, 0xff,
169 ];
170 let encoded = encode(&original);
171 let decoded = decode(&encoded).unwrap();
172 assert_eq!(original, decoded);
173 }
174}