extern crate rand;
mod utils;
use rand::Rng;
#[cfg(feature="built_in_dicts")]
#[allow(non_camel_case_types)]
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
pub enum Dictionary {
en_US,
}
#[cfg(feature="built_in_dicts")]
impl Dictionary {
fn to_raw_dict(self) -> &'static str {
match self {
Dictionary::en_US => include_str!(concat!(env!("OUT_DIR"), "/dictionary/en_US")),
}
}
}
pub fn generate_password_custom_dict(word_count: usize, dict: &[&str]) -> String {
let dict = utils::trim_right_by(dict, |e| e.is_empty());
let capacity = word_count * dict.len() + word_count;
let n_of_words: Vec<usize> = dict.iter().enumerate().map(|x| x.1.len() / (x.0 + 1)).collect();
let total_n_of_words = n_of_words.iter().sum();
if dict.iter().zip(n_of_words.iter()).enumerate().any(|(wlen, (words, count))| (wlen+1)*count != words.len()) {
panic!("Error in password generation: Dictionary invalid.");
}
let mut s = String::with_capacity(capacity);
for word_n in (0..word_count).map(|_| rand::thread_rng().gen_range(0, total_n_of_words)) {
let mut found: Option<&str> = None;
let mut real_pos = 0;
for (wlen, (n_words, words)) in n_of_words.iter().zip(dict.iter()).enumerate() {
let wlen = wlen + 1;
real_pos += n_words;
if word_n < real_pos {
let base_pos = real_pos - n_words;
let inner_pos = word_n - base_pos;
let inner_index = inner_pos * wlen;
found = Some(&words[inner_index..inner_index+wlen]);
break;
}
}
s.push_str(found.expect("bug in xkcd-password"));
s.push(' ');
}
s
}
pub fn generate_password_from_words<'a, I: IntoIterator<Item=&'a str>>(word_count: usize, dict: I) -> String {
let iter = dict.into_iter();
let mut dict: Vec<String> = vec![];
for word in iter {
if word.contains(' ') || word.contains('\n') {
panic!("Word contains space or newline characters");
}
if dict.len() < word.len() {
dict.resize(word.len(), String::default());
}
if dict[word.len() - 1].as_bytes().chunks(word.len()).any(|chunk| chunk == word.as_bytes()) {
panic!("Duplicate word in dict");
}
dict[word.len() - 1].push_str(word);
}
let dict: Vec<&str> = dict.iter().map(String::as_str).collect();
generate_password_custom_dict(word_count, &dict)
}
#[cfg(feature="built_in_dicts")]
pub fn generate_password(word_count: usize, dict: Dictionary) -> String {
let v: Vec<&str> = dict.to_raw_dict().lines().collect();
generate_password_custom_dict(word_count, &v)
}