use core::fmt;
use core::mem::ManuallyDrop;
use zeroize::Zeroize;
pub struct Secret<T: Zeroize> {
value: ManuallyDrop<T>,
}
impl<T: Zeroize> Secret<T> {
pub fn new(value: T) -> Self {
Self {
value: ManuallyDrop::new(value),
}
}
pub fn as_inner(&self) -> &T {
unsafe { &*(&self.value as *const ManuallyDrop<T> as *const T) }
}
pub fn into_inner(self) -> T {
let mut this = ManuallyDrop::new(self);
unsafe { ManuallyDrop::take(&mut this.value) }
}
pub fn expose_secret<R>(&self, f: impl FnOnce(&T) -> R) -> R {
f(self.as_inner())
}
}
impl<T: Zeroize> Drop for Secret<T> {
fn drop(&mut self) {
unsafe {
(&mut *(&mut self.value as *mut ManuallyDrop<T> as *mut T)).zeroize();
ManuallyDrop::drop(&mut self.value);
}
}
}
impl<T: Zeroize + Clone> Clone for Secret<T> {
fn clone(&self) -> Self {
Self::new(self.as_inner().clone())
}
}
impl<T: Zeroize + PartialEq> PartialEq for Secret<T> {
fn eq(&self, other: &Self) -> bool {
self.as_inner() == other.as_inner()
}
}
impl<T: Zeroize + Eq> Eq for Secret<T> {}
impl<T: Zeroize> fmt::Debug for Secret<T> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("[REDACTED]")
}
}
impl<T: Zeroize> fmt::Display for Secret<T> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("[REDACTED]")
}
}