use mumu::{
parser::types::FunctionValue::RustClosure,
parser::types::Value,
Interpreter,
};
use std::sync::{Arc, Mutex};
pub fn array_assoc_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);
match a {
Value::Placeholder => Ok(make_three_arg_partial(None, None, None)),
Value::SingleString(_) => Ok(make_three_arg_partial(Some(a), None, None)),
other => Err(format!(
"array:assoc => first param must be string/_ => got {:?}",
other
)),
}
}
2 => {
let a1 = args.remove(0);
let a2 = args.remove(0);
let a1_is_pl = matches!(a1, Value::Placeholder);
if !a1_is_pl && !matches!(a1, Value::SingleString(_)) {
return Err(format!(
"array:assoc => first param must be string/_ => got {:?}",
a1
));
}
let a2_is_pl = matches!(a2, Value::Placeholder);
Ok(make_three_arg_partial(
if a1_is_pl { None } else { Some(a1) },
if a2_is_pl { None } else { Some(a2) },
None,
))
}
3 => {
let a1 = args.remove(0);
let a2 = args.remove(0);
let a3 = args.remove(0);
let a1_pl = matches!(a1, Value::Placeholder);
let a2_pl = matches!(a2, Value::Placeholder);
let a3_pl = matches!(a3, Value::Placeholder);
if !a1_pl && !a2_pl && !a3_pl {
do_assoc_final(a1, a2, a3)
} else {
Ok(make_three_arg_partial(
if a1_pl { None } else { Some(a1) },
if a2_pl { None } else { Some(a2) },
if a3_pl { None } else { Some(a3) },
))
}
}
n => Err(format!("array:assoc => expected up to 3 arguments, got {}", n)),
}
}
fn do_assoc_final(
key_val: Value,
val_val: Value,
obj_val: Value
) -> Result<Value, String> {
let key_str = match key_val {
Value::SingleString(s) => s,
other => {
return Err(format!(
"array:assoc => first param must be string, got {:?}",
other
));
}
};
match obj_val {
Value::KeyedArray(m) => {
let mut new_map = m.clone();
new_map.insert(key_str, val_val);
Ok(Value::KeyedArray(new_map))
}
other => Err(format!(
"array:assoc => final object must be a KeyedArray, got {:?}",
other
)),
}
}
fn make_three_arg_partial(
key_opt: Option<Value>,
val_opt: Option<Value>,
obj_opt: Option<Value>
) -> Value {
let k_clone = key_opt.clone();
let v_clone = val_opt.clone();
let o_clone = obj_opt.clone();
Value::Function(Box::new(RustClosure(
"array:assoc-partial".to_string(),
Arc::new(Mutex::new(move |_interp: &mut Interpreter, new_args: Vec<Value>| {
let mut k_current = k_clone.clone();
let mut v_current = v_clone.clone();
let mut o_current = o_clone.clone();
for arg in new_args {
if k_current.is_none() {
match arg {
Value::Placeholder => {}
Value::SingleString(_) => {
k_current = Some(arg);
}
other => {
return Err(format!(
"array:assoc => first param must be string/_ => got {:?}",
other
));
}
}
continue;
}
if v_current.is_none() {
if matches!(arg, Value::Placeholder) {
} else {
v_current = Some(arg);
}
continue;
}
if o_current.is_none() {
if matches!(arg, Value::Placeholder) {
} else {
o_current = Some(arg);
}
continue;
}
return Err("array:assoc => partial => too many arguments".to_string());
}
if k_current.is_some() && v_current.is_some() && o_current.is_some() {
do_assoc_final(
k_current.as_ref().unwrap().clone(),
v_current.as_ref().unwrap().clone(),
o_current.as_ref().unwrap().clone(),
)
} else {
Ok(make_three_arg_partial(k_current, v_current, o_current))
}
})),
0,
)))
}