test-mumu 0.1.10

Test suite plugin for the Lava language
Documentation
// src/diceware.rs

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())))
    );
}