hex_conservative/
parse.rs1use core::{fmt, str};
6
7#[cfg(all(feature = "alloc", not(feature = "std")))]
8use crate::alloc::vec::Vec;
9use crate::iter::HexToBytesIter;
10
11pub trait FromHex: Sized {
13 type Err: From<HexToBytesError> + Sized + fmt::Debug + fmt::Display;
15
16 fn from_byte_iter<I>(iter: I) -> Result<Self, Self::Err>
18 where
19 I: Iterator<Item = Result<u8, HexToBytesError>> + ExactSizeIterator + DoubleEndedIterator;
20
21 fn from_hex(s: &str) -> Result<Self, Self::Err> {
23 Self::from_byte_iter(HexToBytesIter::new(s)?)
24 }
25}
26
27#[cfg(any(test, feature = "std", feature = "alloc"))]
28impl FromHex for Vec<u8> {
29 type Err = HexToBytesError;
30
31 fn from_byte_iter<I>(iter: I) -> Result<Self, Self::Err>
32 where
33 I: Iterator<Item = Result<u8, HexToBytesError>> + ExactSizeIterator + DoubleEndedIterator,
34 {
35 iter.collect()
36 }
37}
38
39#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
41pub enum HexToBytesError {
42 InvalidChar(u8),
44 OddLengthString(usize),
46}
47
48impl fmt::Display for HexToBytesError {
49 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
50 use self::HexToBytesError::*;
51
52 match *self {
53 InvalidChar(ch) => write!(f, "invalid hex character {}", ch),
54 OddLengthString(ell) => write!(f, "odd hex string length {}", ell),
55 }
56 }
57}
58
59#[cfg(feature = "std")]
60impl std::error::Error for HexToBytesError {
61 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
62 use self::HexToBytesError::*;
63
64 match self {
65 InvalidChar(_) | OddLengthString(_) => None,
66 }
67 }
68}
69
70macro_rules! impl_fromhex_array {
71 ($len:expr) => {
72 impl FromHex for [u8; $len] {
73 type Err = HexToArrayError;
74
75 fn from_byte_iter<I>(iter: I) -> Result<Self, Self::Err>
76 where
77 I: Iterator<Item = Result<u8, HexToBytesError>>
78 + ExactSizeIterator
79 + DoubleEndedIterator,
80 {
81 if iter.len() == $len {
82 let mut ret = [0; $len];
83 for (n, byte) in iter.enumerate() {
84 ret[n] = byte?;
85 }
86 Ok(ret)
87 } else {
88 let got = 2 * iter.len();
89 let want = 2 * $len;
90 Err(HexToArrayError::InvalidLength(got, want))
91 }
92 }
93 }
94 };
95}
96
97impl_fromhex_array!(2);
98impl_fromhex_array!(4);
99impl_fromhex_array!(6);
100impl_fromhex_array!(8);
101impl_fromhex_array!(10);
102impl_fromhex_array!(12);
103impl_fromhex_array!(14);
104impl_fromhex_array!(16);
105impl_fromhex_array!(20);
106impl_fromhex_array!(24);
107impl_fromhex_array!(28);
108impl_fromhex_array!(32);
109impl_fromhex_array!(33);
110impl_fromhex_array!(64);
111impl_fromhex_array!(65);
112impl_fromhex_array!(128);
113impl_fromhex_array!(256);
114impl_fromhex_array!(384);
115impl_fromhex_array!(512);
116
117#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
119pub enum HexToArrayError {
120 Conversion(HexToBytesError),
122 InvalidLength(usize, usize),
124}
125
126impl fmt::Display for HexToArrayError {
127 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
128 use HexToArrayError::*;
129
130 match *self {
131 Conversion(ref e) => crate::write_err!(f, "conversion error"; e),
132 InvalidLength(got, want) =>
133 write!(f, "bad hex string length {} (expected {})", got, want),
134 }
135 }
136}
137
138#[cfg(feature = "std")]
139impl std::error::Error for HexToArrayError {
140 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
141 use HexToArrayError::*;
142
143 match *self {
144 Conversion(ref e) => Some(e),
145 InvalidLength(_, _) => None,
146 }
147 }
148}
149
150impl From<HexToBytesError> for HexToArrayError {
151 fn from(e: HexToBytesError) -> Self { Self::Conversion(e) }
152}
153
154#[cfg(test)]
155mod tests {
156 use super::*;
157 use crate::display::DisplayHex;
158
159 #[test]
160 #[cfg(feature = "alloc")]
161 fn hex_error() {
162 use HexToBytesError::*;
163
164 let oddlen = "0123456789abcdef0";
165 let badchar1 = "Z123456789abcdef";
166 let badchar2 = "012Y456789abcdeb";
167 let badchar3 = "«23456789abcdef";
168
169 assert_eq!(Vec::<u8>::from_hex(oddlen), Err(OddLengthString(17)));
170 assert_eq!(
171 <[u8; 4]>::from_hex(oddlen),
172 Err(HexToArrayError::Conversion(OddLengthString(17)))
173 );
174 assert_eq!(Vec::<u8>::from_hex(badchar1), Err(InvalidChar(b'Z')));
175 assert_eq!(Vec::<u8>::from_hex(badchar2), Err(InvalidChar(b'Y')));
176 assert_eq!(Vec::<u8>::from_hex(badchar3), Err(InvalidChar(194)));
177 }
178
179 #[test]
180 fn hex_to_array() {
181 let len_sixteen = "0123456789abcdef";
182 assert!(<[u8; 8]>::from_hex(len_sixteen).is_ok());
183 }
184 #[test]
185 fn hex_to_array_error() {
186 use HexToArrayError::*;
187 let len_sixteen = "0123456789abcdef";
188 let result = <[u8; 4]>::from_hex(len_sixteen);
189 assert_eq!(result, Err(InvalidLength(16, 8)));
190 assert_eq!(&result.unwrap_err().to_string(), "bad hex string length 16 (expected 8)");
191 }
192
193 #[test]
194 fn mixed_case() {
195 let s = "DEADbeef0123";
196 let want_lower = "deadbeef0123";
197 let want_upper = "DEADBEEF0123";
198
199 let v = Vec::<u8>::from_hex(s).expect("valid hex");
200 assert_eq!(format!("{:x}", v.as_hex()), want_lower);
201 assert_eq!(format!("{:X}", v.as_hex()), want_upper);
202 }
203}