hex_conservative/
parse.rs1use core::{fmt, str};
6
7#[cfg(all(feature = "alloc", not(feature = "std")))]
8use crate::alloc::vec::Vec;
9use crate::error::InvalidLengthError;
10use crate::iter::HexToBytesIter;
11
12#[rustfmt::skip] pub use crate::error::{HexToBytesError, HexToArrayError};
14
15pub trait FromHex: Sized {
17 type Error: Sized + fmt::Debug + fmt::Display;
19
20 fn from_hex(s: &str) -> Result<Self, Self::Error>;
22}
23
24#[cfg(any(test, feature = "std", feature = "alloc"))]
25impl FromHex for Vec<u8> {
26 type Error = HexToBytesError;
27
28 fn from_hex(s: &str) -> Result<Self, Self::Error> {
29 Ok(HexToBytesIter::new(s)?.drain_to_vec()?)
30 }
31}
32
33impl<const LEN: usize> FromHex for [u8; LEN] {
34 type Error = HexToArrayError;
35
36 fn from_hex(s: &str) -> Result<Self, Self::Error> {
37 if s.len() == LEN * 2 {
38 let mut ret = [0u8; LEN];
39 HexToBytesIter::new_unchecked(s).drain_to_slice(&mut ret)?;
41 Ok(ret)
42 } else {
43 Err(InvalidLengthError { invalid: s.len(), expected: 2 * LEN }.into())
44 }
45 }
46}
47
48#[cfg(test)]
49mod tests {
50 use super::*;
51 use crate::display::DisplayHex;
52
53 #[test]
54 #[cfg(feature = "alloc")]
55 fn hex_error() {
56 use crate::error::{InvalidCharError, OddLengthStringError};
57
58 let oddlen = "0123456789abcdef0";
59 let badchar1 = "Z123456789abcdef";
60 let badchar2 = "012Y456789abcdeb";
61 let badchar3 = "«23456789abcdef";
62
63 assert_eq!(Vec::<u8>::from_hex(oddlen), Err(OddLengthStringError { len: 17 }.into()));
64 assert_eq!(
65 <[u8; 4]>::from_hex(oddlen),
66 Err(InvalidLengthError { invalid: 17, expected: 8 }.into())
67 );
68 assert_eq!(
69 Vec::<u8>::from_hex(badchar1),
70 Err(InvalidCharError { pos: 0, invalid: b'Z' }.into())
71 );
72 assert_eq!(
73 Vec::<u8>::from_hex(badchar2),
74 Err(InvalidCharError { pos: 3, invalid: b'Y' }.into())
75 );
76 assert_eq!(
77 Vec::<u8>::from_hex(badchar3),
78 Err(InvalidCharError { pos: 0, invalid: 194 }.into())
79 );
80 }
81
82 #[test]
83 fn hex_error_position() {
84 use crate::error::InvalidCharError;
85 let badpos1 = "Z123456789abcdef";
86 let badpos2 = "012Y456789abcdeb";
87 let badpos3 = "0123456789abcdeZ";
88 let badpos4 = "0123456789abYdef";
89
90 assert_eq!(
91 HexToBytesIter::new(badpos1).unwrap().next().unwrap(),
92 Err(InvalidCharError { pos: 0, invalid: b'Z' })
93 );
94 assert_eq!(
95 HexToBytesIter::new(badpos2).unwrap().nth(1).unwrap(),
96 Err(InvalidCharError { pos: 3, invalid: b'Y' })
97 );
98 assert_eq!(
99 HexToBytesIter::new(badpos3).unwrap().next_back().unwrap(),
100 Err(InvalidCharError { pos: 15, invalid: b'Z' })
101 );
102 assert_eq!(
103 HexToBytesIter::new(badpos4).unwrap().nth_back(1).unwrap(),
104 Err(InvalidCharError { pos: 12, invalid: b'Y' })
105 );
106 }
107
108 #[test]
109 fn hex_to_array() {
110 let len_sixteen = "0123456789abcdef";
111 assert!(<[u8; 8]>::from_hex(len_sixteen).is_ok());
112 }
113
114 #[test]
115 fn hex_to_array_error() {
116 let len_sixteen = "0123456789abcdef";
117 assert_eq!(
118 <[u8; 4]>::from_hex(len_sixteen),
119 Err(InvalidLengthError { invalid: 16, expected: 8 }.into())
120 )
121 }
122
123 #[test]
124 fn mixed_case() {
125 let s = "DEADbeef0123";
126 let want_lower = "deadbeef0123";
127 let want_upper = "DEADBEEF0123";
128
129 let v = Vec::<u8>::from_hex(s).expect("valid hex");
130 assert_eq!(format!("{:x}", v.as_hex()), want_lower);
131 assert_eq!(format!("{:X}", v.as_hex()), want_upper);
132 }
133}