use std::fmt;
use secrecy::{ExposeSecret, SecretString};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use crate::StorageError;
#[derive(Clone)]
pub struct Password {
secret: SecretString,
}
impl Password {
pub fn new(secret: SecretString) -> Result<Self, StorageError> {
reject_empty_secret("password", &secret)?;
Ok(Self { secret })
}
pub fn from_string(secret: impl Into<String>) -> Result<Self, StorageError> {
Self::new(SecretString::new(secret.into().into()))
}
#[cfg(any(feature = "postgres", feature = "surrealdb"))]
pub(crate) fn as_secret(&self) -> &SecretString {
&self.secret
}
}
impl fmt::Debug for Password {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Password")
.field("secret", &"[redacted]")
.finish()
}
}
impl Serialize for Password {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(self.secret.expose_secret())
}
}
impl<'de> Deserialize<'de> for Password {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Self::from_string(String::deserialize(deserializer)?).map_err(serde::de::Error::custom)
}
}
fn reject_empty_secret(label: &str, secret: &SecretString) -> Result<(), StorageError> {
if secret.expose_secret().trim().is_empty() {
return Err(StorageError::InvalidConfig({
let mut message = String::from(label);
message.push_str(" must not be empty");
message
}));
}
Ok(())
}