use std::fmt;
#[derive(Clone, PartialEq, Eq)]
pub struct SecretString(String);
impl SecretString {
pub fn new(value: impl Into<String>) -> Self {
Self(value.into())
}
pub fn expose(&self) -> &str {
&self.0
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
}
impl fmt::Debug for SecretString {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("\"***\"")
}
}
impl serde::Serialize for SecretString {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str("***")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn debug_is_redacted() {
let s = SecretString::new("sk-super-secret-value");
assert_eq!(format!("{:?}", s), "\"***\"");
assert!(!format!("{:?}", s).contains("secret"));
}
#[test]
fn serialize_is_redacted() {
let s = SecretString::new("sk-super-secret-value");
let json = serde_json::to_string(&s).unwrap();
assert_eq!(json, "\"***\"");
assert!(!json.contains("secret"));
}
#[test]
fn expose_returns_real_value() {
let s = SecretString::new("real");
assert_eq!(s.expose(), "real");
}
}