1use crate::suite::mark_fail;
2use mumu::parser::types::Value;
3use mumu::parser::interpreter::Interpreter;
4
5pub fn expect_equal_bridge_fn(_interp: &mut Interpreter, mut args: Vec<Value>) -> Result<Value, String> {
6 if args.len() != 2 {
7 return Err(format!("expect_equal => expected 2 args, got {}", args.len()));
8 }
9 let actual = args.remove(0);
10 let expected = args.remove(0);
11
12 if let Err(msg) = values_equal(&actual, &expected) {
13 mark_fail(&format!("expect_equal fail: actual={:?}, expected={:?}\n{}", actual, expected, msg));
14 }
15 Ok(Value::Bool(true))
16}
17
18pub fn expect_not_equal_bridge_fn(_interp: &mut Interpreter, mut args: Vec<Value>) -> Result<Value, String> {
19 if args.len() != 2 {
20 return Err(format!("expect_not_equal => expected 2 args, got {}", args.len()));
21 }
22 let actual = args.remove(0);
23 let expected = args.remove(0);
24
25 if values_equal(&actual, &expected).is_ok() {
26 mark_fail(&format!("expect_not_equal fail: both are {:?}", actual));
27 }
28 Ok(Value::Bool(true))
29}
30
31pub fn has_key_bridge_fn(_interp: &mut Interpreter, mut args: Vec<Value>) -> Result<Value, String> {
32 if args.len() != 2 {
33 return Err(format!("has_key => expected 2 args, got {}", args.len()));
34 }
35 let subject = args.remove(0);
36 let key_val = args.remove(0);
37
38 let map = match subject {
39 Value::KeyedArray(m) => m,
40 other => {
41 mark_fail(&format!("has_key => subject not keyed array, got {:?}", other));
42 return Ok(Value::Bool(true));
43 }
44 };
45 let key_str = match key_val {
46 Value::SingleString(s) => s,
47 Value::StrArray(ss) if ss.len() == 1 => ss[0].clone(),
48 other => {
49 mark_fail(&format!("has_key => key must be single string, got {:?}", other));
50 return Ok(Value::Bool(true));
51 }
52 };
53
54 if !map.contains_key(&key_str) {
55 mark_fail(&format!("has_key => missing key '{}'", key_str));
56 }
57 Ok(Value::Bool(true))
58}
59
60pub fn prop_equals_bridge_fn(_interp: &mut Interpreter, mut args: Vec<Value>) -> Result<Value, String> {
61 if args.len() != 3 {
62 return Err(format!("prop_equals => expected 3 args, got {}", args.len()));
63 }
64 let subject = args.remove(0);
65 let key_val = args.remove(0);
66 let expected_val = args.remove(0);
67
68 let map = match subject {
69 Value::KeyedArray(m) => m,
70 other => {
71 mark_fail(&format!("prop_equals => subject is not keyed, got {:?}", other));
72 return Ok(Value::Bool(true));
73 }
74 };
75 let key_str = match key_val {
76 Value::SingleString(s) => s,
77 Value::StrArray(sa) if sa.len() == 1 => sa[0].clone(),
78 other => {
79 mark_fail(&format!("prop_equals => key must be single string, got {:?}", other));
80 return Ok(Value::Bool(true));
81 }
82 };
83
84 match map.get(&key_str) {
85 Some(actual_val) => {
86 if values_equal(actual_val, &expected_val).is_err() {
87 mark_fail(&format!(
88 "prop_equals => mismatch for '{}': got={:?}, expected={:?}",
89 key_str, actual_val, expected_val
90 ));
91 }
92 }
93 None => {
94 mark_fail(&format!("prop_equals => missing key '{}'", key_str));
95 }
96 }
97
98 Ok(Value::Bool(true))
99}
100
101pub fn values_equal(a: &Value, b: &Value) -> Result<(), String> {
103 match (a, b) {
104 (Value::Int(x), Value::Int(y)) => if x == y { Ok(()) } else { Err(format!("Int {} != {}", x, y)) },
105 (Value::Long(x), Value::Long(y)) => if x == y { Ok(()) } else { Err(format!("Long {} != {}", x, y)) },
106 (Value::Bool(x), Value::Bool(y)) => if x == y { Ok(()) } else { Err(format!("Bool {} != {}", x, y)) },
107 (Value::SingleString(x), Value::SingleString(y)) => if x == y { Ok(()) } else { Err(format!("String {:?} != {:?}", x, y)) },
108 (Value::Float(x), Value::Float(y)) => {
109 if (x - y).abs() <= 1e-9 || (x.is_nan() && y.is_nan()) {
110 Ok(())
111 } else {
112 Err(format!("Float {} != {} (eps=1e-9)", x, y))
113 }
114 }
115 (Value::IntArray(xs), Value::IntArray(ys)) => {
116 if xs.len() != ys.len() {
117 return Err(format!("IntArray length {} != {}", xs.len(), ys.len()));
118 }
119 for (i, (x, y)) in xs.iter().zip(ys).enumerate() {
120 if x != y {
121 return Err(format!("IntArray[{}] {} != {}", i, x, y));
122 }
123 }
124 Ok(())
125 }
126 (Value::FloatArray(xs), Value::FloatArray(ys)) => {
127 if xs.len() != ys.len() {
128 return Err(format!("FloatArray length {} != {}", xs.len(), ys.len()));
129 }
130 for (i, (x, y)) in xs.iter().zip(ys).enumerate() {
131 if (x - y).abs() > 1e-9 && !(x.is_nan() && y.is_nan()) {
132 return Err(format!("FloatArray[{}] {} != {} (eps=1e-9)", i, x, y));
133 }
134 }
135 Ok(())
136 }
137 (Value::StrArray(xs), Value::StrArray(ys)) => {
138 if xs.len() != ys.len() {
139 return Err(format!("StrArray length {} != {}", xs.len(), ys.len()));
140 }
141 for (i, (x, y)) in xs.iter().zip(ys).enumerate() {
142 if x != y {
143 return Err(format!("StrArray[{}] {:?} != {:?}", i, x, y));
144 }
145 }
146 Ok(())
147 }
148 (Value::BoolArray(xs), Value::BoolArray(ys)) => {
149 if xs.len() != ys.len() {
150 return Err(format!("BoolArray length {} != {}", xs.len(), ys.len()));
151 }
152 for (i, (x, y)) in xs.iter().zip(ys).enumerate() {
153 if x != y {
154 return Err(format!("BoolArray[{}] {} != {}", i, x, y));
155 }
156 }
157 Ok(())
158 }
159 (Value::MixedArray(xs), Value::MixedArray(ys)) => {
160 if xs.len() != ys.len() {
161 return Err(format!("MixedArray length {} != {}", xs.len(), ys.len()));
162 }
163 for (i, (x, y)) in xs.iter().zip(ys).enumerate() {
164 if let Err(e) = values_equal(x, y) {
165 return Err(format!("MixedArray[{}] failed: {}", i, e));
166 }
167 }
168 Ok(())
169 }
170 (Value::KeyedArray(mx), Value::KeyedArray(my)) => {
171 if mx.len() != my.len() {
172 return Err(format!("KeyedArray len {} != {}", mx.len(), my.len()));
173 }
174 for (k, vx) in mx {
175 if let Some(vy) = my.get(k) {
176 if let Err(e) = values_equal(vx, vy) {
177 return Err(format!("KeyedArray[{:?}] failed: {}", k, e));
178 }
179 } else {
180 return Err(format!("KeyedArray missing key {:?}", k));
181 }
182 }
183 Ok(())
184 }
185 (Value::Int2DArray(xs), Value::Int2DArray(ys)) => {
186 if xs.len() != ys.len() {
187 return Err(format!("Int2DArray outer len {} != {}", xs.len(), ys.len()));
188 }
189 for (i, (r1, r2)) in xs.iter().zip(ys).enumerate() {
190 if r1.len() != r2.len() {
191 return Err(format!("Int2DArray row {} len {} != {}", i, r1.len(), r2.len()));
192 }
193 for (j, (x, y)) in r1.iter().zip(r2).enumerate() {
194 if x != y {
195 return Err(format!("Int2DArray[{}][{}] {} != {}", i, j, x, y));
196 }
197 }
198 }
199 Ok(())
200 }
201 (Value::Float2DArray(xs), Value::Float2DArray(ys)) => {
202 if xs.len() != ys.len() {
203 return Err(format!("Float2DArray outer len {} != {}", xs.len(), ys.len()));
204 }
205 for (i, (r1, r2)) in xs.iter().zip(ys).enumerate() {
206 if r1.len() != r2.len() {
207 return Err(format!("Float2DArray row {} len {} != {}", i, r1.len(), r2.len()));
208 }
209 for (j, (x, y)) in r1.iter().zip(r2).enumerate() {
210 if (x - y).abs() > 1e-9 && !(x.is_nan() && y.is_nan()) {
211 return Err(format!("Float2DArray[{}][{}] {} != {}", i, j, x, y));
212 }
213 }
214 }
215 Ok(())
216 }
217 _ => if a == b { Ok(()) } else { Err(format!("actual={:?}, expected={:?} (no deep match)", a, b)) },
218 }
219}
220