deadbolt-parser 0.1.1

Database parser of deadbolt
Documentation
use deadbolt_crypto::rand::Random;
use regex::Regex;
use secrecy::Secret;

const MIN_PASSWORD_LEN: usize = 18;

fn is_no_repetition(password: &str) -> bool {
    // https://stackoverflow.com/questions/63327968/detect-duplicated-elements-of-a-string-slice-happening-in-order
    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)
}