use secrecy::{ExposeSecret, Zeroize};
pub trait SecretMicrotype: ExposeSecret<Self::Inner> {
type Inner: Zeroize;
fn new(inner: Self::Inner) -> Self;
}
#[macro_export]
macro_rules! secret_microtype {
(ser $inner:ty => [$name:ident, $($names:ident),*]) => {
secret_microtype!(ser $inner => [$name, $($names),*], );
};
(ser $inner:ty => [$name:ident]) => {
secret_microtype!(ser $inner => [$name],);
};
($inner:ty => [$name:ident, $($names:ident),*]) => {
secret_microtype!($inner => [$name, $($names),*],);
};
($inner:ty => [$name:ident]) => {
secret_microtype!($inner => [$name],);
};
(ser $inner:ty => [$name:ident, $($names:ident),*], $($derives:tt),*) => {
secret_microtype!(ser $inner => [$name], $($derives),*);
secret_microtype!(ser $inner => [$($names),*], $($derives),*);
};
(ser $inner:ty => [$name:ident], $($derives:tt),*) => {
$crate::paste::paste! {
#[repr(transparent)]
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
#[derive($($derives),*)]
#[serde(transparent)]
pub struct $name(secrecy::Secret<[<$name Wrapper>]>);
#[repr(transparent)]
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
#[derive($($derives),*)]
#[serde(transparent)]
pub struct [<$name Wrapper>]($inner);
impl $crate::secrecy::CloneableSecret for [<$name Wrapper>] {}
impl $crate::secrecy::DebugSecret for [<$name Wrapper>] {}
impl $crate::secrecy::SerializableSecret for [<$name Wrapper>] {}
impl $crate::secrecy::Zeroize for [<$name Wrapper>] {
fn zeroize(&mut self) {
self.0.zeroize()
}
}
impl $crate::secrecy::ExposeSecret<$inner> for $name {
fn expose_secret(&self) -> &String {
&self.0.expose_secret().0
}
}
impl $crate::SecretMicrotype for $name {
type Inner = $inner;
fn new(inner: Self::Inner) -> Self {
Self($crate::secrecy::Secret::new([<$name Wrapper>](inner)))
}
}
}
};
($inner:ty => [$name:ident, $($names:ident),*], $($derives:tt),*) => {
secret_microtype!($inner => [$name], $($derives),*);
secret_microtype!($inner => [$($names),*], $($derives),*);
};
($inner:ty => [$name:ident], $($derives:tt),*) => {
$crate::paste::paste! {
#[repr(transparent)]
#[derive($($derives),*)]
#[derive(serde::Deserialize, Debug, Clone)]
#[serde(transparent)]
pub struct $name($crate::secrecy::Secret<[<$name Wrapper>]>);
#[repr(transparent)]
#[derive($($derives),*)]
#[derive($crate::serde::Deserialize, Debug, Clone)]
#[serde(transparent)]
pub struct [<$name Wrapper>]($inner);
impl $crate::secrecy::CloneableSecret for [<$name Wrapper>] {}
impl $crate::secrecy::DebugSecret for [<$name Wrapper>] {}
impl $crate::secrecy::Zeroize for [<$name Wrapper>] {
fn zeroize(&mut self) {
self.0.zeroize()
}
}
impl $crate::secrecy::ExposeSecret<$inner> for $name {
fn expose_secret(&self) -> &String {
&self.0.expose_secret().0
}
}
impl SecretMicrotype for $name {
type Inner = $inner;
fn new(inner: Self::Inner) -> Self {
Self($crate::secrecy::Secret::new([<$name Wrapper>](inner)))
}
}
}
};
}
#[cfg(test)]
mod tests {
use super::*;
secret_microtype!(ser String => [Jwt]);
secret_microtype!(String => [Password, Other]);
#[test]
fn example_non_serializable() {
let password = Password::new("asdf".into());
let debug = format!("{:?}", password);
assert!(!debug.contains("asdf"));
let _ = password.clone();
}
#[test]
fn example_serializable() {
let jwt = Jwt::new("jwt".into());
let parsed: Jwt = serde_json::from_str(r#""jwt""#).unwrap();
let serialized = serde_json::to_string(&jwt).unwrap();
assert_eq!(parsed.expose_secret(), jwt.expose_secret());
assert_eq!(serialized, r#""jwt""#);
}
mod can_customise_derives {
secret_microtype!(ser String => [Asdf],);
}
}