use std::fmt;
use serde::Deserialize;
#[derive(Clone, Deserialize, Eq, PartialEq)]
#[serde(transparent)]
pub(crate) struct Secret<T>(T);
#[cfg_attr(
not(test),
allow(dead_code, reason = "constructed via serde; expose is the read path")
)]
impl<T> Secret<T> {
#[must_use]
pub(crate) const fn new(inner: T) -> Self {
Self(inner)
}
#[must_use]
pub(crate) const fn expose(&self) -> &T {
&self.0
}
}
impl<T> fmt::Debug for Secret<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("***")
}
}
impl<T> fmt::Display for Secret<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("***")
}
}
#[cfg(test)]
mod tests {
use super::Secret;
#[test]
fn debug_and_display_redact() {
let secret = Secret::new("super-secret");
assert_eq!(format!("{secret:?}"), "***", "debug");
assert_eq!(format!("{secret}"), "***", "display");
assert_eq!(secret.expose(), &"super-secret", "expose");
}
}