mumu-array 0.1.2

Array tools plugin for the MuMu/Lava language
Documentation
use mumu::parser::types::{FunctionValue, Value};
use mumu::parser::interpreter::{Interpreter, apply_n_ary_function_value};
use indexmap::IndexMap;
use super::common::{is_placeholder, make_two_arg_partial};

pub fn array_group_by_bridge(interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
    match args.len() {
        0 => Ok(make_two_arg_partial(do_group_by, None, None, "array-transform-group_by-partial")),
        1 => {
            if is_placeholder(&args[0]) {
                Ok(make_two_arg_partial(do_group_by, None, None, "array-transform-group_by-partial"))
            } else {
                Ok(make_two_arg_partial(do_group_by, Some(args[0].clone()), None, "array-transform-group_by-partial"))
            }
        }
        2 => {
            let a1_pl = is_placeholder(&args[0]);
            let a2_pl = is_placeholder(&args[1]);
            if !a1_pl && !a2_pl {
                do_group_by(interp, args[0].clone(), args[1].clone())
            } else {
                Ok(make_two_arg_partial(
                    do_group_by,
                    if a1_pl { None } else { Some(args[0].clone()) },
                    if a2_pl { None } else { Some(args[1].clone()) },
                    "array-transform-group_by-partial"
                ))
            }
        }
        _ => Err("array:group_by expects 2 arguments (fn, array)".to_string()),
    }
}

fn do_group_by(interp: &mut Interpreter, func: Value, arr: Value) -> Result<Value, String> {
    let func = match func {
        Value::Function(fb) => fb,
        _ => return Err("array:group_by => first argument must be a function".to_string()),
    };
    let items = match arr {
        Value::MixedArray(xs) => xs,
        _ => return Err("array:group_by => second argument must be MixedArray".to_string()),
    };
    let mut groups: IndexMap<String, Vec<Value>> = IndexMap::new();
    for item in items {
        let key_val = apply_n_ary_function_value(interp, func.clone(), vec![item.clone()])?;
        let key_str = match key_val {
            Value::SingleString(s) => s,
            Value::StrArray(a) if a.len() == 1 => a[0].clone(),
            Value::Int(i) => i.to_string(),
            Value::Float(f) => f.to_string(),
            _ => "_".to_string(),
        };
        groups.entry(key_str).or_default().push(item);
    }
    let mut result = IndexMap::new();
    for (k, v) in groups {
        result.insert(k, Value::MixedArray(v));
    }
    Ok(Value::KeyedArray(result))
}