hex_display/
lib.rs

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