Provides wrapper structs with default Debug
impls.
This allows you to use the default implementation of Debug
for large structs while enabling you
to:
- avoid using
Debug
impls that traverse deeply nested or overly large structures, - avoid using a
Debug
impl that leaks info that should not be logged.
This can improve:
- readability (logs can focus on the information you care about),
- debuggability & security (logs can be more complete without accidentally leaking private info),
- and performance (complex data structures don't need to be traversed for debugging unless intentionally requested via
Deref
).
Example usage: Hiding a user's password from logs.
use no_debug::{NoDebug, WithTypeInfo, Ellipses};
#[derive(Debug)]
struct UserInfo {
username: String,
password: NoDebug<String>, // Defaults to WithTypeInfo
posts: NoDebug<Vec<String>, Ellipses>,
}
let user = UserInfo {
username: "Cypher1".to_string(),
password: "hunter2".to_string().into(),
posts: vec![
"long post 1...".to_string(),
"long post 2...".to_string(),
"long post 3...".to_string(),
].into()
};
// The password is hidden by default
assert_eq!(
format!("{:#?}", user),
r#"UserInfo {
username: "Cypher1",
password: <no debug: alloc::string::String>,
posts: ...,
}"#
);
// And when accessed
assert_eq!(format!("{:?}", user.password), r#"<no debug: alloc::string::String>"#);
// But it can be extracted easily for operating on the data inside, at which point it is
// visible again.
assert_eq!(format!("{:?}", *user.password), r#""hunter2""#);
// The debug output is based on the Msg type.
assert_eq!(format!("{:?}", user.posts), r#"..."#);
// Output can be changed easily with a type conversion
let post_with_type: NoDebug<Vec<String>, WithTypeInfo> = user.posts.take().into();
assert_eq!(format!("{:?}", post_with_type), r#"<no debug: alloc::vec::Vec<alloc::string::String>>"#);