Skip to main content

hex_display/
lib.rs

1#![no_std]
2#![doc = include_str!("../README.md")]
3
4use core::fmt::{Debug, Display, LowerHex, UpperHex};
5
6#[cfg(any(feature = "std", test))]
7extern crate std;
8
9#[cfg(feature = "alloc")]
10extern crate alloc;
11
12/// An extension trait that allows for more easily constructing [`Hex`] values
13///
14/// ```
15/// use hex_display::HexDisplayExt;
16/// assert_eq!(
17///     format!("{}", [0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef].hex()),
18///     "0123456789abcdef"
19/// );
20/// assert_eq!(
21///     format!("{:X}", [0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef].hex()),
22///     "0123456789ABCDEF"
23/// );
24/// ```
25pub trait HexDisplayExt {
26    /// Display as a hexdump
27    fn hex(&self) -> Hex<'_>;
28
29    /// Convert to a upper-case hex string
30    ///
31    /// Only present when built with `alloc` support.
32    #[cfg(feature = "alloc")]
33    fn upper_hex_string(&self) -> alloc::string::String {
34        alloc::format!("{:X}", self.hex())
35    }
36
37    /// Convert to a lower-case hex string
38    ///
39    /// Only present when built with `alloc` support.
40    #[cfg(feature = "alloc")]
41    fn hex_string(&self) -> alloc::string::String {
42        use alloc::string::ToString;
43        self.hex().to_string()
44    }
45}
46
47impl HexDisplayExt for [u8] {
48    fn hex(&self) -> Hex<'_> {
49        Hex(self)
50    }
51}
52
53impl<const N: usize> HexDisplayExt for [u8; N] {
54    fn hex(&self) -> Hex<'_> {
55        Hex(self)
56    }
57}
58
59/// A wrapper type for `&[u8]` which implements Display by providing a hexdump
60///
61/// See [`HexDisplayExt`] for an easier method of constructing this type.
62///
63/// By default, it outputs a lower-case hexdump, but it outputs upper-case if provided with `{:X}`
64/// formatting option.
65///
66/// ```
67/// use hex_display::Hex;
68///
69/// assert_eq!(
70///     format!("{}", Hex(&[0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef])),
71///     "0123456789abcdef"
72/// );
73/// assert_eq!(
74///     format!("{:?}", Hex(&[0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef])),
75///     "0123456789abcdef"
76/// );
77/// assert_eq!(
78///     format!("{:X}", Hex(&[0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef])),
79///     "0123456789ABCDEF"
80/// );
81/// ```
82pub struct Hex<'a>(
83    /// The bytes to be converted into a hexdump
84    pub &'a [u8],
85);
86
87impl UpperHex for Hex<'_> {
88    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
89        for byte in self.0 {
90            f.write_fmt(format_args!("{:02X}", byte))?;
91        }
92        Ok(())
93    }
94}
95impl LowerHex for Hex<'_> {
96    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
97        for byte in self.0 {
98            f.write_fmt(format_args!("{:02x}", byte))?;
99        }
100        Ok(())
101    }
102}
103impl Debug for Hex<'_> {
104    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
105        for byte in self.0 {
106            f.write_fmt(format_args!("{:02x}", byte))?;
107        }
108        Ok(())
109    }
110}
111impl Display for Hex<'_> {
112    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
113        for byte in self.0 {
114            f.write_fmt(format_args!("{:02x}", byte))?;
115        }
116        Ok(())
117    }
118}
119
120#[cfg(test)]
121mod tests {
122    use std::format;
123
124    use super::*;
125
126    #[test]
127    fn test_all_bytes() {
128        for byte in 0..=0xff {
129            assert_eq!(format!("{:02x}", byte), format!("{}", Hex(&[byte])));
130            assert_eq!(format!("{:02X}", byte), format!("{:X}", Hex(&[byte])));
131        }
132    }
133
134    #[test]
135    fn test_all_byte_pairs() {
136        for (a, b) in (0..=0xff).zip(0..=0xff) {
137            assert_eq!(format!("{:02x}{:02x}", a, b), format!("{}", Hex(&[a, b])));
138            assert_eq!(format!("{:02X}{:02X}", a, b), format!("{:X}", Hex(&[a, b])));
139        }
140    }
141}