mls_rs_core/
debug.rs

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
5use core::fmt::{self, Debug};
6
7const DEFAULT_BYTES_TYPE_NAME: &str = "Bytes";
8
9pub fn pretty_bytes(bytes: &[u8]) -> PrettyBytes<'_> {
10    PrettyBytes {
11        ty: None,
12        bytes,
13        show_len: true,
14        show_raw: false,
15    }
16}
17
18pub struct PrettyBytes<'a> {
19    ty: Option<&'a str>,
20    bytes: &'a [u8],
21    show_len: bool,
22    show_raw: bool,
23}
24
25impl<'a> PrettyBytes<'a> {
26    pub fn named(self, ty: &'a str) -> Self {
27        Self {
28            ty: Some(ty),
29            ..self
30        }
31    }
32
33    pub fn show_len(self, show: bool) -> Self {
34        Self {
35            show_len: show,
36            ..self
37        }
38    }
39
40    pub fn show_raw(self, show: bool) -> Self {
41        Self {
42            show_raw: show,
43            ..self
44        }
45    }
46}
47
48impl Debug for PrettyBytes<'_> {
49    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
60fn 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
64fn show_only_raw(bytes: &[u8], f: &mut fmt::Formatter<'_>) -> fmt::Result {
65    f.write_str(&hex::encode(bytes))
66}
67
68fn show_newtype(ty: &str, bytes: &[u8], f: &mut fmt::Formatter<'_>) -> fmt::Result {
69    write!(f, "{ty}({})", hex::encode(bytes))
70}
71
72fn 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
87pub fn pretty_with<F>(f: F) -> impl Debug
88where
89    F: Fn(&mut fmt::Formatter<'_>) -> fmt::Result,
90{
91    PrettyWith(f)
92}
93
94struct PrettyWith<F>(F);
95
96impl<F> Debug for PrettyWith<F>
97where
98    F: Fn(&mut fmt::Formatter<'_>) -> fmt::Result,
99{
100    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101        self.0(f)
102    }
103}
104
105pub fn pretty_group_id(id: &[u8]) -> impl Debug + '_ {
106    pretty_bytes(id).show_len(false).show_raw(true)
107}
108
109#[cfg(test)]
110mod tests {
111    use crate::debug::pretty_bytes;
112
113    #[test]
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]
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}