jiff/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 crate::util::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 readable
12/// representation of it. If the byte isn't ASCII, then it's emitted as a hex
13/// escape sequence.
14#[derive(Clone, Copy)]
15pub 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 to cover 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 UTF-8,
49/// but will work for anything. For any bytes that aren't UTF-8, they are
50/// emitted as hex escape sequences.
51#[derive(Clone, Copy)]
52pub 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                // ASCII control characters except \0, \n, \r, \t
71                '\x01'..='\x08'
72                | '\x0b'
73                | '\x0c'
74                | '\x0e'..='\x19'
75                | '\x7f' => {
76                    write!(f, "\\x{:02x}", u32::from(ch))?;
77                }
78                '\n' | '\r' | '\t' | _ => {
79                    write!(f, "{}", ch.escape_debug())?;
80                }
81            }
82        }
83        Ok(())
84    }
85}
86
87impl<'a> core::fmt::Debug for Bytes<'a> {
88    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
89        write!(f, "\"")?;
90        core::fmt::Display::fmt(self, f)?;
91        write!(f, "\"")?;
92        Ok(())
93    }
94}