// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // Copyright by contributors to this project. // SPDX-License-Identifier: (Apache-2.0 OR MIT) use core::fmt::{self, Debug}; const DEFAULT_BYTES_TYPE_NAME: &str = "Bytes"; pub fn pretty_bytes(bytes: &[u8]) -> PrettyBytes<'_> { PrettyBytes { ty: None, bytes, show_len: true, show_raw: false, } } pub struct PrettyBytes<'a> { ty: Option<&'a str>, bytes: &'a [u8], show_len: bool, show_raw: bool, } impl<'a> PrettyBytes<'a> { pub fn named(self, ty: &'a str) -> Self { Self { ty: Some(ty), ..self } } pub fn show_len(self, show: bool) -> Self { Self { show_len: show, ..self } } pub fn show_raw(self, show: bool) -> Self { Self { show_raw: show, ..self } } } impl Debug for PrettyBytes<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let show_raw = self.show_raw || f.alternate(); match (self.ty, self.show_len, show_raw) { (_, false, false) => show_only_type(self.ty, f), (None, false, true) => show_only_raw(self.bytes, f), (Some(ty), false, true) => show_newtype(ty, self.bytes, f), (_, true, _) => show_struct(self.ty, self.bytes, show_raw, f), } } } fn show_only_type(ty: Option<&str>, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(ty.unwrap_or(DEFAULT_BYTES_TYPE_NAME)) } fn show_only_raw(bytes: &[u8], f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(&hex::encode(bytes)) } fn show_newtype(ty: &str, bytes: &[u8], f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{ty}({})", hex::encode(bytes)) } fn show_struct( ty: Option<&str>, bytes: &[u8], show_raw: bool, f: &mut fmt::Formatter<'_>, ) -> fmt::Result { let ty = ty.unwrap_or(DEFAULT_BYTES_TYPE_NAME); let mut out = f.debug_struct(ty); out.field("len", &bytes.len()); if show_raw { out.field("raw", &hex::encode(bytes)); } out.finish() } pub fn pretty_with(f: F) -> impl Debug where F: Fn(&mut fmt::Formatter<'_>) -> fmt::Result, { PrettyWith(f) } struct PrettyWith(F); impl Debug for PrettyWith where F: Fn(&mut fmt::Formatter<'_>) -> fmt::Result, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0(f) } } pub fn pretty_group_id(id: &[u8]) -> impl Debug + '_ { pretty_bytes(id).show_len(false).show_raw(true) } #[cfg(test)] mod tests { use crate::debug::pretty_bytes; #[test] fn default_format_contains_only_length() { let bytes = pretty_bytes(b"foobar"); let output = format!("{bytes:?}"); assert!(output.contains("len")); assert!(!output.contains("raw")); assert!(!output.contains(&hex::encode(b"foobar"))); } #[test] fn alternate_format_contains_length_and_hex_encoded_bytes() { let bytes = pretty_bytes(b"foobar"); let output = format!("{bytes:#?}"); assert!(output.contains("len")); assert!(output.contains("raw")); assert!(output.contains(&hex::encode(b"foobar"))); } }