85 lines
2.1 KiB
Rust
85 lines
2.1 KiB
Rust
#![feature(iter_intersperse)]
|
|
|
|
#[macro_use]
|
|
extern crate serde;
|
|
|
|
use std::collections::{HashMap, HashSet};
|
|
|
|
use rand::Rng;
|
|
use rand::distributions::Slice as SliceDist;
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
#[serde(untagged)]
|
|
enum RuleSet {
|
|
Password {
|
|
length: usize,
|
|
lowercase: bool,
|
|
uppercase: bool,
|
|
numbers: bool,
|
|
#[serde(default = "default_special_characters")]
|
|
special_characters: Vec<char>,
|
|
},
|
|
Passphrase {
|
|
count: usize,
|
|
delimiters: Vec<char>,
|
|
/// Path to words list.
|
|
words: String,
|
|
},
|
|
}
|
|
|
|
fn default_special_characters() -> Vec<char> {
|
|
(0..128)
|
|
.map(|b| char::from_u32(b).unwrap())
|
|
.filter(|c| c.is_ascii_graphic() && !c.is_ascii_alphanumeric())
|
|
.collect()
|
|
}
|
|
|
|
fn main() {
|
|
let mut rng = rand::thread_rng();
|
|
|
|
let mut rule_sets: HashMap<String, RuleSet> = toml::from_str(&std::fs::read_to_string("rules.toml").unwrap()).unwrap();
|
|
|
|
let mut args = std::env::args();
|
|
_ = args.next().unwrap();
|
|
let rule_set_name = args.next().expect("Must specify a rule set name");
|
|
|
|
let password = match rule_sets.remove(&rule_set_name).unwrap() {
|
|
RuleSet::Password { length, lowercase, uppercase, numbers, special_characters } => {
|
|
let mut chars = HashSet::new();
|
|
if lowercase {
|
|
chars.extend('a'..='z');
|
|
}
|
|
if uppercase {
|
|
chars.extend('A'..='Z');
|
|
}
|
|
if numbers {
|
|
chars.extend('0'..='9');
|
|
}
|
|
chars.extend(&special_characters);
|
|
let chars = chars.into_iter().collect::<Vec<_>>();
|
|
|
|
rng.sample_iter(SliceDist::new(&chars).unwrap())
|
|
.take(length)
|
|
.collect::<String>()
|
|
}
|
|
RuleSet::Passphrase { count, delimiters, words } => {
|
|
assert!(count > 0);
|
|
let words = std::fs::read_to_string(&words).unwrap();
|
|
let words = words.lines().filter(|s| !s.is_empty()).collect::<Vec<_>>();
|
|
let words = SliceDist::new(&words).unwrap();
|
|
let delimiters = delimiters.iter().map(char::to_string).collect::<Vec<String>>();
|
|
let delimiters = SliceDist::new(&delimiters).unwrap();
|
|
let mut s = String::new();
|
|
let mut word = rng.sample(&words);
|
|
for _ in 0..count-1 {
|
|
s.push_str(word);
|
|
word = rng.sample(&words);
|
|
s.push_str(rng.sample(&delimiters));
|
|
}
|
|
s.push_str(word);
|
|
s
|
|
}
|
|
};
|
|
println!("{}", password);
|
|
}
|