1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
use serde_json::Value;
use super::{logic, Data, Expression};
pub fn compute(args: &[Expression], data: &Data) -> Value {
match args.len() {
// Return the condition, for whatever reason.
0..=1 => args
.get(0)
.map(|arg| arg.compute(data))
.unwrap_or(Value::Null),
// Normal if/then/else, with default null.
2..=3 => {
let condition = args
.get(0)
.map(|arg| arg.compute(data))
.unwrap_or(Value::Null);
if logic::is_truthy(&condition) {
args.get(1).map(|arg| arg.compute(data)).unwrap()
} else {
args.get(2)
.map(|arg| arg.compute(data))
.unwrap_or(Value::Null)
}
}
// Now the arguments are pairs of condition and then value. The last argument is the
// else value.
// TODO: Actually the logic of this arm computes the other cases properly. Test whether
// the short circuit cases have a performance benefit or not.
_ => {
let mut args = args.iter();
loop {
let condition_or_else_val = args
.next()
.map(|arg| arg.compute(data))
.unwrap_or(Value::Null);
let then_val = args.next();
match then_val {
// The value behind arg1 is the last argument to the if operator, which is
// the else argument. Since we come until here, no other (else-)if condition
// was truthy. Therefore return the value of arg1.
None => return condition_or_else_val,
// If the condition (arg1) is truthy, return the then value (arg2).
// Otherwise just continue with the next pair.
Some(then_val) => {
if logic::is_truthy(&condition_or_else_val) {
return then_val.compute(data);
}
}
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::compute_const;
use serde_json::json;
#[test]
fn simple() {
assert_eq!(compute_const!(), Value::Null);
assert_eq!(compute_const!(Value::Null), Value::Null);
}
#[test]
fn one_arg() {
assert_eq!(compute_const!(json!(true)), json!(true));
assert_eq!(compute_const!(json!(false)), json!(false));
assert_eq!(compute_const!(json!("foo")), json!("foo"));
}
#[test]
fn two_args() {
assert_eq!(compute_const!(json!(true), json!(5)), json!(5));
assert_eq!(compute_const!(json!(false), json!(5)), json!(null));
assert_eq!(compute_const!(json!("foo"), json!("bar")), json!("bar"));
}
#[test]
fn three_args() {
assert_eq!(compute_const!(json!(true), json!(5), json!(6)), json!(5));
assert_eq!(compute_const!(json!(false), json!(5), json!(6)), json!(6));
}
#[test]
fn more() {
assert_eq!(
compute_const!(json!(false), json!(5), json!(true), json!(6)),
json!(6)
);
assert_eq!(
compute_const!(json!(false), json!(5), json!(false), json!(6)),
json!(null)
);
assert_eq!(
compute_const!(json!(false), json!(5), json!(false), json!(6), json!(7)),
json!(7)
);
assert_eq!(
compute_const!(
json!(false),
json!(5),
json!(false),
json!(6),
json!(7),
json!(8)
),
json!(8)
);
}
}