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
117
118
119
120
121
122
123
124
125
use serde_json::Value;
use super::{Data, Expression, PartialResult};
/// Takes an array of data keys. Returns an array of any keys missing from the data object.
///
/// Can also receive 1 argument that is an array of keys, which typically happens if it's actually
/// acting on the output of another command (like 'if' or 'merge').
/// See https://github.com/jwadhams/json-logic-js/blob/a15f528919346f2ec7d82bd4fc91c41481546c01/logic.js#L145
pub fn compute(args: &[Expression], data: &Data) -> Value {
let mut result: Vec<Value> = vec![];
let mut args = args.iter().map(|arg| arg.compute(data));
// The list of keys to look up is either the first argument if that is an array or the list
// of all arguments otherwise.
let first = args.next();
let keys = match first {
// The first argument is an array, so use its values as keys.
Some(Value::Array(arr)) => arr,
// The first argument is something else, so interpret the arguments as keys.
Some(first) => {
let mut keys = Vec::with_capacity(args.len());
keys.push(first);
keys.append(&mut args.collect());
keys
}
// No argument, return an empty array.
_ => return Value::Array(result),
};
for key in keys.iter() {
// TODO: Even tough we only look for the existence, the value to the key will be cloned.
// Something like Data::has_value without cloning would help.
if data.get_value(key).is_none() {
result.push(key.clone());
}
}
Value::Array(result)
}
// early returns on finding any Ambiguous arg
pub fn partial_compute(args: &[Expression], data: &Data) -> PartialResult {
let mut result: Vec<Value> = vec![];
let mut args = args
.into_iter()
.map(|arg| arg.partial_compute(data))
.collect::<Result<Vec<Value>, _>>()?
.into_iter();
// The list of keys to look up is either the first argument if that is an array or the list
// of all arguments otherwise.
let first = args.next();
let keys = match first {
// The first argument is an array, so use its values as keys.
Some(Value::Array(arr)) => arr,
// The first argument is something else, so interpret the arguments as keys.
Some(first) => {
let mut keys = Vec::with_capacity(args.len());
keys.push(first);
keys.append(&mut args.collect());
keys
}
// No argument, return an empty array.
_ => return Ok(Value::Array(result)),
};
for key in keys.iter() {
// TODO: Even tough we only look for the existence, the value to the key will be cloned.
// Something like Data::has_value without cloning would help.
if data.get_value(key).is_none() {
result.push(key.clone());
}
}
Ok(Value::Array(result))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::compute_const_with_data;
use serde_json::json;
#[test]
fn tests() {
let data_json = json!({ "a": 5, "b": "foo" });
let data = &Data::from_json(&data_json);
assert_eq!(compute_const_with_data!(&[], data), json!([]));
assert_eq!(
compute_const_with_data!(&[json!("bar")], data),
json!(["bar"])
);
assert_eq!(compute_const_with_data!(&[json!("a")], data), json!([]));
assert_eq!(
compute_const_with_data!(&[json!("a"), json!("b")], data),
json!([])
);
assert_eq!(
compute_const_with_data!(&[json!("a"), json!("b"), json!("c")], data),
json!(["c"])
);
}
#[test]
fn first_arg_is_array() {
let data_json = json!({ "a": 5, "b": "foo" });
let data = &Data::from_json(&data_json);
assert_eq!(
compute_const_with_data!(&[json!(["bar"])], data),
json!(["bar"])
);
assert_eq!(compute_const_with_data!(&[json!(["a"])], data), json!([]));
assert_eq!(
compute_const_with_data!(&[json!(["a", "b"])], data),
json!([])
);
assert_eq!(
compute_const_with_data!(&[json!(["a", "b", "c"])], data),
json!(["c"])
);
}
}