1use std::fmt::{self, Debug, Display, Formatter};
2
3#[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 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}