use mumu::parser::interpreter::Interpreter;
use mumu::parser::types::{FunctionValue, Value};
use std::sync::{Arc, Mutex};
fn is_placeholder(v: &Value) -> bool {
matches!(v, Value::Placeholder)
|| matches!(v, Value::SingleString(s) if s == "_")
|| matches!(v, Value::StrArray(ss) if ss.len() == 1 && ss[0] == "_")
}
fn key_from_value(v: Value) -> Result<String, String> {
match v {
Value::SingleString(s) => Ok(s),
Value::StrArray(a) if a.len() == 1 => Ok(a[0].clone()),
other => Err(format!(
"array:key_eq => key must be string or single-element StrArray, got {:?}",
other
)),
}
}
fn key_eq_finalize(expected: Value, key_val: Value, obj: Value) -> Result<Value, String> {
let key = key_from_value(key_val)?;
match obj {
Value::KeyedArray(map) => {
if let Some(found) = map.get(&key) {
Ok(Value::Bool(found == &expected))
} else {
Ok(Value::Bool(false))
}
}
other => Err(format!(
"array:key_eq => third argument must be a keyed array, got {:?}",
other
)),
}
}
fn make_three_arg_partial(
expected_opt: Option<Value>,
key_opt: Option<Value>,
obj_opt: Option<Value>,
) -> Value {
use FunctionValue::RustClosure;
let closure = move |_interp: &mut Interpreter, new_args: Vec<Value>| {
let mut ev = expected_opt.clone();
let mut kv = key_opt.clone();
let mut ov = obj_opt.clone();
for arg in new_args {
if ev.is_none() {
if !is_placeholder(&arg) {
ev = Some(arg);
}
continue;
}
if kv.is_none() {
if !is_placeholder(&arg) {
kv = Some(arg);
}
continue;
}
if ov.is_none() {
if !is_placeholder(&arg) {
ov = Some(arg);
}
continue;
}
return Err("array:key_eq => partial => too many arguments".to_string());
}
if let (Some(e), Some(k), Some(o)) = (ev.clone(), kv.clone(), ov.clone()) {
key_eq_finalize(e, k, o)
} else {
Ok(make_three_arg_partial(ev, kv, ov))
}
};
Value::Function(Box::new(RustClosure(
"array:key_eq-partial".to_string(),
Arc::new(Mutex::new(closure)),
0,
)))
}
pub fn array_key_eq_bridge(_interp: &mut Interpreter, mut args: Vec<Value>) -> Result<Value, String> {
match args.len() {
0 => Ok(make_three_arg_partial(None, None, None)),
1 => {
let a = args.remove(0);
if is_placeholder(&a) {
Ok(make_three_arg_partial(None, None, None))
} else {
Ok(make_three_arg_partial(Some(a), None, None))
}
}
2 => {
let a = args.remove(0);
let b = args.remove(0);
Ok(make_three_arg_partial(
if is_placeholder(&a) { None } else { Some(a) },
if is_placeholder(&b) { None } else { Some(b) },
None,
))
}
3 => {
let e = args.remove(0);
let k = args.remove(0);
let o = args.remove(0);
if is_placeholder(&e) || is_placeholder(&k) || is_placeholder(&o) {
return Ok(make_three_arg_partial(
if is_placeholder(&e) { None } else { Some(e) },
if is_placeholder(&k) { None } else { Some(k) },
if is_placeholder(&o) { None } else { Some(o) },
));
}
key_eq_finalize(e, k, o)
}
n => Err(format!(
"array:key_eq => expected up to 3 arguments (value, key, object), got {}",
n
)),
}
}