1use crate::{util::Itertools, ContractError, GenericResult};
20
21#[inline]
23pub fn hex_from_iter<I: Iterator<Item = u8>>(iter: I) -> String {
24 let mut repr = String::new();
25 for b in iter {
26 repr += &format!("{:02x}", b);
27 }
28 repr
29}
30
31pub fn decode_hex(hex: &str) -> HexDecodeIter {
33 HexDecodeIter { hex, curr: 0 }
34}
35
36pub struct HexDecodeIter<'a> {
37 hex: &'a str,
38 curr: usize,
39}
40
41impl Iterator for HexDecodeIter<'_> {
42 type Item = GenericResult<u8>;
43
44 fn next(&mut self) -> Option<Self::Item> {
47 if self.curr == self.hex.len() {
49 return None
50 }
51
52 if self.curr + 2 > self.hex.len() {
54 return Some(Err(ContractError::HexFmtErr))
55 }
56
57 let Ok(byte) = u8::from_str_radix(&self.hex[self.curr..self.curr + 2], 16) else {
59 return Some(Err(ContractError::HexFmtErr))
60 };
61
62 self.curr += 2;
63
64 Some(Ok(byte))
65 }
66}
67
68pub fn decode_hex_arr<const N: usize>(hex: &str) -> GenericResult<[u8; N]> {
69 let decoded: Vec<_> = decode_hex(hex).try_collect()?;
70 let bytes: [u8; N] = decoded.try_into().map_err(|_| ContractError::HexFmtErr)?;
71 Ok(bytes)
72}
73
74pub trait AsHex {
75 fn hex(&self) -> String;
76}
77
78impl<T: AsRef<[u8]>> AsHex for T {
79 fn hex(&self) -> String {
81 hex_from_iter(self.as_ref().iter().cloned())
82 }
83}
84
85#[cfg(test)]
86mod tests {
87 use super::*;
88
89 #[test]
90 fn test_hex_encode_decode() {
91 let decoded = decode_hex("0a00").collect::<GenericResult<Vec<_>>>().unwrap();
92 assert_eq!(decoded, vec![10, 0]);
93 assert!(decode_hex("0x").collect::<GenericResult<Vec<_>>>().is_err());
94 assert!(decode_hex("0a1").collect::<GenericResult<Vec<_>>>().is_err());
95 assert_eq!(hex_from_iter([10u8, 0].into_iter()), "0a00");
96 }
97}