use crate::suite::mark_fail;
use mumu::parser::types::Value;
use mumu::parser::interpreter::Interpreter;
pub fn expect_equal_bridge_fn(_interp: &mut Interpreter, mut args: Vec<Value>) -> Result<Value, String> {
if args.len() != 2 {
return Err(format!("expect_equal => expected 2 args, got {}", args.len()));
}
let actual = args.remove(0);
let expected = args.remove(0);
if let Err(msg) = values_equal(&actual, &expected) {
mark_fail(&format!("expect_equal fail: actual={:?}, expected={:?}\n{}", actual, expected, msg));
}
Ok(Value::Bool(true))
}
pub fn expect_not_equal_bridge_fn(_interp: &mut Interpreter, mut args: Vec<Value>) -> Result<Value, String> {
if args.len() != 2 {
return Err(format!("expect_not_equal => expected 2 args, got {}", args.len()));
}
let actual = args.remove(0);
let expected = args.remove(0);
if values_equal(&actual, &expected).is_ok() {
mark_fail(&format!("expect_not_equal fail: both are {:?}", actual));
}
Ok(Value::Bool(true))
}
pub fn has_key_bridge_fn(_interp: &mut Interpreter, mut args: Vec<Value>) -> Result<Value, String> {
if args.len() != 2 {
return Err(format!("has_key => expected 2 args, got {}", args.len()));
}
let subject = args.remove(0);
let key_val = args.remove(0);
let map = match subject {
Value::KeyedArray(m) => m,
other => {
mark_fail(&format!("has_key => subject not keyed array, got {:?}", other));
return Ok(Value::Bool(true));
}
};
let key_str = match key_val {
Value::SingleString(s) => s,
Value::StrArray(ss) if ss.len() == 1 => ss[0].clone(),
other => {
mark_fail(&format!("has_key => key must be single string, got {:?}", other));
return Ok(Value::Bool(true));
}
};
if !map.contains_key(&key_str) {
mark_fail(&format!("has_key => missing key '{}'", key_str));
}
Ok(Value::Bool(true))
}
pub fn prop_equals_bridge_fn(_interp: &mut Interpreter, mut args: Vec<Value>) -> Result<Value, String> {
if args.len() != 3 {
return Err(format!("prop_equals => expected 3 args, got {}", args.len()));
}
let subject = args.remove(0);
let key_val = args.remove(0);
let expected_val = args.remove(0);
let map = match subject {
Value::KeyedArray(m) => m,
other => {
mark_fail(&format!("prop_equals => subject is not keyed, got {:?}", other));
return Ok(Value::Bool(true));
}
};
let key_str = match key_val {
Value::SingleString(s) => s,
Value::StrArray(sa) if sa.len() == 1 => sa[0].clone(),
other => {
mark_fail(&format!("prop_equals => key must be single string, got {:?}", other));
return Ok(Value::Bool(true));
}
};
match map.get(&key_str) {
Some(actual_val) => {
if values_equal(actual_val, &expected_val).is_err() {
mark_fail(&format!(
"prop_equals => mismatch for '{}': got={:?}, expected={:?}",
key_str, actual_val, expected_val
));
}
}
None => {
mark_fail(&format!("prop_equals => missing key '{}'", key_str));
}
}
Ok(Value::Bool(true))
}
pub fn values_equal(a: &Value, b: &Value) -> Result<(), String> {
match (a, b) {
(Value::Int(x), Value::Int(y)) => if x == y { Ok(()) } else { Err(format!("Int {} != {}", x, y)) },
(Value::Long(x), Value::Long(y)) => if x == y { Ok(()) } else { Err(format!("Long {} != {}", x, y)) },
(Value::Bool(x), Value::Bool(y)) => if x == y { Ok(()) } else { Err(format!("Bool {} != {}", x, y)) },
(Value::SingleString(x), Value::SingleString(y)) => if x == y { Ok(()) } else { Err(format!("String {:?} != {:?}", x, y)) },
(Value::Float(x), Value::Float(y)) => {
if (x - y).abs() <= 1e-9 || (x.is_nan() && y.is_nan()) {
Ok(())
} else {
Err(format!("Float {} != {} (eps=1e-9)", x, y))
}
}
(Value::IntArray(xs), Value::IntArray(ys)) => {
if xs.len() != ys.len() {
return Err(format!("IntArray length {} != {}", xs.len(), ys.len()));
}
for (i, (x, y)) in xs.iter().zip(ys).enumerate() {
if x != y {
return Err(format!("IntArray[{}] {} != {}", i, x, y));
}
}
Ok(())
}
(Value::FloatArray(xs), Value::FloatArray(ys)) => {
if xs.len() != ys.len() {
return Err(format!("FloatArray length {} != {}", xs.len(), ys.len()));
}
for (i, (x, y)) in xs.iter().zip(ys).enumerate() {
if (x - y).abs() > 1e-9 && !(x.is_nan() && y.is_nan()) {
return Err(format!("FloatArray[{}] {} != {} (eps=1e-9)", i, x, y));
}
}
Ok(())
}
(Value::StrArray(xs), Value::StrArray(ys)) => {
if xs.len() != ys.len() {
return Err(format!("StrArray length {} != {}", xs.len(), ys.len()));
}
for (i, (x, y)) in xs.iter().zip(ys).enumerate() {
if x != y {
return Err(format!("StrArray[{}] {:?} != {:?}", i, x, y));
}
}
Ok(())
}
(Value::BoolArray(xs), Value::BoolArray(ys)) => {
if xs.len() != ys.len() {
return Err(format!("BoolArray length {} != {}", xs.len(), ys.len()));
}
for (i, (x, y)) in xs.iter().zip(ys).enumerate() {
if x != y {
return Err(format!("BoolArray[{}] {} != {}", i, x, y));
}
}
Ok(())
}
(Value::MixedArray(xs), Value::MixedArray(ys)) => {
if xs.len() != ys.len() {
return Err(format!("MixedArray length {} != {}", xs.len(), ys.len()));
}
for (i, (x, y)) in xs.iter().zip(ys).enumerate() {
if let Err(e) = values_equal(x, y) {
return Err(format!("MixedArray[{}] failed: {}", i, e));
}
}
Ok(())
}
(Value::KeyedArray(mx), Value::KeyedArray(my)) => {
if mx.len() != my.len() {
return Err(format!("KeyedArray len {} != {}", mx.len(), my.len()));
}
for (k, vx) in mx {
if let Some(vy) = my.get(k) {
if let Err(e) = values_equal(vx, vy) {
return Err(format!("KeyedArray[{:?}] failed: {}", k, e));
}
} else {
return Err(format!("KeyedArray missing key {:?}", k));
}
}
Ok(())
}
(Value::Int2DArray(xs), Value::Int2DArray(ys)) => {
if xs.len() != ys.len() {
return Err(format!("Int2DArray outer len {} != {}", xs.len(), ys.len()));
}
for (i, (r1, r2)) in xs.iter().zip(ys).enumerate() {
if r1.len() != r2.len() {
return Err(format!("Int2DArray row {} len {} != {}", i, r1.len(), r2.len()));
}
for (j, (x, y)) in r1.iter().zip(r2).enumerate() {
if x != y {
return Err(format!("Int2DArray[{}][{}] {} != {}", i, j, x, y));
}
}
}
Ok(())
}
(Value::Float2DArray(xs), Value::Float2DArray(ys)) => {
if xs.len() != ys.len() {
return Err(format!("Float2DArray outer len {} != {}", xs.len(), ys.len()));
}
for (i, (r1, r2)) in xs.iter().zip(ys).enumerate() {
if r1.len() != r2.len() {
return Err(format!("Float2DArray row {} len {} != {}", i, r1.len(), r2.len()));
}
for (j, (x, y)) in r1.iter().zip(r2).enumerate() {
if (x - y).abs() > 1e-9 && !(x.is_nan() && y.is_nan()) {
return Err(format!("Float2DArray[{}][{}] {} != {}", i, j, x, y));
}
}
}
Ok(())
}
_ => if a == b { Ok(()) } else { Err(format!("actual={:?}, expected={:?} (no deep match)", a, b)) },
}
}