use deadbolt_crypto::rand::Random;
use regex::Regex;
use secrecy::Secret;
const MIN_PASSWORD_LEN: usize = 18;
fn is_no_repetition(password: &str) -> bool {
let n = 2;
let mut repetitions = password
.chars()
.map(Some)
.chain(std::iter::once(None))
.scan((0, None), |(count, ch), v| match ch {
Some(c) if *c == v => {
*count += 1;
Some((None, *count))
}
_ => Some((ch.replace(v), std::mem::replace(count, 1))),
})
.filter_map(|(ch, count)| match ch {
Some(Some(ch)) if count >= n => Some((ch, count)),
_ => None,
})
.peekable();
repetitions.peek().is_none()
}
fn is_good_distribution(password: &str, punctuation: Option<bool>) -> bool {
let uppercase_regex = Regex::new(r"[A-Z]").unwrap();
let lowercase_regex = Regex::new(r"[a-z]").unwrap();
let digit_regex = Regex::new(r"\d").unwrap();
let punctuation_regex = Regex::new(r"[~!@#\$%\^&\*()-_=\+\[\]\{\};':,\./<>?]").unwrap();
if uppercase_regex.is_match(password)
&& lowercase_regex.is_match(password)
&& digit_regex.is_match(password)
{
if let Some(punctuation) = punctuation {
if punctuation {
return punctuation_regex.is_match(password);
}
}
return true;
}
false
}
fn is_strong_password(password: &str, punctuation: Option<bool>) -> bool {
if !is_no_repetition(password) {
return false;
}
if !is_good_distribution(password, punctuation) {
return false;
}
true
}
pub fn get_strong_random_password(
length: Option<usize>,
punctuation: Option<bool>,
) -> Secret<String> {
let mut password = Random::get_rand_chars(
length.unwrap_or(MIN_PASSWORD_LEN),
punctuation.unwrap_or(true),
);
loop {
if is_strong_password(&password, punctuation) {
break;
}
password = Random::get_rand_chars(
length.unwrap_or(MIN_PASSWORD_LEN),
punctuation.unwrap_or(true),
);
}
Secret::new(password)
}