use crate::errors::Error;
use crate::password::checker::PasswordStrongChecker;
use bcrypt::{hash, DEFAULT_COST};
use regex::Regex;
use std::fmt::{Debug, Display, Formatter};
use std::marker::PhantomData;
use std::ops::Deref;
pub const HASHED_PASSWORD_REGEX_VALUE: &str = r"^\$([a-z\d]+)\$([a-z\d]+)\$.*";
pub struct Raw;
pub struct Encrypt;
#[derive(Clone, Eq, PartialEq)]
pub struct Password<State = Encrypt> {
value: String,
state: PhantomData<State>,
}
impl Password {
pub fn new(raw_password: &str) -> Password<Raw> {
Password {
value: raw_password.to_owned(),
state: PhantomData,
}
}
pub fn from_raw(raw_password: &str) -> Password<Raw> {
Self::new(raw_password)
}
pub fn from_encrypt(encrypted_password: &str) -> Result<Password<Encrypt>, Error> {
let password_regex = Regex::new(HASHED_PASSWORD_REGEX_VALUE)?;
if !password_regex.is_match(encrypted_password) {
return Err(Error::InexistentEncryptPassword);
}
Ok(Password {
value: encrypted_password.to_owned(),
state: PhantomData,
})
}
}
impl Password<Raw> {
pub fn check(self) -> Result<Self, Error> {
PasswordStrongChecker::new().check(&self.value)?;
Ok(self)
}
pub fn custom_check(self, checker: PasswordStrongChecker) -> Result<Self, Error> {
checker.check(&self.value)?;
Ok(self)
}
pub fn to_encrypt(self) -> Result<Password<Encrypt>, Error> {
let encrypt_password = hash(&self.value, DEFAULT_COST + 1)?;
Ok(Password {
value: encrypt_password,
state: PhantomData,
})
}
}
impl Display for Password<Encrypt> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
Display::fmt(&self.value, f)
}
}
impl AsRef<str> for Password<Encrypt> {
fn as_ref(&self) -> &str {
&self.value
}
}
impl Debug for Password<Encrypt> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "Password(\"{}\")", self.as_ref())
}
}
impl Deref for Password<Encrypt> {
type Target = str;
fn deref(&self) -> &Self::Target {
&self.value
}
}