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.clone()),
Value::StrArray(a) if a.len() == 1 => Ok(a[0].clone()),
other => Err(format!(
"array:prop => key must be string or single-element StrArray, got {:?}",
other
)),
}
}
fn do_prop(_interp: &mut Interpreter, key: Value, obj: Value) -> Result<Value, String> {
let k = key_from_value(&key)?;
match obj {
Value::KeyedArray(map) => Ok(map.get(&k).cloned().unwrap_or(Value::Placeholder)),
other => Err(format!(
"array:prop => second argument must be a keyed array, got {:?}",
other
)),
}
}
fn make_two_arg_partial(
key_opt: Option<Value>,
obj_opt: Option<Value>,
label: &'static str,
) -> Value {
use FunctionValue::RustClosure;
let closure = move |interp: &mut Interpreter, new_args: Vec<Value>| {
let mut k = key_opt.clone();
let mut o = obj_opt.clone();
for arg in new_args {
if k.is_none() {
if is_placeholder(&arg) {
} else {
k = Some(arg);
}
continue;
}
if o.is_none() {
if is_placeholder(&arg) {
} else {
o = Some(arg);
}
continue;
}
return Err("array:prop => partial => too many arguments".to_string());
}
if let (Some(key_v), Some(obj_v)) = (k.clone(), o.clone()) {
do_prop(interp, key_v, obj_v)
} else {
Ok(make_two_arg_partial(k, o, label))
}
};
Value::Function(Box::new(RustClosure(
label.to_string(),
Arc::new(Mutex::new(closure)),
0,
)))
}
pub fn array_prop_bridge(_interp: &mut Interpreter, args: Vec<Value>) -> Result<Value, String> {
match args.len() {
0 => Ok(make_two_arg_partial(None, None, "array-prop-partial")),
1 => {
let a = &args[0];
if is_placeholder(a) {
Ok(make_two_arg_partial(None, None, "array-prop-partial"))
} else if matches!(a, Value::SingleString(_) | Value::StrArray(_)) {
Ok(make_two_arg_partial(Some(a.clone()), None, "array-prop-partial"))
} else {
Ok(make_two_arg_partial(None, Some(a.clone()), "array-prop-partial"))
}
}
2 => {
let key = &args[0];
let obj = &args[1];
let key_pl = is_placeholder(key);
let obj_pl = is_placeholder(obj);
if key_pl && obj_pl {
return Ok(make_two_arg_partial(None, None, "array-prop-partial"));
}
if key_pl {
return Ok(make_two_arg_partial(None, Some(obj.clone()), "array-prop-partial"));
}
if obj_pl {
return Ok(make_two_arg_partial(Some(key.clone()), None, "array-prop-partial"));
}
do_prop(_interp, key.clone(), obj.clone())
}
n => Err(format!(
"array:prop expects up to 2 arguments (key?, object?), got {}",
n
)),
}
}