Skip to main content

webc/
readable_bytes.rs

1use std::fmt::{self, Debug, Display, Formatter};
2
3/// Print a human-friendly version of a byte string.
4#[allow(dead_code)]
5pub(crate) fn readable_bytes(bytes: &[u8]) -> impl Display + Debug + Copy + '_ {
6    ReadableBytes { bytes, limit: 64 }
7}
8
9#[derive(Copy, Clone)]
10pub(crate) struct ReadableBytes<'a> {
11    bytes: &'a [u8],
12    limit: usize,
13}
14
15impl ReadableBytes<'_> {
16    fn write(&self, f: &mut Formatter<'_>) -> fmt::Result {
17        if let Ok(s) = std::str::from_utf8(self.bytes) {
18            f.write_str("\"")?;
19            for (i, c) in s.chars().enumerate() {
20                if i >= self.limit {
21                    f.write_str("...")?;
22                    break;
23                }
24                for c in c.escape_debug() {
25                    Display::fmt(&c, f)?;
26                }
27            }
28            f.write_str("\"")?;
29            return Ok(());
30        }
31
32        // otherwise, fall back to escaped ascii
33        let len = std::cmp::min(self.bytes.len(), self.limit);
34
35        f.write_str("b\"")?;
36        for &byte in &self.bytes[..len] {
37            for c in std::ascii::escape_default(byte) {
38                Display::fmt(&c, f)?;
39            }
40        }
41        if len == self.limit {
42            f.write_str("...")?;
43        }
44        f.write_str("\"")?;
45
46        Ok(())
47    }
48}
49
50impl Display for ReadableBytes<'_> {
51    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
52        self.write(f)
53    }
54}
55
56impl Debug for ReadableBytes<'_> {
57    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
58        self.write(f)
59    }
60}
61
62#[cfg(test)]
63mod tests {
64    use super::*;
65
66    #[test]
67    fn unicode() {
68        let src = "Hello, Unicode! ✓";
69
70        let got = readable_bytes(src.as_bytes()).to_string();
71
72        assert_eq!(got, "\"Hello, Unicode! \u{2713}\"");
73    }
74
75    #[test]
76    fn binary() {
77        let src = [0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00];
78
79        let got = readable_bytes(&src).to_string();
80
81        assert_eq!(got, "\"\\0asm\\u{1}\\0\\0\\0\"");
82    }
83
84    #[test]
85    fn limit_the_length() {
86        let src = [b'A'; 256];
87
88        let got = readable_bytes(&src).to_string();
89
90        assert_eq!(got, format!("\"{}...\"", "A".repeat(64)));
91    }
92}