use secrecy::{ExposeSecret, SecretString};
#[derive(Clone)]
pub struct ApiToken(SecretString);
impl ApiToken {
pub fn new(s: impl Into<String>) -> Self {
Self(SecretString::from(s.into()))
}
pub fn expose_for_auth(&self) -> &str {
self.0.expose_secret()
}
}
impl std::fmt::Debug for ApiToken {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("ApiToken([REDACTED])")
}
}
impl From<String> for ApiToken {
fn from(s: String) -> Self {
Self::new(s)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn debug_does_not_expose_secret() {
let token = ApiToken::new("super-secret");
assert_eq!(format!("{token:?}"), "ApiToken([REDACTED])");
assert!(!format!("{token:?}").contains("super-secret"));
}
#[test]
fn expose_for_auth_returns_raw_value() {
let token = ApiToken::new("super-secret");
assert_eq!(token.expose_for_auth(), "super-secret");
}
#[test]
fn clone_preserves_value() {
let token = ApiToken::new("secret");
assert_eq!(token.clone().expose_for_auth(), "secret");
}
}