use std::fmt;
use zeroize::Zeroizing;
#[derive(PartialEq, Eq)]
pub(crate) struct Credential(Zeroizing<String>);
impl Credential {
#[allow(dead_code)]
pub(crate) fn new(value: impl Into<String>) -> Self {
Self(Zeroizing::new(value.into()))
}
#[allow(dead_code)]
pub(crate) fn expose(&self) -> &str {
self.0.as_str()
}
}
impl fmt::Debug for Credential {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("[REDACTED]")
}
}
#[cfg(test)]
mod tests {
use std::fmt::Display;
use serde::{Serialize, de::DeserializeOwned};
use static_assertions::assert_not_impl_any;
use super::Credential;
assert_not_impl_any!(Credential: Clone, Display, Serialize, DeserializeOwned);
#[test]
fn credential_debug_output_is_redacted() {
let credential = Credential::new("credential-test-value");
let debug = format!("{credential:?}");
assert_eq!(debug, "[REDACTED]");
assert!(!debug.contains("credential-test-value"));
assert_eq!(credential.expose(), "credential-test-value");
}
}