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