mumu-array 0.1.2

Array tools plugin for the MuMu/Lava language
Documentation
// array/src/prop.rs

use mumu::parser::types::Value;
use mumu::parser::interpreter::Interpreter;
use std::sync::{Arc, Mutex};

pub fn array_prop_bridge(
    _interp: &mut Interpreter,
    args: Vec<Value>
) -> Result<Value, String> {
    match args.len() {
        0 => Ok(make_prop_partial(None, None)),
        1 => match &args[0] {
            Value::KeyedArray(_) => Ok(make_prop_partial(None, Some(args[0].clone()))),
            Value::Placeholder => Ok(make_prop_partial(None, None)),
            _ => Ok(make_prop_partial(Some(args[0].clone()), None)),
        },
        2 => {
            let a1_pl = matches!(&args[0], Value::Placeholder);
            let a2_pl = matches!(&args[1], Value::Placeholder);
            if a1_pl && a2_pl {
                return Ok(make_prop_partial(None, None));
            }
            if a1_pl {
                return Ok(make_prop_partial(None, Some(args[1].clone())));
            }
            if a2_pl {
                return Ok(make_prop_partial(Some(args[0].clone()), None));
            }
            // No placeholders: full application
            do_prop_lookup(&args[0], &args[1])
        }
        _ => Err("array:prop expects at most two arguments".to_string()),
    }
}

fn make_prop_partial(
    key_opt: Option<Value>,
    arr_opt: Option<Value>
) -> Value {
    use mumu::parser::types::FunctionValue::RustClosure;
    let closure = move |_interp: &mut Interpreter, new_args: Vec<Value>| {
        let mut k = key_opt.clone();
        let mut a = arr_opt.clone();
        for arg in new_args {
            if k.is_none() && !matches!(arg, Value::Placeholder) {
                k = Some(arg);
                continue;
            }
            if a.is_none() && !matches!(arg, Value::Placeholder) {
                a = Some(arg);
                continue;
            }
            return Err("array:prop partial expects only a missing key or object".to_string());
        }
        if k.is_some() && a.is_some() {
            do_prop_lookup(&k.unwrap(), &a.unwrap())
        } else {
            Ok(make_prop_partial(k, a))
        }
    };
    Value::Function(Box::new(RustClosure(
        "array:prop-partial".to_string(),
        Arc::new(Mutex::new(closure)),
        0,
    )))
}

fn do_prop_lookup(key_val: &Value, arr_val: &Value) -> Result<Value, String> {
    let key = match key_val {
        Value::SingleString(s) => s,
        Value::StrArray(ss) if ss.len() == 1 => &ss[0],
        _ => return Err("array:prop expects a string key as the first argument".to_string()),
    };
    match arr_val {
        Value::KeyedArray(map) => map.get(key).cloned().ok_or_else(|| format!("Key '{}' not found", key)),
        _ => Err("array:prop expects keyed array as the second argument".to_string()),
    }
}