rustywallet_address/encoding/
hex.rs

1//! Hex encoding utilities for Ethereum addresses.
2
3use crate::error::AddressError;
4
5/// Hex encoder/decoder.
6pub struct HexEncoder;
7
8impl HexEncoder {
9    /// Encode bytes to lowercase hex string.
10    pub fn encode(data: &[u8]) -> String {
11        data.iter().map(|b| format!("{:02x}", b)).collect()
12    }
13
14    /// Decode hex string to bytes.
15    pub fn decode(s: &str) -> Result<Vec<u8>, AddressError> {
16        let s = s.strip_prefix("0x").unwrap_or(s);
17        
18        if !s.len().is_multiple_of(2) {
19            return Err(AddressError::InvalidFormat("Odd hex string length".to_string()));
20        }
21
22        (0..s.len())
23            .step_by(2)
24            .map(|i| {
25                u8::from_str_radix(&s[i..i + 2], 16)
26                    .map_err(|_| AddressError::InvalidFormat("Invalid hex character".to_string()))
27            })
28            .collect()
29    }
30}
31
32#[cfg(test)]
33mod tests {
34    use super::*;
35
36    #[test]
37    fn test_hex_roundtrip() {
38        let data = [0xde, 0xad, 0xbe, 0xef];
39        let encoded = HexEncoder::encode(&data);
40        assert_eq!(encoded, "deadbeef");
41        
42        let decoded = HexEncoder::decode(&encoded).unwrap();
43        assert_eq!(decoded, data);
44    }
45
46    #[test]
47    fn test_hex_with_prefix() {
48        let decoded = HexEncoder::decode("0xdeadbeef").unwrap();
49        assert_eq!(decoded, [0xde, 0xad, 0xbe, 0xef]);
50    }
51}