mod words;
use crate::words::WORDS;
use clap::Parser;
use rand::seq::{IteratorRandom, SliceRandom};
use rand::Rng;
#[derive(Parser, Debug)]
#[command(version, about, long_about = None)]
pub struct Args {
#[arg(
short = 'n',
long = "num",
default_value_t = 5,
value_parser = clap::value_parser!(u8).range(3..)
)]
number_of_words: u8,
#[arg(
short = 'm',
long = "min",
default_value_t = 3,
value_parser = clap::value_parser!(u8).range(3..=8)
)]
min_word_length: u8,
#[arg(
short = 'x',
long = "max",
default_value_t = 8,
value_parser = clap::value_parser!(u8).range(3..=8)
)]
max_word_length: u8,
}
const SYMBOLS: &str = "-!@$%&*_+=#^.";
const DIGITS: &str = "0123456789";
pub fn password(args: Args) -> String {
let number_of_words = args.number_of_words;
let caps_word = rand::thread_rng().gen_range(0..number_of_words);
let filtered_words = words(args.min_word_length, args.max_word_length);
let number_of_digits = number_of_words - 2;
let symbol_joiner = rand::thread_rng().gen_range(0..number_of_digits);
(0..number_of_words)
.map(|n| {
let word = if n == caps_word {
filtered_words
.choose(&mut rand::thread_rng())
.unwrap()
.to_uppercase()
} else {
filtered_words
.choose(&mut rand::thread_rng())
.unwrap()
.to_string()
};
if n + 1 < number_of_words {
let joiner = if n == symbol_joiner {
random_char(SYMBOLS)
} else {
random_char(DIGITS)
};
format!("{}{}", word, joiner)
} else {
word
}
})
.collect::<Vec<String>>()
.join("")
}
fn random_char(input: &str) -> char {
input.chars().choose(&mut rand::thread_rng()).unwrap()
}
fn words(min: u8, max: u8) -> Vec<&'static str> {
WORDS
.iter()
.filter(|&&word| {
let len: u8 = word.len().try_into().unwrap();
len >= min && len <= max
})
.copied()
.collect()
}