use rand::prelude::*;
mod diceware8k;
pub fn generate(size: usize) -> String {
generate_passphrase(size, None)
}
pub fn generate_with_separator(size: usize, separator: &str) -> String {
generate_passphrase(size, separator)
}
fn generate_passphrase<'a, SEP>(size: usize, separator: SEP) -> String
where
SEP: Into<Option<&'a str>>,
{
let separator = separator.into().unwrap_or(" ");
if size <= 0 {
"".to_string()
} else {
(0..size)
.map(|_| {
let mut buf = [0u8; 16];
let mut rng = rand::rng();
rng.fill_bytes(&mut buf);
let random_u64 = rng.random::<u64>();
diceware8k::get_dice_word(random_u64 as usize)
})
.fold("".to_string(), |mut acc, passphrase| {
if !acc.is_empty() {
acc.push_str(separator);
}
acc.push_str(passphrase);
acc
})
}
}
#[cfg(test)]
mod dice_unit_tests {
use super::*;
#[test]
fn test_generate_passphrase() {
assert!(generate_passphrase(0, None) == "");
let mut all: Vec<String> = Vec::new();
for _ in 0..1000 {
let passphrase = generate_passphrase(10, None);
if all.contains(&passphrase) {
assert!(
false,
"Same passphrase has already been generated: {}",
passphrase
);
}
all.push(passphrase);
}
let passphrase = generate_passphrase(3, "~");
let s: Vec<&str> = passphrase.split('~').collect();
assert_eq!(s.len(), 3);
}
#[test]
fn test_generate_with_separator() {
let passphrase = generate_with_separator(3, "~");
let s: Vec<&str> = passphrase.split('~').collect();
assert_eq!(s.len(), 3);
let passphrase = generate_with_separator(3, "");
let s: Vec<&str> = passphrase.split(" ").collect();
assert_eq!(s.len(), 1);
}
#[test]
fn test_generate() {
let passphrase = generate_with_separator(3, " ");
let s: Vec<&str> = passphrase.split(' ').collect();
assert_eq!(s.len(), 3);
}
}