use std::fmt;
use serde::Deserialize;
use zeroize::{Zeroize, ZeroizeOnDrop};
#[derive(Clone, PartialEq, Eq, Deserialize, Zeroize, ZeroizeOnDrop)]
pub struct SecretBytes(Vec<u8>);
impl SecretBytes {
#[must_use]
pub const fn new(secret: Vec<u8>) -> Self {
Self(secret)
}
#[must_use]
pub fn from_slice(secret: &[u8]) -> Self {
Self(secret.to_vec())
}
#[must_use]
pub fn expose_secret(&self) -> &[u8] {
&self.0
}
#[must_use]
pub const fn len(&self) -> usize {
self.0.len()
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.0.is_empty()
}
}
impl AsRef<[u8]> for SecretBytes {
fn as_ref(&self) -> &[u8] {
self.expose_secret()
}
}
impl fmt::Debug for SecretBytes {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("***")
}
}
#[derive(Clone, PartialEq, Eq, Deserialize, Zeroize, ZeroizeOnDrop)]
pub struct SecretString(String);
impl SecretString {
#[must_use]
pub const fn new(secret: String) -> Self {
Self(secret)
}
#[must_use]
pub fn from_secret(secret: &str) -> Self {
Self(secret.to_owned())
}
#[must_use]
pub fn expose_secret(&self) -> &str {
&self.0
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.0.is_empty()
}
}
impl AsRef<str> for SecretString {
fn as_ref(&self) -> &str {
self.expose_secret()
}
}
impl fmt::Debug for SecretString {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("***")
}
}
#[cfg(test)]
mod tests {
use super::{SecretBytes, SecretString};
#[test]
fn secret_bytes_debug_redacts_contents() {
let secret = SecretBytes::from_slice(b"test-signing-key-32-bytes-long!!");
assert_eq!(format!("{secret:?}"), "***");
}
#[test]
fn secret_bytes_exposes_underlying_bytes() {
let secret = SecretBytes::from_slice(b"test-signing-key-32-bytes-long!!");
assert_eq!(secret.expose_secret(), b"test-signing-key-32-bytes-long!!");
}
#[test]
fn secret_string_debug_redacts_contents() {
let secret = SecretString::from_secret("bootstrap-token");
assert_eq!(format!("{secret:?}"), "***");
}
#[test]
fn secret_string_exposes_underlying_text() {
let secret = SecretString::from_secret("bootstrap-token");
assert_eq!(secret.expose_secret(), "bootstrap-token");
}
}