jiff/shared/util/
escape.rs

1/*!
2Provides convenience routines for escaping raw bytes.
3
4This was copied from `regex-automata` with a few light edits.
5*/
6
7use super::utf8;
8
9/// Provides a convenient `Debug` implementation for a `u8`.
10///
11/// The `Debug` impl treats the byte as an ASCII, and emits a human
12/// readable representation of it. If the byte isn't ASCII, then it's
13/// emitted as a hex escape sequence.
14#[derive(Clone, Copy)]
15pub(crate) struct Byte(pub u8);
16
17impl core::fmt::Display for Byte {
18    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
19        if self.0 == b' ' {
20            return write!(f, " ");
21        }
22        // 10 bytes is enough for any output from ascii::escape_default.
23        let mut bytes = [0u8; 10];
24        let mut len = 0;
25        for (i, mut b) in core::ascii::escape_default(self.0).enumerate() {
26            // capitalize \xab to \xAB
27            if i >= 2 && b'a' <= b && b <= b'f' {
28                b -= 32;
29            }
30            bytes[len] = b;
31            len += 1;
32        }
33        write!(f, "{}", core::str::from_utf8(&bytes[..len]).unwrap())
34    }
35}
36
37impl core::fmt::Debug for Byte {
38    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
39        write!(f, "\"")?;
40        core::fmt::Display::fmt(self, f)?;
41        write!(f, "\"")?;
42        Ok(())
43    }
44}
45
46/// Provides a convenient `Debug` implementation for `&[u8]`.
47///
48/// This generally works best when the bytes are presumed to be mostly
49/// UTF-8, but will work for anything. For any bytes that aren't UTF-8,
50/// they are emitted as hex escape sequences.
51#[derive(Clone, Copy)]
52pub(crate) struct Bytes<'a>(pub &'a [u8]);
53
54impl<'a> core::fmt::Display for Bytes<'a> {
55    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
56        // This is a sad re-implementation of a similar impl found in bstr.
57        let mut bytes = self.0;
58        while let Some(result) = utf8::decode(bytes) {
59            let ch = match result {
60                Ok(ch) => ch,
61                Err(byte) => {
62                    write!(f, r"\x{:02x}", byte)?;
63                    bytes = &bytes[1..];
64                    continue;
65                }
66            };
67            bytes = &bytes[ch.len_utf8()..];
68            match ch {
69                '\0' => write!(f, "\\0")?,
70                '\x01'..='\x7f' => {
71                    write!(f, "{}", (ch as u8).escape_ascii())?;
72                }
73                _ => write!(f, "{}", ch.escape_debug())?,
74            }
75        }
76        Ok(())
77    }
78}
79
80impl<'a> core::fmt::Debug for Bytes<'a> {
81    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
82        write!(f, "\"")?;
83        core::fmt::Display::fmt(self, f)?;
84        write!(f, "\"")?;
85        Ok(())
86    }
87}