hex_conservative/
parse.rs

1// SPDX-License-Identifier: CC0-1.0
2
3//! Hex encoding and decoding.
4
5use 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]                // Keep public re-exports separate.
13pub use crate::error::{HexToBytesError, HexToArrayError};
14
15/// Trait for objects that can be deserialized from hex strings.
16pub trait FromHex: Sized {
17    /// Error type returned while parsing hex string.
18    type Error: Sized + fmt::Debug + fmt::Display;
19
20    /// Produces an object from a hex string.
21    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            // checked above
40            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}