use std::fmt::Display;
use serde::Serialize;
use zeroize::{Zeroize, ZeroizeOnDrop};
use crate::account::Account;
use crate::redacted::Redacted;
#[derive(Debug, Serialize, Clone, PartialEq, Zeroize, ZeroizeOnDrop)]
#[serde(rename_all = "lowercase")]
pub enum Password {
Super,
User1,
User2,
User3,
Custom(Redacted<String>),
}
impl AsRef<str> for Password {
fn as_ref(&self) -> &str {
match self {
Password::Super => "super",
Password::User1 => "user1",
Password::User2 => "user2",
Password::User3 => "user3",
Password::Custom(e) => e,
}
}
}
impl Display for Password {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Password::Super => write!(f, "super"),
Password::User1 => write!(f, "user1"),
Password::User2 => write!(f, "user2"),
Password::User3 => write!(f, "user3"),
Password::Custom(e) => write!(f, "{e}"),
}
}
}
impl From<Account> for Password {
fn from(value: Account) -> Self {
match value {
Account::Super => Self::Super,
Account::User1 => Self::User1,
Account::User2 => Self::User2,
Account::User3 => Self::User3,
}
}
}
impl From<&str> for Password {
fn from(s: &str) -> Self {
match s {
"super" => Self::Super,
"user1" => Self::User1,
"user2" => Self::User2,
"user3" => Self::User3,
other => Self::Custom(Redacted(other.to_string())),
}
}
}
impl From<String> for Password {
fn from(mut s: String) -> Self {
match s.as_str() {
"super" => {
s.zeroize();
Self::Super
}
"user1" => {
s.zeroize();
Self::User1
}
"user2" => {
s.zeroize();
Self::User2
}
"user3" => {
s.zeroize();
Self::User3
}
_ => Self::Custom(Redacted(s)),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn from_str_builtins() {
assert_eq!(Password::from("super"), Password::Super);
assert_eq!(Password::from("user1"), Password::User1);
assert_eq!(Password::from("user2"), Password::User2);
assert_eq!(Password::from("user3"), Password::User3);
}
#[test]
fn from_str_custom() {
let p = Password::from("custom_secret");
assert_eq!(p, Password::Custom(Redacted("custom_secret".to_string())));
assert_eq!(p.as_ref(), "custom_secret");
assert_eq!(format!("{p:?}"), "Custom([REDACTED])");
assert_eq!(format!("{p}"), "[REDACTED]");
}
#[test]
fn from_string_builtins() {
assert_eq!(Password::from(String::from("super")), Password::Super);
assert_eq!(Password::from(String::from("user1")), Password::User1);
assert_eq!(Password::from(String::from("user2")), Password::User2);
assert_eq!(Password::from(String::from("user3")), Password::User3);
}
#[test]
fn from_string_custom() {
let p = Password::from(String::from("heap_secret"));
assert_eq!(p, Password::Custom(Redacted("heap_secret".to_string())));
assert_eq!(p.as_ref(), "heap_secret");
assert_eq!(format!("{p:?}"), "Custom([REDACTED])");
assert_eq!(format!("{p}"), "[REDACTED]");
}
#[test]
fn display_and_debug_redaction() {
assert_eq!(format!("{}", Password::Super), "super");
assert_eq!(format!("{:?}", Password::Super), "Super");
let custom = Password::Custom(Redacted("hidden_pass".to_string()));
assert_eq!(format!("{custom}"), "[REDACTED]");
assert_eq!(format!("{custom:?}"), "Custom([REDACTED])");
assert_eq!(custom.as_ref(), "hidden_pass");
}
#[test]
fn zeroize_custom_password() {
let mut p = Password::from("temporary_secret");
p.zeroize();
if let Password::Custom(r) = &p {
assert_eq!(&**r, "");
} else {
panic!("expected Custom variant");
}
}
}