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(errant_bytes) => {
62                    // The decode API guarantees `errant_bytes` is non-empty.
63                    write!(f, r"\x{:02x}", errant_bytes[0])?;
64                    bytes = &bytes[1..];
65                    continue;
66                }
67            };
68            bytes = &bytes[ch.len_utf8()..];
69            match ch {
70                '\0' => write!(f, "\\0")?,
71                '\x01'..='\x7f' => {
72                    write!(f, "{}", (ch as u8).escape_ascii())?;
73                }
74                _ => write!(f, "{}", ch.escape_debug())?,
75            }
76        }
77        Ok(())
78    }
79}
80
81impl<'a> core::fmt::Debug for Bytes<'a> {
82    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
83        write!(f, "\"")?;
84        core::fmt::Display::fmt(self, f)?;
85        write!(f, "\"")?;
86        Ok(())
87    }
88}