1 // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 // Copyright by contributors to this project.
3 // SPDX-License-Identifier: (Apache-2.0 OR MIT)
4 
5 use core::fmt::{self, Debug};
6 
7 const DEFAULT_BYTES_TYPE_NAME: &str = "Bytes";
8 
pretty_bytes(bytes: &[u8]) -> PrettyBytes<'_>9 pub fn pretty_bytes(bytes: &[u8]) -> PrettyBytes<'_> {
10     PrettyBytes {
11         ty: None,
12         bytes,
13         show_len: true,
14         show_raw: false,
15     }
16 }
17 
18 pub struct PrettyBytes<'a> {
19     ty: Option<&'a str>,
20     bytes: &'a [u8],
21     show_len: bool,
22     show_raw: bool,
23 }
24 
25 impl<'a> PrettyBytes<'a> {
named(self, ty: &'a str) -> Self26     pub fn named(self, ty: &'a str) -> Self {
27         Self {
28             ty: Some(ty),
29             ..self
30         }
31     }
32 
show_len(self, show: bool) -> Self33     pub fn show_len(self, show: bool) -> Self {
34         Self {
35             show_len: show,
36             ..self
37         }
38     }
39 
show_raw(self, show: bool) -> Self40     pub fn show_raw(self, show: bool) -> Self {
41         Self {
42             show_raw: show,
43             ..self
44         }
45     }
46 }
47 
48 impl Debug for PrettyBytes<'_> {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result49     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50         let show_raw = self.show_raw || f.alternate();
51         match (self.ty, self.show_len, show_raw) {
52             (_, false, false) => show_only_type(self.ty, f),
53             (None, false, true) => show_only_raw(self.bytes, f),
54             (Some(ty), false, true) => show_newtype(ty, self.bytes, f),
55             (_, true, _) => show_struct(self.ty, self.bytes, show_raw, f),
56         }
57     }
58 }
59 
show_only_type(ty: Option<&str>, f: &mut fmt::Formatter<'_>) -> fmt::Result60 fn show_only_type(ty: Option<&str>, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61     f.write_str(ty.unwrap_or(DEFAULT_BYTES_TYPE_NAME))
62 }
63 
show_only_raw(bytes: &[u8], f: &mut fmt::Formatter<'_>) -> fmt::Result64 fn show_only_raw(bytes: &[u8], f: &mut fmt::Formatter<'_>) -> fmt::Result {
65     f.write_str(&hex::encode(bytes))
66 }
67 
show_newtype(ty: &str, bytes: &[u8], f: &mut fmt::Formatter<'_>) -> fmt::Result68 fn show_newtype(ty: &str, bytes: &[u8], f: &mut fmt::Formatter<'_>) -> fmt::Result {
69     write!(f, "{ty}({})", hex::encode(bytes))
70 }
71 
show_struct( ty: Option<&str>, bytes: &[u8], show_raw: bool, f: &mut fmt::Formatter<'_>, ) -> fmt::Result72 fn show_struct(
73     ty: Option<&str>,
74     bytes: &[u8],
75     show_raw: bool,
76     f: &mut fmt::Formatter<'_>,
77 ) -> fmt::Result {
78     let ty = ty.unwrap_or(DEFAULT_BYTES_TYPE_NAME);
79     let mut out = f.debug_struct(ty);
80     out.field("len", &bytes.len());
81     if show_raw {
82         out.field("raw", &hex::encode(bytes));
83     }
84     out.finish()
85 }
86 
pretty_with<F>(f: F) -> impl Debug where F: Fn(&mut fmt::Formatter<'_>) -> fmt::Result,87 pub fn pretty_with<F>(f: F) -> impl Debug
88 where
89     F: Fn(&mut fmt::Formatter<'_>) -> fmt::Result,
90 {
91     PrettyWith(f)
92 }
93 
94 struct PrettyWith<F>(F);
95 
96 impl<F> Debug for PrettyWith<F>
97 where
98     F: Fn(&mut fmt::Formatter<'_>) -> fmt::Result,
99 {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result100     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101         self.0(f)
102     }
103 }
104 
pretty_group_id(id: &[u8]) -> impl Debug + '_105 pub fn pretty_group_id(id: &[u8]) -> impl Debug + '_ {
106     pretty_bytes(id).show_len(false).show_raw(true)
107 }
108 
109 #[cfg(test)]
110 mod tests {
111     use crate::debug::pretty_bytes;
112 
113     #[test]
default_format_contains_only_length()114     fn default_format_contains_only_length() {
115         let bytes = pretty_bytes(b"foobar");
116         let output = format!("{bytes:?}");
117         assert!(output.contains("len"));
118         assert!(!output.contains("raw"));
119         assert!(!output.contains(&hex::encode(b"foobar")));
120     }
121 
122     #[test]
alternate_format_contains_length_and_hex_encoded_bytes()123     fn alternate_format_contains_length_and_hex_encoded_bytes() {
124         let bytes = pretty_bytes(b"foobar");
125         let output = format!("{bytes:#?}");
126         assert!(output.contains("len"));
127         assert!(output.contains("raw"));
128         assert!(output.contains(&hex::encode(b"foobar")));
129     }
130 }
131