use rand::seq::SliceRandom;
use crate::models::PasswordGeneratorSettings;
pub fn generate_password(settings: &PasswordGeneratorSettings) -> String {
let mut rng = rand::thread_rng();
let mut groups: Vec<Vec<u8>> = Vec::new();
if settings.use_lowercase {
groups.push(b"abcdefghijklmnopqrstuvwxyz".to_vec());
}
if settings.use_uppercase {
groups.push(b"ABCDEFGHIJKLMNOPQRSTUVWXYZ".to_vec());
}
if settings.use_numbers {
groups.push(b"0123456789".to_vec());
}
if settings.use_symbols {
groups.push(b"!@#$%^&*()_+-=[]{}|;:,.<>?".to_vec());
}
if groups.is_empty() {
groups.push(b"abcdefghijklmnopqrstuvwxyz".to_vec());
}
if settings.exclude_similar || settings.exclude_ambiguous {
let similar = b"il1Lo0O";
let ambiguous = b"{}[]()/\\'\"`~,;:.<>";
for group in &mut groups {
if settings.exclude_similar {
group.retain(|c| !similar.contains(c));
}
if settings.exclude_ambiguous {
group.retain(|c| !ambiguous.contains(c));
}
}
groups.retain(|g| !g.is_empty());
if groups.is_empty() {
groups.push(b"abcdefghijklmnopqrstuvwxyz".to_vec());
}
}
let chars: Vec<u8> = groups.iter().flatten().cloned().collect();
let mut password: Vec<char> = Vec::with_capacity(settings.length as usize);
for group in &groups {
if password.len() < settings.length as usize {
if let Some(&ch) = group.choose(&mut rng) {
password.push(ch as char);
}
}
}
while password.len() < settings.length as usize {
if let Some(&ch) = chars.choose(&mut rng) {
password.push(ch as char);
}
}
password.shuffle(&mut rng);
password.into_iter().collect()
}