use mumu::{
parser::interpreter::Interpreter,
parser::types::{FunctionValue, Value},
};
use std::sync::{Arc, Mutex};
const RAW_DICEWARE: &str = include_str!("diceware_words.txt");
fn get_diceware_words() -> &'static [&'static str] {
use std::sync::OnceLock;
static WORDS: OnceLock<Vec<&'static str>> = OnceLock::new();
WORDS.get_or_init(|| {
RAW_DICEWARE
.lines()
.filter_map(|line| line.split_once('\t').map(|(_, word)| word.trim()))
.collect()
})
}
fn diceware_bridge_fn(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
let delimiter = match args.len() {
0 => "-",
1 => match &args[0] {
Value::SingleString(s) if s.chars().count() == 1 => s,
Value::StrArray(a) if a.len() == 1 && a[0].chars().count() == 1 => &a[0],
other => {
return Err(format!(
"test:diceware => delimiter must be a single character string, got {:?}", other
))
}
},
n => return Err(format!("test:diceware => expected 0 or 1 argument, got {n}")),
};
let words = get_diceware_words();
if words.len() < 3 {
return Err("Diceware wordlist too short!".into());
}
let mut parts = Vec::with_capacity(3);
for _ in 0..3 {
let idx = fastrand::usize(..words.len());
parts.push(words[idx]);
}
let passphrase = parts.join(delimiter);
Ok(Value::SingleString(passphrase))
}
pub fn register_diceware(interp: &mut Interpreter) {
let fn_arc = Arc::new(Mutex::new(diceware_bridge_fn));
interp.register_dynamic_function("test:diceware", fn_arc);
interp.set_variable(
"test:diceware",
Value::Function(Box::new(FunctionValue::Named("test:diceware".to_string())))
);
}