jsonlogic/operators/mod.rs
1mod test_helper;
2
3mod addition;
4mod all;
5mod and;
6mod cat;
7mod division;
8mod double_negation;
9mod equality;
10mod filter;
11mod greater_equal_than;
12mod greater_than;
13mod if_else;
14mod is_in;
15mod less_equal_than;
16mod less_than;
17mod log;
18mod logic;
19mod map;
20mod max;
21mod merge;
22mod min;
23mod missing;
24mod missing_some;
25mod modulo;
26mod multiplication;
27mod negation;
28mod none;
29mod not_equal;
30mod or;
31mod reduce;
32mod some;
33mod strict_equality;
34mod strict_not_equal;
35mod substr;
36mod subtraction;
37mod variable;
38
39use serde_json::Value;
40
41use super::expression::Expression;
42use super::Data;
43
44/// Represents a JsonLogic operator.
45#[derive(Debug, PartialEq, Copy, Clone)]
46pub enum Operator {
47 /// Tests abstract equality as specified in
48 /// https://www.ecma-international.org/ecma-262/#sec-abstract-equality-comparison, with type
49 /// coercion. Requires two arguments.
50 Equal,
51 /// Tests strict equality. Requires two arguments.
52 StrictEqual,
53 /// Tests not-equal, with type coercion.
54 NotEqual,
55 /// Tests strict not-equal.
56 StrictNotEqual,
57 /// Retrieve data from the provided data object.
58 ///
59 /// If the first argument is null, the data object is returned as is.
60 Variable,
61 /// Logical negation (“not”). Takes just one argument.
62 Negation,
63 /// Double negation, or “cast to a boolean.” Takes a single argument.
64 DoubleNegation,
65 /// The if statement typically takes 3 arguments: a condition (if), what to do if it’s true
66 /// (then), and what to do if it’s false (else). If can also take more than 3 arguments, and
67 /// will pair up arguments like if/then elseif/then elseif/then else.
68 If,
69 /// Takes an arbitrary number of arguments. Returns the first truthy argument or the last
70 /// argument.
71 Or,
72 /// Takes an arbitrary number of arguments. Returns the first falsy argument or the last
73 /// argument.
74 And,
75 /// Less than. Takes exactly 2 arguments, otherwise returns `false`.
76 LessThan,
77 /// Less or equal than. Takes exactly 2 arguments, otherwise returns `false`.
78 LessEqualThan,
79 /// Greater than. Takes exactly 2 arguments, otherwise returns `false`.
80 GreaterThan,
81 /// Greater or equal than. Takes exactly 2 arguments, otherwise returns `false`.
82 GreaterEqualThan,
83 /// Takes an array of data keys to search for (same format as `var`).Returns an array of any
84 /// keys that are missing from the data object, or an empty array.
85 ///
86 /// Can also receive 1 argument that is an array of keys, which typically happens if it's
87 /// actually acting on the output of another command (like 'if' or 'merge').
88 /// See https://github.com/jwadhams/json-logic-js/blob/a15f528919346f2ec7d82bd4fc91c41481546c01/logic.js#L145
89 Missing,
90 /// Takes a minimum number of data keys that are required, and an array of keys to search for
91 /// (same format as `var` or `missing`). Returns an empty array if the minimum is met, or an
92 /// array of the missing keys otherwise.
93 MissingSome,
94 /// Return the minimum from a list of values. Matches the javascript `Math.min` implementation.
95 Min,
96 /// Return the maximum from a list of values. Matches the javascript `Math.max` implementation.
97 Max,
98 /// +, takes an arbitrary number of arguments and sums them up. If just one argument is passed,
99 /// it will be cast to a number. Returns `Value::Null` if one argument cannot be coerced into a
100 /// number.
101 Addition,
102 /// -, if just one argument is passed, it gets negated.
103 Subtraction,
104 /// *, takes an arbitrary number of arguments and multiplicates them.
105 Multiplication,
106 /// /
107 Division,
108 /// %, Finds the remainder after the first argument is divided by the second argument.
109 Modulo,
110 /// Expects two arguments. Tests either for substring or whether an array contains an element.
111 ///
112 /// If the second argument is an array, tests that the first argument is a member of the array.
113 ///
114 /// If the second argument is a string, tests that the first argument is a substring.
115 In,
116 /// Concatenate all the supplied arguments. Note that this is not a join or implode operation,
117 /// there is no "glue" string.
118 Cat,
119 /// Gets a portion of a string. Takes two to three arguments.
120 ///
121 /// The first argument is a string. Any other value will be coerced into a string.
122 ///
123 /// The second argument is a number (or will be coerced to a number, default to 0) and is the
124 /// start position to return everything beginning at that index. Give a negative start position
125 /// to work from the end of the string and then return the rest.
126 ///
127 /// The third argument limits the length of the returned substring. Give a negative index to
128 /// stop that many characters before the end.
129 Substr,
130 /// Logs the first value to console, then passes it through unmodified.
131 Log,
132 /// Takes one or more arrays, and merges them into one array. If arguments aren’t arrays, they
133 /// get cast to arrays.
134 Merge,
135 /// You can use `map` to perform an action on every member of an array. Note, that inside the
136 /// logic being used to map, var operations are relative to the array element being worked on.
137 Map,
138 /// You can use `filter` to keep only elements of the array that pass a test. Note, that inside
139 /// the logic being used to map, var operations are relative to the array element being worked
140 /// on.
141 ///
142 /// Also note, the returned array will have contiguous indexes starting at zero (typical for
143 /// JavaScript, Python and Ruby) it will not preserve the source indexes (making it unlike
144 /// PHP’s array_filter).
145 Filter,
146 /// You can use `reduce` to combine all the elements in an array into a single value, like adding
147 /// up a list of numbers. Note, that inside the logic being used to reduce, var operations only
148 /// have access to an object like:
149 ///
150 /// ```ignore
151 /// {
152 /// "current" : // this element of the array,
153 /// "accumulator" : // progress so far, or the initial value
154 /// }
155 /// ```
156 Reduce,
157 /// Takes an array as the first argument and a condition as the second argument. Returns `true`
158 /// if the condition evaluates to a truthy value for each element of the first parameter.
159 ///
160 /// `var` operations inside the second argument expression are relative to the array element
161 /// being tested.
162 All,
163 /// Takes an array as the first argument and a condition as the second argument. Returns `true`
164 /// if the condition evaluates to a truthy value for at least one element of the first
165 /// parameter.
166 ///
167 /// `var` operations inside the second argument expression are relative to the array element
168 /// being tested.
169 Some,
170 /// Takes an array as the first argument and a condition as the second argument. Returns `true`
171 /// if the condition evaluates to a falsy value for each element of the first parameter.
172 ///
173 /// `var` operations inside the second argument expression are relative to the array element
174 /// being tested.
175 None,
176}
177
178impl Operator {
179 /// Returns the Operator matching the given string representation. Returns None if the given
180 /// string matches no known operator.
181 pub fn from_str(s: &str) -> Option<Operator> {
182 match s {
183 "==" => Some(Operator::Equal),
184 "===" => Some(Operator::StrictEqual),
185 "!=" => Some(Operator::NotEqual),
186 "!==" => Some(Operator::StrictNotEqual),
187 "var" => Some(Operator::Variable),
188 "!" => Some(Operator::Negation),
189 "!!" => Some(Operator::DoubleNegation),
190 "if" => Some(Operator::If),
191 "or" => Some(Operator::Or),
192 "and" => Some(Operator::And),
193 "<" => Some(Operator::LessThan),
194 "<=" => Some(Operator::LessEqualThan),
195 ">" => Some(Operator::GreaterThan),
196 ">=" => Some(Operator::GreaterEqualThan),
197 "missing" => Some(Operator::Missing),
198 "missing_some" => Some(Operator::MissingSome),
199 "min" => Some(Operator::Min),
200 "max" => Some(Operator::Max),
201 "+" => Some(Operator::Addition),
202 "-" => Some(Operator::Subtraction),
203 "*" => Some(Operator::Multiplication),
204 "/" => Some(Operator::Division),
205 "%" => Some(Operator::Modulo),
206 "in" => Some(Operator::In),
207 "cat" => Some(Operator::Cat),
208 "substr" => Some(Operator::Substr),
209 "log" => Some(Operator::Log),
210 "merge" => Some(Operator::Merge),
211 "map" => Some(Operator::Map),
212 "filter" => Some(Operator::Filter),
213 "reduce" => Some(Operator::Reduce),
214 "all" => Some(Operator::All),
215 "some" => Some(Operator::Some),
216 "none" => Some(Operator::None),
217 _ => None,
218 }
219 }
220
221 pub fn compute(self, args: &[Expression], data: &Data) -> Value {
222 let compute_fn = match self {
223 Operator::Addition => addition::compute,
224 Operator::All => all::compute,
225 Operator::And => and::compute,
226 Operator::Cat => cat::compute,
227 Operator::Division => division::compute,
228 Operator::DoubleNegation => double_negation::compute,
229 Operator::Equal => equality::compute,
230 Operator::Filter => filter::compute,
231 Operator::GreaterEqualThan => greater_equal_than::compute,
232 Operator::GreaterThan => greater_than::compute,
233 Operator::If => if_else::compute,
234 Operator::In => is_in::compute,
235 Operator::LessEqualThan => less_equal_than::compute,
236 Operator::LessThan => less_than::compute,
237 Operator::Log => log::compute,
238 Operator::Max => max::compute,
239 Operator::Merge => merge::compute,
240 Operator::Min => min::compute,
241 Operator::MissingSome => missing_some::compute,
242 Operator::Missing => missing::compute,
243 Operator::Map => map::compute,
244 Operator::Modulo => modulo::compute,
245 Operator::Multiplication => multiplication::compute,
246 Operator::Negation => negation::compute,
247 Operator::None => none::compute,
248 Operator::NotEqual => not_equal::compute,
249 Operator::Or => or::compute,
250 Operator::Reduce => reduce::compute,
251 Operator::Some => some::compute,
252 Operator::StrictEqual => strict_equality::compute,
253 Operator::StrictNotEqual => strict_not_equal::compute,
254 Operator::Substr => substr::compute,
255 Operator::Subtraction => subtraction::compute,
256 Operator::Variable => variable::compute,
257 };
258
259 compute_fn(args, data)
260 }
261}
262
263#[cfg(test)]
264mod tests {
265 use super::*;
266
267 #[allow(clippy::cognitive_complexity)]
268 #[test]
269 fn from_str() {
270 assert_eq!(Operator::from_str("=="), Some(Operator::Equal));
271 assert_eq!(Operator::from_str("!="), Some(Operator::NotEqual));
272 assert_eq!(Operator::from_str("==="), Some(Operator::StrictEqual));
273 assert_eq!(Operator::from_str("!=="), Some(Operator::StrictNotEqual));
274 assert_eq!(Operator::from_str("var"), Some(Operator::Variable));
275 assert_eq!(Operator::from_str("!"), Some(Operator::Negation));
276 assert_eq!(Operator::from_str("!!"), Some(Operator::DoubleNegation));
277 assert_eq!(Operator::from_str("if"), Some(Operator::If));
278 assert_eq!(Operator::from_str("or"), Some(Operator::Or));
279 assert_eq!(Operator::from_str("and"), Some(Operator::And));
280 assert_eq!(Operator::from_str("<"), Some(Operator::LessThan));
281 assert_eq!(Operator::from_str("<="), Some(Operator::LessEqualThan));
282 assert_eq!(Operator::from_str(">"), Some(Operator::GreaterThan));
283 assert_eq!(Operator::from_str(">="), Some(Operator::GreaterEqualThan));
284 assert_eq!(Operator::from_str("missing"), Some(Operator::Missing));
285 assert_eq!(
286 Operator::from_str("missing_some"),
287 Some(Operator::MissingSome)
288 );
289 assert_eq!(Operator::from_str("min"), Some(Operator::Min));
290 assert_eq!(Operator::from_str("max"), Some(Operator::Max));
291 assert_eq!(Operator::from_str("+"), Some(Operator::Addition));
292 assert_eq!(Operator::from_str("-"), Some(Operator::Subtraction));
293 assert_eq!(Operator::from_str("*"), Some(Operator::Multiplication));
294 assert_eq!(Operator::from_str("/"), Some(Operator::Division));
295 assert_eq!(Operator::from_str("%"), Some(Operator::Modulo));
296 assert_eq!(Operator::from_str("in"), Some(Operator::In));
297 assert_eq!(Operator::from_str("cat"), Some(Operator::Cat));
298 assert_eq!(Operator::from_str("substr"), Some(Operator::Substr));
299 assert_eq!(Operator::from_str("log"), Some(Operator::Log));
300 assert_eq!(Operator::from_str("merge"), Some(Operator::Merge));
301 assert_eq!(Operator::from_str("map"), Some(Operator::Map));
302 assert_eq!(Operator::from_str("filter"), Some(Operator::Filter));
303 assert_eq!(Operator::from_str("reduce"), Some(Operator::Reduce));
304 assert_eq!(Operator::from_str("all"), Some(Operator::All));
305 assert_eq!(Operator::from_str("none"), Some(Operator::None));
306 assert_eq!(Operator::from_str("some"), Some(Operator::Some));
307 }
308}