hex_conservative/
parse.rs

1// SPDX-License-Identifier: CC0-1.0
2
3//! Hex encoding and decoding.
4
5use core::{fmt, str};
6
7use arrayvec::ArrayVec;
8
9#[cfg(all(feature = "alloc", not(feature = "std")))]
10use crate::alloc::vec::Vec;
11use crate::error::InvalidLengthError;
12use crate::iter::HexToBytesIter;
13
14#[rustfmt::skip]                // Keep public re-exports separate.
15pub use crate::error::{HexToBytesError, HexToArrayError};
16
17/// Trait for objects that can be deserialized from hex strings.
18pub trait FromHex: Sized {
19    /// Error type returned while parsing hex string.
20    type Error: Sized + fmt::Debug + fmt::Display;
21
22    /// Produces an object from a hex string.
23    fn from_hex(s: &str) -> Result<Self, Self::Error>;
24}
25
26#[cfg(any(test, feature = "std", feature = "alloc"))]
27impl FromHex for Vec<u8> {
28    type Error = HexToBytesError;
29
30    fn from_hex(s: &str) -> Result<Self, Self::Error> {
31        HexToBytesIter::new(s)?.map(|result| result.map_err(Into::into)).collect()
32    }
33}
34
35impl<const LEN: usize> FromHex for [u8; LEN] {
36    type Error = HexToArrayError;
37
38    fn from_hex(s: &str) -> Result<Self, Self::Error> {
39        if s.len() == LEN * 2 {
40            let mut ret = ArrayVec::<u8, LEN>::new();
41            // checked above
42            for byte in HexToBytesIter::new_unchecked(s) {
43                ret.push(byte?);
44            }
45            Ok(ret.into_inner().expect("inner is full"))
46        } else {
47            Err(InvalidLengthError { invalid: s.len(), expected: 2 * LEN }.into())
48        }
49    }
50}
51
52#[cfg(test)]
53mod tests {
54    use super::*;
55    use crate::display::DisplayHex;
56
57    #[test]
58    #[cfg(feature = "alloc")]
59    fn hex_error() {
60        use crate::error::{InvalidCharError, OddLengthStringError};
61
62        let oddlen = "0123456789abcdef0";
63        let badchar1 = "Z123456789abcdef";
64        let badchar2 = "012Y456789abcdeb";
65        let badchar3 = "«23456789abcdef";
66
67        assert_eq!(Vec::<u8>::from_hex(oddlen), Err(OddLengthStringError { len: 17 }.into()));
68        assert_eq!(
69            <[u8; 4]>::from_hex(oddlen),
70            Err(InvalidLengthError { invalid: 17, expected: 8 }.into())
71        );
72        assert_eq!(Vec::<u8>::from_hex(badchar1), Err(InvalidCharError { invalid: b'Z' }.into()));
73        assert_eq!(Vec::<u8>::from_hex(badchar2), Err(InvalidCharError { invalid: b'Y' }.into()));
74        assert_eq!(Vec::<u8>::from_hex(badchar3), Err(InvalidCharError { invalid: 194 }.into()));
75    }
76
77    #[test]
78    fn hex_to_array() {
79        let len_sixteen = "0123456789abcdef";
80        assert!(<[u8; 8]>::from_hex(len_sixteen).is_ok());
81    }
82
83    #[test]
84    fn hex_to_array_error() {
85        let len_sixteen = "0123456789abcdef";
86        assert_eq!(
87            <[u8; 4]>::from_hex(len_sixteen),
88            Err(InvalidLengthError { invalid: 16, expected: 8 }.into())
89        )
90    }
91
92    #[test]
93    fn mixed_case() {
94        let s = "DEADbeef0123";
95        let want_lower = "deadbeef0123";
96        let want_upper = "DEADBEEF0123";
97
98        let v = Vec::<u8>::from_hex(s).expect("valid hex");
99        assert_eq!(format!("{:x}", v.as_hex()), want_lower);
100        assert_eq!(format!("{:X}", v.as_hex()), want_upper);
101    }
102}