jsonlogic_rs/
js_op.rs

1//! Implementations of JavaScript operators for JSON Values
2
3use serde_json::{Number, Value};
4use std::f64;
5use std::str::FromStr;
6
7use crate::error::Error;
8
9// numeric characters according to parseFloat
10const NUMERICS: &'static [char] = &[
11    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '.', '-', '+', 'e', 'E',
12];
13
14// TODOS:
15// - there are too many tests in docstrings
16// - the docstrings are too sarcastic about JS equality
17
18pub fn to_string(value: &Value) -> String {
19    match value {
20        Value::Object(_) => String::from("[object Object]"),
21        Value::Bool(val) => val.to_string(),
22        Value::Null => String::from("null"),
23        Value::Number(val) => val.to_string(),
24        Value::String(val) => String::from(val),
25        Value::Array(val) => val
26            .iter()
27            .map(|i| match i {
28                Value::Null => String::from(""),
29                _ => to_string(i),
30            })
31            .collect::<Vec<String>>()
32            .join(","),
33    }
34}
35
36/// Implement something like OrdinaryToPrimitive() with a Number hint.
37///
38/// If it's possible to return a numeric primitive, returns Some<f64>.
39/// Otherwise, return None.
40fn to_primitive_number(value: &Value) -> Option<f64> {
41    match value {
42        // .valueOf() returns the object itself, which is not a primitive
43        Value::Object(_) => None,
44        // .valueOf() returns the array itself
45        Value::Array(_) => None,
46        Value::Bool(val) => {
47            if *val {
48                Some(1.0)
49            } else {
50                Some(0.0)
51            }
52        }
53        Value::Null => Some(0.0),
54        Value::Number(val) => val.as_f64(),
55        Value::String(_) => None, // already a primitive
56    }
57}
58
59pub fn str_to_number<S: AsRef<str>>(string: S) -> Option<f64> {
60    let s = string.as_ref();
61    if s == "" {
62        Some(0.0)
63    } else {
64        f64::from_str(s).ok()
65    }
66}
67
68enum Primitive {
69    String(String),
70    Number(f64),
71}
72
73#[allow(dead_code)]
74enum PrimitiveHint {
75    String,
76    Number,
77    Default,
78}
79
80fn to_primitive(value: &Value, hint: PrimitiveHint) -> Primitive {
81    match hint {
82        PrimitiveHint::String => Primitive::String(to_string(value)),
83        _ => to_primitive_number(value)
84            .map(Primitive::Number)
85            .unwrap_or(Primitive::String(to_string(value))),
86    }
87}
88
89/// Do our best to convert something into a number.
90///
91/// Should be pretty much equivalent to calling Number(value) in JS,
92/// returning None where that would return NaN.
93pub fn to_number(value: &Value) -> Option<f64> {
94    match to_primitive(value, PrimitiveHint::Number) {
95        Primitive::Number(num) => Some(num),
96        Primitive::String(string) => str_to_number(string),
97    }
98}
99
100/// Compare values in the JavaScript `==` style
101///
102/// Implements the Abstract Equality Comparison algorithm (`==` in JS)
103/// as defined [here](https://www.ecma-international.org/ecma-262/5.1/#sec-11.9.3).
104///
105/// ```rust
106/// use serde_json::json;
107/// use jsonlogic_rs::js_op::abstract_eq;
108///
109/// assert!(
110///   abstract_eq(
111///     &json!(null),
112///     &json!(null),
113///   )
114/// );
115/// assert!(
116///   abstract_eq(
117///     &json!(1.0),
118///     &json!(1),
119///   )
120/// );
121/// assert!(
122///   abstract_eq(
123///     &json!("foo"),
124///     &json!("foo"),
125///   )
126/// );
127/// assert!(
128///   abstract_eq(
129///     &json!(true),
130///     &json!(true),
131///   )
132/// );
133/// assert!(
134///   abstract_eq(
135///     &json!("1"),
136///     &json!(1.0),
137///   )
138/// );
139/// assert!(
140///   abstract_eq(
141///     &json!(1.0),
142///     &json!("1"),
143///   )
144/// );
145/// assert!(
146///   abstract_eq(
147///     &json!(true),
148///     &json!("1"),
149///   )
150/// );
151/// assert!(
152///   abstract_eq(
153///     &json!(true),
154///     &json!(1.0),
155///   )
156/// );
157/// assert!(
158///   abstract_eq(
159///     &json!({}),
160///     &json!("[object Object]"),
161///   )
162/// );
163///
164/// assert!(
165///   ! abstract_eq(
166///     &json!({}),
167///     &json!({}),
168///   )
169/// );
170/// assert!(
171///   ! abstract_eq(
172///     &json!([]),
173///     &json!([]),
174///   )
175/// );
176/// ```
177pub fn abstract_eq(first: &Value, second: &Value) -> bool {
178    // Follows the ECMA specification 2019:7.2.14 (Abstract Equality Comparison)
179    match (first, second) {
180        // 1. If Type(x) is the same as Type(y), then
181        //   a. If Type(x) is Undefined, return true.
182        //      - No need to handle this case, b/c undefined is not in JSON
183        //   b. If Type(x) is Null, return true.
184        (Value::Null, Value::Null) => true,
185        //   c. If Type(x) is Number, then
186        (Value::Number(x), Value::Number(y)) => {
187            // i. If x is NaN, return false.
188            //    - we can ignore this case, b/c NaN is not in JSON
189            // ii. If y is NaN, return false.
190            //    - same here
191            // iii. If x is the same Number value as y, return true.
192            x.as_f64()
193                .map(|x_val| y.as_f64().map(|y_val| x_val == y_val).unwrap_or(false))
194                .unwrap_or(false)
195            // x.as_f64() == y.as_f64()
196            // iv. If x is +0 and y is −0, return true.
197            //     - with serde's Number, this is handled by the above
198            // v. If x is −0 and y is +0, return true.
199            //    - same here
200            // vi. Return false.
201            //     - done!
202        }
203        //   d. If Type(x) is String, then return true if x and y are exactly
204        //      the same sequence of characters (same length and same characters
205        //      in corresponding positions). Otherwise, return false.
206        (Value::String(x), Value::String(y)) => x == y,
207        //   e. If Type(x) is Boolean, return true if x and y are both true
208        //      or both false. Otherwise, return false.
209        (Value::Bool(x), Value::Bool(y)) => x == y,
210        //   f. Return true if x and y refer to the same object. Otherwise, return false.
211        //      - not applicable to comparisons from JSON
212        // 2. If x is null and y is undefined, return true.
213        //    - not applicable to JSON b/c there is no undefined
214        // 3. If x is undefined and y is null, return true.
215        //    - not applicable to JSON b/c there is no undefined
216        // 4. If Type(x) is Number and Type(y) is String, return the result of
217        //    the comparison x == ToNumber(y).
218        (Value::Number(x), Value::String(y)) => {
219            // the empty string is 0
220            let y_res = str_to_number(y);
221            y_res
222                .map(|y_number| {
223                    x.as_f64()
224                        .map(|x_number| x_number == y_number)
225                        .unwrap_or(false)
226                })
227                .unwrap_or(false)
228        }
229        // 5. If Type(x) is String and Type(y) is Number, return the result
230        //    of the comparison ToNumber(x) == y.
231        (Value::String(x), Value::Number(y)) => {
232            let x_res = str_to_number(x);
233            x_res
234                .map(|x_number| {
235                    y.as_f64()
236                        .map(|y_number| x_number == y_number)
237                        .unwrap_or(false)
238                })
239                .unwrap_or(false)
240        }
241        // 6. If Type(x) is Boolean, return the result of the comparison ToNumber(x) == y.
242        (Value::Bool(x), _) => match x {
243            true => Number::from_f64(1 as f64)
244                .map(|num| {
245                    let value = Value::Number(num);
246                    abstract_eq(&value, second)
247                })
248                .unwrap_or(false),
249            false => Number::from_f64(0 as f64)
250                .map(|num| {
251                    let value = Value::Number(num);
252                    abstract_eq(&value, second)
253                })
254                .unwrap_or(false),
255        },
256        // 7. If Type(y) is Boolean, return the result of the comparison x == ToNumber(y).
257        (_, Value::Bool(y)) => match y {
258            true => Number::from_f64(1 as f64)
259                .map(|num| {
260                    let value = Value::Number(num);
261                    abstract_eq(first, &value)
262                })
263                .unwrap_or(false),
264            false => Number::from_f64(0 as f64)
265                .map(|num| {
266                    let value = Value::Number(num);
267                    abstract_eq(first, &value)
268                })
269                .unwrap_or(false),
270        },
271        // 8. If Type(x) is either String, Number, or Symbol and Type(y) is
272        //    Object, return the result of the comparison x == ToPrimitive(y).
273        // NB: the only type of Objects we get in JSON are regular old arrays
274        //     and regular old objects. ToPrimitive on the former yields a
275        //     stringification of its values, stuck together with commands,
276        //     but with no brackets on the outside. ToPrimitive on the later
277        //     is just always [object Object].
278        (Value::String(_), Value::Array(_)) | (Value::Number(_), Value::Array(_)) => {
279            abstract_eq(first, &Value::String(to_string(second)))
280        }
281        (Value::String(_), Value::Object(_)) | (Value::Number(_), Value::Object(_)) => {
282            abstract_eq(first, &Value::String(to_string(second)))
283        }
284        // 9. If Type(x) is Object and Type(y) is either String, Number, or
285        //    Symbol, return the result of the comparison ToPrimitive(x) == y.
286        (Value::Object(_), Value::String(_)) | (Value::Object(_), Value::Number(_)) => {
287            abstract_eq(&Value::String(to_string(first)), second)
288        }
289        (Value::Array(_), Value::String(_)) | (Value::Array(_), Value::Number(_)) => {
290            abstract_eq(&Value::String(to_string(first)), second)
291        }
292        _ => false,
293    }
294}
295
296/// Perform JS-style strict equality
297///
298/// Items are strictly equal if:
299/// - They are the same non-primitive object
300/// - They are a primitive object of the same type with the same value
301///
302/// ```rust
303/// use serde_json::json;
304/// use jsonlogic_rs::js_op::strict_eq;
305///
306/// // References of the same type and value are strictly equal
307/// assert!(strict_eq(&json!(1), &json!(1)));
308/// assert!(strict_eq(&json!(false), &json!(false)));
309/// assert!(strict_eq(&json!("foo"), &json!("foo")));
310///
311/// // "Abstract" type conversion is not performed for strict equality
312/// assert!(!strict_eq(&json!(0), &json!(false)));
313/// assert!(!strict_eq(&json!(""), &json!(0)));
314///
315/// // Objects only compare equal if they are the same reference
316/// assert!(!strict_eq(&json!([]), &json!([])));
317/// assert!(!strict_eq(&json!({}), &json!({})));
318///
319/// let arr = json!([]);
320/// let obj = json!({});
321/// assert!(strict_eq(&arr, &arr));
322/// assert!(strict_eq(&obj, &obj));
323/// ```
324///
325pub fn strict_eq(first: &Value, second: &Value) -> bool {
326    if std::ptr::eq(first, second) {
327        return true;
328    };
329    match (first, second) {
330        (Value::Null, Value::Null) => true,
331        (Value::Bool(x), Value::Bool(y)) => x == y,
332        (Value::Number(x), Value::Number(y)) => x
333            .as_f64()
334            .and_then(|x_val| y.as_f64().map(|y_val| x_val == y_val))
335            .unwrap_or(false),
336        (Value::String(x), Value::String(y)) => x == y,
337        _ => false,
338    }
339}
340
341pub fn strict_ne(first: &Value, second: &Value) -> bool {
342    !strict_eq(first, second)
343}
344
345/// Perform JS-style abstract less-than
346///
347///
348/// ```rust
349/// use serde_json::json;
350/// use jsonlogic_rs::js_op::abstract_lt;
351///
352/// assert_eq!(abstract_lt(&json!(-1), &json!(0)), true);
353/// assert_eq!(abstract_lt(&json!("-1"), &json!(0)), true);
354/// assert_eq!(abstract_lt(&json!(0), &json!(1)), true);
355/// assert_eq!(abstract_lt(&json!(0), &json!("1")), true);
356/// assert_eq!(abstract_lt(&json!(0), &json!("a")), false);
357/// ```
358pub fn abstract_lt(first: &Value, second: &Value) -> bool {
359    match (
360        to_primitive(first, PrimitiveHint::Number),
361        to_primitive(second, PrimitiveHint::Number),
362    ) {
363        (Primitive::String(f), Primitive::String(s)) => f < s,
364        (Primitive::Number(f), Primitive::Number(s)) => f < s,
365        (Primitive::String(f), Primitive::Number(s)) => {
366            if let Some(f) = str_to_number(f) {
367                f < s
368            } else {
369                false
370            }
371        }
372        (Primitive::Number(f), Primitive::String(s)) => {
373            if let Some(s) = str_to_number(s) {
374                f < s
375            } else {
376                false
377            }
378        }
379    }
380}
381
382/// JS-style abstract gt
383///
384/// ```rust
385/// use serde_json::json;
386/// use jsonlogic_rs::js_op::abstract_gt;
387///
388/// assert_eq!(abstract_gt(&json!(0), &json!(-1)), true);
389/// assert_eq!(abstract_gt(&json!(0), &json!("-1")), true);
390/// assert_eq!(abstract_gt(&json!(1), &json!(0)), true);
391/// assert_eq!(abstract_gt(&json!("1"), &json!(0)), true);
392/// ```
393pub fn abstract_gt(first: &Value, second: &Value) -> bool {
394    match (
395        to_primitive(first, PrimitiveHint::Number),
396        to_primitive(second, PrimitiveHint::Number),
397    ) {
398        (Primitive::String(f), Primitive::String(s)) => f > s,
399        (Primitive::Number(f), Primitive::Number(s)) => f > s,
400        (Primitive::String(f), Primitive::Number(s)) => {
401            if let Some(f) = str_to_number(f) {
402                f > s
403            } else {
404                false
405            }
406        }
407        (Primitive::Number(f), Primitive::String(s)) => {
408            if let Some(s) = str_to_number(s) {
409                f > s
410            } else {
411                false
412            }
413        }
414    }
415}
416
417/// Abstract inequality
418pub fn abstract_ne(first: &Value, second: &Value) -> bool {
419    !abstract_eq(first, second)
420}
421
422/// Provide abstract <= comparisons
423pub fn abstract_lte(first: &Value, second: &Value) -> bool {
424    abstract_lt(first, second) || abstract_eq(first, second)
425}
426
427/// Provide abstract >= comparisons
428pub fn abstract_gte(first: &Value, second: &Value) -> bool {
429    abstract_gt(first, second) || abstract_eq(first, second)
430}
431
432/// Get the max of an array of values, performing abstract type conversion
433pub fn abstract_max(items: &Vec<&Value>) -> Result<f64, Error> {
434    items
435        .into_iter()
436        .map(|v| {
437            to_number(v).ok_or_else(|| Error::InvalidArgument {
438                value: (*v).clone(),
439                operation: "max".into(),
440                reason: "Could not convert value to number".into(),
441            })
442        })
443        .fold(Ok(f64::NEG_INFINITY), |acc, cur| {
444            let max = acc?;
445            match cur {
446                Ok(num) => {
447                    if num > max {
448                        Ok(num)
449                    } else {
450                        Ok(max)
451                    }
452                }
453                _ => cur,
454            }
455        })
456}
457
458/// Get the max of an array of values, performing abstract type conversion
459pub fn abstract_min(items: &Vec<&Value>) -> Result<f64, Error> {
460    items
461        .into_iter()
462        .map(|v| {
463            to_number(v).ok_or_else(|| Error::InvalidArgument {
464                value: (*v).clone(),
465                operation: "max".into(),
466                reason: "Could not convert value to number".into(),
467            })
468        })
469        .fold(Ok(f64::INFINITY), |acc, cur| {
470            let min = acc?;
471            match cur {
472                Ok(num) => {
473                    if num < min {
474                        Ok(num)
475                    } else {
476                        Ok(min)
477                    }
478                }
479                _ => cur,
480            }
481        })
482}
483
484/// Do plus
485pub fn abstract_plus(first: &Value, second: &Value) -> Value {
486    let first_num = to_primitive_number(first);
487    let second_num = to_primitive_number(second);
488
489    match (first_num, second_num) {
490        (Some(f), Some(s)) => {
491            return Value::Number(Number::from_f64(f + s).unwrap());
492        }
493        _ => {}
494    };
495
496    let first_string = to_string(first);
497    let second_string = to_string(second);
498
499    Value::String(first_string.chars().chain(second_string.chars()).collect())
500}
501
502/// Add values, parsing to floats first.
503///
504/// The JSONLogic reference implementation uses the JS `parseFloat` operation
505/// on the parameters, which behaves quite differently from the normal JS
506/// numeric conversion with `Number(val)`. While the latter uses the
507/// `toPrimitive` method on the base object Prototype, the former first
508/// converts any incoming value to a string, and then tries to parse it
509/// as a float. The upshot is that things that normally parse fine into
510/// numbers in JS, like bools and null, convert to NaN, because you can't
511/// make "false" into a number.
512///
513/// The JSONLogic reference implementation deals with any values that
514/// evaluate to NaN by returning null. We instead will return an error,
515/// the behavior for non-numeric inputs is not specified in the spec,
516/// and returning errors seems like a more reasonable course of action
517/// than returning null.
518pub fn parse_float_add(vals: &Vec<&Value>) -> Result<f64, Error> {
519    vals.into_iter()
520        .map(|&v| {
521            parse_float(v).ok_or_else(|| Error::InvalidArgument {
522                value: v.clone(),
523                operation: "+".into(),
524                reason: "Argument could not be converted to a float".into(),
525            })
526        })
527        .fold(Ok(0.0), |acc, cur| {
528            let total = acc?;
529            match cur {
530                Ok(num) => Ok(total + num),
531                _ => cur,
532            }
533        })
534}
535
536/// Multiply values, parsing to floats first
537///
538/// See notes for parse_float_add on how this differs from normal number
539/// conversion as is done for _other_ arithmetic operators in the reference
540/// implementation
541pub fn parse_float_mul(vals: &Vec<&Value>) -> Result<f64, Error> {
542    vals.into_iter()
543        .map(|&v| {
544            parse_float(v).ok_or_else(|| Error::InvalidArgument {
545                value: v.clone(),
546                operation: "*".into(),
547                reason: "Argument could not be converted to a float".into(),
548            })
549        })
550        .fold(Ok(1.0), |acc, cur| {
551            let total = acc?;
552            match cur {
553                Ok(num) => Ok(total * num),
554                _ => cur,
555            }
556        })
557}
558
559/// Do minus
560pub fn abstract_minus(first: &Value, second: &Value) -> Result<f64, Error> {
561    let first_num = to_number(first);
562    let second_num = to_number(second);
563
564    if let None = first_num {
565        return Err(Error::InvalidArgument {
566            value: first.clone(),
567            operation: "-".into(),
568            reason: "Could not convert value to number.".into(),
569        });
570    }
571    if let None = second_num {
572        return Err(Error::InvalidArgument {
573            value: second.clone(),
574            operation: "-".into(),
575            reason: "Could not convert value to number.".into(),
576        });
577    }
578
579    Ok(first_num.unwrap() - second_num.unwrap())
580}
581
582/// Do division
583pub fn abstract_div(first: &Value, second: &Value) -> Result<f64, Error> {
584    let first_num = to_number(first);
585    let second_num = to_number(second);
586
587    if let None = first_num {
588        return Err(Error::InvalidArgument {
589            value: first.clone(),
590            operation: "/".into(),
591            reason: "Could not convert value to number.".into(),
592        });
593    }
594    if let None = second_num {
595        return Err(Error::InvalidArgument {
596            value: second.clone(),
597            operation: "/".into(),
598            reason: "Could not convert value to number.".into(),
599        });
600    }
601
602    Ok(first_num.unwrap() / second_num.unwrap())
603}
604
605/// Do modulo
606pub fn abstract_mod(first: &Value, second: &Value) -> Result<f64, Error> {
607    let first_num = to_number(first);
608    let second_num = to_number(second);
609
610    if let None = first_num {
611        return Err(Error::InvalidArgument {
612            value: first.clone(),
613            operation: "%".into(),
614            reason: "Could not convert value to number.".into(),
615        });
616    }
617    if let None = second_num {
618        return Err(Error::InvalidArgument {
619            value: second.clone(),
620            operation: "%".into(),
621            reason: "Could not convert value to number.".into(),
622        });
623    }
624
625    Ok(first_num.unwrap() % second_num.unwrap())
626}
627
628/// Attempt to convert a value to a negative number
629pub fn to_negative(val: &Value) -> Result<f64, Error> {
630    to_number(val)
631        .map(|v| -1.0 * v)
632        .ok_or_else(|| Error::InvalidArgument {
633            value: val.clone(),
634            operation: "to_negative".into(),
635            reason: "Could not convert value to a number".into(),
636        })
637}
638
639/// Try to parse a string as a float, javascript style
640///
641/// Strip whitespace, accumulate any potentially numeric characters at the
642/// start of the string and try to convert them into a float. We don't
643/// quite follow the spec exactly: we don't deal with infinity
644/// and NaN. That is okay, because this is only used in a context dealing
645/// with JSON values, which can't be Infinity or NaN.
646fn parse_float_string(val: &String) -> Option<f64> {
647    let (mut leading_numerics, _, _) = val.trim().chars().fold(
648        (Vec::new(), false, false),
649        |(mut acc, broke, saw_decimal), c| {
650            if broke {
651                // if we hit a nonnumeric last iter, just return what we've got
652                (acc, broke, saw_decimal)
653            } else if NUMERICS.contains(&c) {
654                let is_decimal = c == '.';
655                if saw_decimal && is_decimal {
656                    // if we're a decimal and we've seen one before, break
657                    (acc, true, is_decimal)
658                } else {
659                    // if we're a numeric, stick it on the acc
660                    acc.push(c);
661                    (acc, broke, saw_decimal || is_decimal)
662                }
663            } else {
664                // return the acc as is and let 'em know we hit a nonnumeric
665                (acc, true, saw_decimal)
666            }
667        },
668    );
669    // don't bother collecting into a string if we don't need to
670    if leading_numerics.len() == 0 {
671        return None;
672    };
673    if let Some('e') | Some('E') = leading_numerics.last() {
674        // If the last character is an 'e' or an `E`, remove it, to match
675        // edge case where JS ignores a trailing `e` rather than treating it
676        // as bad exponential notation, e.g. JS treats 1e as just 1.
677        leading_numerics.pop();
678    }
679
680    // collect into a string, try to parse as a float, return an option
681    leading_numerics
682        .iter()
683        .collect::<String>()
684        .parse::<f64>()
685        .ok()
686}
687
688/// Attempt to parse a value into a float.
689///
690/// The implementation should match https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseFloat
691/// as closely as is reasonable.
692pub fn parse_float(val: &Value) -> Option<f64> {
693    match val {
694        Value::Number(num) => num.as_f64(),
695        Value::String(string) => parse_float_string(string),
696        _ => parse_float(&Value::String(to_string(&val))),
697    }
698}
699
700// =====================================================================
701// Unit Tests
702// =====================================================================
703
704#[cfg(test)]
705mod abstract_operations {
706
707    use super::*;
708    use serde_json::json;
709
710    fn equal_values() -> Vec<(Value, Value)> {
711        vec![
712            (json!(null), json!(null)),
713            (json!(1), json!(1)),
714            (json!(1), json!(1.0)),
715            (json!(1.0), json!(1)),
716            (json!(0), json!(-0)),
717            (json!(-0), json!(0)),
718            (json!("foo"), json!("foo")),
719            (json!(""), json!("")),
720            (json!(true), json!(true)),
721            (json!(false), json!(false)),
722            (json!(1), json!("1")),
723            (json!(1), json!("1.0")),
724            (json!(1.0), json!("1.0")),
725            (json!(1.0), json!("1")),
726            (json!(0), json!("")),
727            (json!(0), json!("0")),
728            (json!(0), json!("-0")),
729            (json!(0), json!("+0")),
730            (json!(-1), json!("-1")),
731            (json!(-1.0), json!("-1")),
732            (json!(true), json!(1)),
733            (json!(true), json!("1")),
734            (json!(true), json!("1.0")),
735            (json!(true), json!([1])),
736            (json!(true), json!(["1"])),
737            (json!(false), json!(0)),
738            (json!(false), json!([])),
739            (json!(false), json!([0])),
740            (json!(false), json!("")),
741            (json!(false), json!("0")),
742            (json!("[object Object]"), json!({})),
743            (json!("[object Object]"), json!({"a": "a"})),
744            (json!(""), json!([])),
745            (json!(""), json!([null])),
746            (json!(","), json!([null, null])),
747            (json!("1,2"), json!([1, 2])),
748            (json!("a,b"), json!(["a", "b"])),
749            (json!(0), json!([])),
750            (json!(false), json!([])),
751            (json!(true), json!([1])),
752            (json!([]), json!("")),
753            (json!([null]), json!("")),
754            (json!([null, null]), json!(",")),
755            (json!([1, 2]), json!("1,2")),
756            (json!(["a", "b"]), json!("a,b")),
757            (json!([]), json!(0)),
758            (json!([0]), json!(0)),
759            (json!([]), json!(false)),
760            (json!([0]), json!(false)),
761            (json!([1]), json!(true)),
762        ]
763    }
764
765    fn lt_values() -> Vec<(Value, Value)> {
766        vec![
767            (json!(-1), json!(0)),
768            (json!("-1"), json!(0)),
769            (json!(0), json!(1)),
770            (json!(0), json!("1")),
771            (json!("foo"), json!("foos")),
772            (json!(""), json!("a")),
773            (json!(""), json!([1])),
774            (json!(""), json!([1, 2])),
775            (json!(""), json!("1")),
776            (json!(""), json!({})),
777            (json!(""), json!({"a": 1})),
778            (json!(false), json!(true)),
779            (json!(false), json!(1)),
780            (json!(false), json!("1")),
781            (json!(false), json!([1])),
782            (json!(null), json!(1)),
783            (json!(null), json!(true)),
784            (json!(null), json!("1")),
785            (json!([]), json!([1])),
786            (json!([]), json!([1, 2])),
787            (json!(0), json!([1])),
788            (json!("0"), json!({})),
789            (json!("0"), json!({"a": 1})),
790            (json!("0"), json!([1, 2])),
791        ]
792    }
793
794    fn gt_values() -> Vec<(Value, Value)> {
795        vec![
796            (json!(0), json!(-1)),
797            (json!(0), json!("-1")),
798            (json!(1), json!(0)),
799            (json!("1"), json!(0)),
800            (json!("foos"), json!("foo")),
801            (json!("a"), json!("")),
802            (json!([1]), json!("")),
803            (json!("1"), json!("")),
804            (json!("1"), json!("0")),
805            (json!(true), json!(false)),
806            (json!(1), json!(false)),
807            (json!("1"), json!(false)),
808            (json!([1]), json!(false)),
809            (json!(1), json!(null)),
810            (json!(true), json!(null)),
811            (json!("1"), json!(null)),
812            (json!([1]), json!([])),
813            (json!([1, 2]), json!([])),
814        ]
815    }
816
817    fn ne_values() -> Vec<(Value, Value)> {
818        vec![
819            (json!([]), json!([])),
820            (json!([1]), json!([1])),
821            (json!([1, 1]), json!([1, 1])),
822            (json!({}), json!({})),
823            (json!({"a": 1}), json!({"a": 1})),
824            (json!([]), json!({})),
825            (json!(0), json!(1)),
826            (json!("a"), json!("b")),
827            (json!(true), json!(false)),
828            (json!(true), json!([0])),
829            (json!(1.0), json!(1.1)),
830            (json!(null), json!(0)),
831            (json!(null), json!("")),
832            (json!(null), json!(false)),
833            (json!(null), json!(true)),
834        ]
835    }
836
837    /// Values that do not compare true for anything other than ne.
838    fn not_gt_not_lt_not_eq() -> Vec<(Value, Value)> {
839        vec![
840            (json!(null), json!("")),
841            (json!(null), json!("a")),
842            (json!(0), json!("a")),
843            (json!(0), json!([1, 2])),
844            (json!([]), json!([])),
845            (json!([1]), json!([1])),
846            (json!([1, 2]), json!([1, 2])),
847            (json!({}), json!({})),
848            (json!(false), json!({})),
849            (json!(true), json!({})),
850            (json!(false), json!([1, 2])),
851            (json!(true), json!([1, 2])),
852        ]
853    }
854
855    fn plus_cases() -> Vec<(Value, Value, Value)> {
856        vec![
857            (json!(1), json!(1), json!(2.0)),
858            (json!(1), json!(true), json!(2.0)),
859            (json!(true), json!(true), json!(2.0)),
860            (json!(1), json!(false), json!(1.0)),
861            (json!(false), json!(false), json!(0.0)),
862            (json!(1), json!(null), json!(1.0)),
863            (json!(null), json!(null), json!(0.0)),
864            (json!(1), json!("1"), json!("11")),
865            (json!(1), json!([1]), json!("11")),
866            (json!(1), json!([1, 2]), json!("11,2")),
867            (json!(1), json!([1, null, 3]), json!("11,,3")),
868            (json!(1), json!({}), json!("1[object Object]")),
869        ]
870    }
871
872    #[test]
873    fn test_to_string_obj() {
874        assert_eq!(&to_string(&json!({})), "[object Object]");
875        assert_eq!(&to_string(&json!({"a": "b"})), "[object Object]");
876    }
877
878    #[test]
879    fn test_to_string_array() {
880        assert_eq!(&to_string(&json!([])), "");
881        assert_eq!(&to_string(&json!([1, 2, 3])), "1,2,3");
882        assert_eq!(&to_string(&json!([1, [2, 3], 4])), "1,2,3,4");
883        assert_eq!(&to_string(&json!([1, {}, 2])), "1,[object Object],2");
884        assert_eq!(&to_string(&json!(["a", "b"])), "a,b");
885        assert_eq!(&to_string(&json!([null])), "");
886        assert_eq!(&to_string(&json!([null, 1, 2, null])), ",1,2,");
887        assert_eq!(&to_string(&json!([true, false])), "true,false");
888    }
889
890    #[test]
891    fn test_to_string_null() {
892        assert_eq!(&to_string(&json!(null)), "null");
893    }
894
895    #[test]
896    fn test_to_string_bool() {
897        assert_eq!(&to_string(&json!(true)), "true");
898        assert_eq!(&to_string(&json!(false)), "false");
899    }
900
901    #[test]
902    fn test_to_string_number() {
903        assert_eq!(&to_string(&json!(1.0)), "1.0");
904        assert_eq!(&to_string(&json!(1)), "1");
905    }
906
907    #[test]
908    fn test_abstract_eq() {
909        equal_values().iter().for_each(|(first, second)| {
910            println!("{:?}-{:?}", &first, &second);
911            assert!(abstract_eq(&first, &second), true);
912        })
913    }
914
915    #[test]
916    fn test_abstract_ne() {
917        ne_values().iter().for_each(|(first, second)| {
918            println!("{:?}-{:?}", &first, &second);
919            assert_eq!(abstract_ne(&first, &second), true);
920        })
921    }
922
923    #[test]
924    fn test_abstract_lt() {
925        lt_values().iter().for_each(|(first, second)| {
926            println!("{:?}-{:?}", &first, &second);
927            assert_eq!(abstract_lt(&first, &second), true);
928        })
929    }
930
931    #[test]
932    fn test_abstract_gt() {
933        gt_values().iter().for_each(|(first, second)| {
934            println!("{:?}-{:?}", &first, &second);
935            assert_eq!(abstract_gt(&first, &second), true);
936        })
937    }
938
939    #[test]
940    fn test_eq_values_are_not_lt() {
941        equal_values().iter().for_each(|(first, second)| {
942            println!("{:?}-{:?}", &first, &second);
943            assert_eq!(abstract_lt(&first, &second), false);
944        })
945    }
946
947    #[test]
948    fn test_eq_values_are_not_gt() {
949        equal_values().iter().for_each(|(first, second)| {
950            println!("{:?}-{:?}", &first, &second);
951            assert_eq!(abstract_gt(&first, &second), false);
952        })
953    }
954
955    #[test]
956    fn test_eq_values_are_not_ne() {
957        equal_values().iter().for_each(|(first, second)| {
958            println!("{:?}-{:?}", &first, &second);
959            assert_eq!(abstract_ne(&first, &second), false);
960        })
961    }
962
963    #[test]
964    fn test_lt_values_are_not_eq() {
965        lt_values().iter().for_each(|(first, second)| {
966            println!("{:?}-{:?}", &first, &second);
967            assert_eq!(abstract_eq(&first, &second), false);
968        })
969    }
970
971    #[test]
972    fn test_lt_values_are_not_gt() {
973        lt_values().iter().for_each(|(first, second)| {
974            println!("{:?}-{:?}", &first, &second);
975            assert_eq!(abstract_gt(&first, &second), false);
976        })
977    }
978
979    #[test]
980    fn test_lt_values_are_ne() {
981        lt_values().iter().for_each(|(first, second)| {
982            println!("{:?}-{:?}", &first, &second);
983            assert_eq!(abstract_ne(&first, &second), true);
984        })
985    }
986
987    #[test]
988    fn test_gt_values_are_not_eq() {
989        gt_values().iter().for_each(|(first, second)| {
990            println!("{:?}-{:?}", &first, &second);
991            assert_eq!(abstract_eq(&first, &second), false);
992        })
993    }
994
995    #[test]
996    fn test_gt_values_are_not_lt() {
997        gt_values().iter().for_each(|(first, second)| {
998            println!("{:?}-{:?}", &first, &second);
999            assert_eq!(abstract_lt(&first, &second), false);
1000        })
1001    }
1002
1003    #[test]
1004    fn test_gt_values_are_ne() {
1005        gt_values().iter().for_each(|(first, second)| {
1006            println!("{:?}-{:?}", &first, &second);
1007            assert_eq!(abstract_ne(&first, &second), true);
1008        })
1009    }
1010
1011    #[test]
1012    fn test_incomparable() {
1013        not_gt_not_lt_not_eq().iter().for_each(|(first, second)| {
1014            println!("{:?}-{:?}", &first, &second);
1015            assert_eq!(abstract_lt(&first, &second), false);
1016            assert_eq!(abstract_gt(&first, &second), false);
1017            assert_eq!(abstract_eq(&first, &second), false);
1018        })
1019    }
1020
1021    // abstract_lte
1022
1023    #[test]
1024    fn test_lt_values_are_lte() {
1025        lt_values().iter().for_each(|(first, second)| {
1026            println!("{:?}-{:?}", &first, &second);
1027            assert_eq!(abstract_lte(&first, &second), true);
1028        })
1029    }
1030
1031    #[test]
1032    fn test_eq_values_are_lte() {
1033        equal_values().iter().for_each(|(first, second)| {
1034            println!("{:?}-{:?}", &first, &second);
1035            assert_eq!(abstract_lte(&first, &second), true);
1036        })
1037    }
1038
1039    #[test]
1040    fn test_gt_values_are_not_lte() {
1041        gt_values().iter().for_each(|(first, second)| {
1042            println!("{:?}-{:?}", &first, &second);
1043            assert_eq!(abstract_lte(&first, &second), false);
1044        })
1045    }
1046
1047    // abstract_gte
1048
1049    #[test]
1050    fn test_gt_values_are_gte() {
1051        gt_values().iter().for_each(|(first, second)| {
1052            println!("{:?}-{:?}", &first, &second);
1053            assert_eq!(abstract_gte(&first, &second), true);
1054        })
1055    }
1056
1057    #[test]
1058    fn test_eq_values_are_gte() {
1059        equal_values().iter().for_each(|(first, second)| {
1060            println!("{:?}-{:?}", &first, &second);
1061            assert_eq!(abstract_gte(&first, &second), true);
1062        })
1063    }
1064
1065    #[test]
1066    fn test_lt_values_are_not_gte() {
1067        lt_values().iter().for_each(|(first, second)| {
1068            println!("{:?}-{:?}", &first, &second);
1069            assert_eq!(abstract_gte(&first, &second), false);
1070        })
1071    }
1072
1073    #[test]
1074    fn test_abstract_plus() {
1075        plus_cases().iter().for_each(|(first, second, exp)| {
1076            println!("{:?}-{:?}", &first, &second);
1077            let result = abstract_plus(&first, &second);
1078            match result {
1079                Value::Number(ref i) => match exp {
1080                    Value::Number(j) => assert_eq!(i, j),
1081                    _ => assert!(false),
1082                },
1083                Value::String(ref i) => match exp {
1084                    Value::String(j) => assert_eq!(i, j),
1085                    _ => assert!(false),
1086                },
1087                _ => assert!(false),
1088            }
1089        })
1090    }
1091}
1092
1093#[cfg(test)]
1094mod test_abstract_max {
1095    use super::*;
1096    use serde_json::json;
1097
1098    fn max_cases() -> Vec<(Vec<Value>, Result<f64, ()>)> {
1099        vec![
1100            (vec![json!(1), json!(2), json!(3)], Ok(3.0)),
1101            (vec![json!("1"), json!(true), json!([1])], Ok(1.0)),
1102            (
1103                vec![json!(""), json!(null), json!([]), json!(false)],
1104                Ok(0.0),
1105            ),
1106            (vec![json!("foo")], Err(())),
1107            (vec![], Ok(f64::NEG_INFINITY)),
1108        ]
1109    }
1110
1111    #[test]
1112    fn test_abstract_max() {
1113        max_cases().into_iter().for_each(|(items, exp)| {
1114            println!("Max: {:?}", items);
1115            let res = abstract_max(&items.iter().collect());
1116            println!("Res: {:?}", res);
1117            match exp {
1118                Ok(exp) => assert_eq!(res.unwrap(), exp),
1119                _ => {
1120                    res.unwrap_err();
1121                }
1122            };
1123        })
1124    }
1125}
1126
1127#[cfg(test)]
1128mod test_abstract_min {
1129    use super::*;
1130    use serde_json::json;
1131
1132    fn min_cases() -> Vec<(Vec<Value>, Result<f64, ()>)> {
1133        vec![
1134            (vec![json!(1), json!(2), json!(3)], Ok(1.0)),
1135            (vec![json!("1"), json!(true), json!([1])], Ok(1.0)),
1136            (
1137                vec![json!(""), json!(null), json!([]), json!(false)],
1138                Ok(0.0),
1139            ),
1140            (vec![json!("foo")], Err(())),
1141            (vec![], Ok(f64::INFINITY)),
1142        ]
1143    }
1144
1145    #[test]
1146    fn test_abstract_min() {
1147        min_cases().into_iter().for_each(|(items, exp)| {
1148            println!("Min: {:?}", items);
1149            let res = abstract_min(&items.iter().collect());
1150            println!("Res: {:?}", res);
1151            match exp {
1152                Ok(exp) => assert_eq!(res.unwrap(), exp),
1153                _ => {
1154                    res.unwrap_err();
1155                }
1156            };
1157        })
1158    }
1159}
1160
1161#[cfg(test)]
1162mod test_abstract_minus {
1163    use super::*;
1164    use serde_json::json;
1165
1166    fn minus_cases() -> Vec<(Value, Value, Result<f64, ()>)> {
1167        vec![
1168            (json!(5), json!(2), Ok(3.0)),
1169            (json!(0), json!(2), Ok(-2.0)),
1170            (json!("5"), json!(2), Ok(3.0)),
1171            (json!(["5"]), json!(2), Ok(3.0)),
1172            (json!(["5"]), json!(true), Ok(4.0)),
1173            (json!("foo"), json!(true), Err(())),
1174        ]
1175    }
1176
1177    #[test]
1178    fn test_abstract_minus() {
1179        minus_cases().into_iter().for_each(|(first, second, exp)| {
1180            println!("Minus: {:?} - {:?}", first, second);
1181            let res = abstract_minus(&first, &second);
1182            println!("Res: {:?}", res);
1183            match exp {
1184                Ok(exp) => assert_eq!(res.unwrap(), exp),
1185                _ => {
1186                    res.unwrap_err();
1187                }
1188            }
1189        })
1190    }
1191}
1192
1193#[cfg(test)]
1194mod test_strict {
1195
1196    use super::*;
1197    use serde_json::json;
1198
1199    fn eq_values() -> Vec<(Value, Value)> {
1200        vec![
1201            (json!(""), json!("")),
1202            (json!("foo"), json!("foo")),
1203            (json!(1), json!(1)),
1204            (json!(1), json!(1.0)),
1205            (json!(null), json!(null)),
1206            (json!(true), json!(true)),
1207            (json!(false), json!(false)),
1208        ]
1209    }
1210
1211    fn ne_values() -> Vec<(Value, Value)> {
1212        vec![
1213            (json!({}), json!({})),
1214            (json!({"a": "a"}), json!({"a": "a"})),
1215            (json!([]), json!([])),
1216            (json!("foo"), json!("noop")),
1217            (json!(1), json!(2)),
1218            (json!(0), json!([])),
1219            (json!(0), json!([0])),
1220            (json!(false), json!(null)),
1221            (json!(true), json!(false)),
1222            (json!(false), json!(true)),
1223            (json!(false), json!([])),
1224            (json!(false), json!("")),
1225        ]
1226    }
1227
1228    #[test]
1229    fn test_strict_eq() {
1230        eq_values().iter().for_each(|(first, second)| {
1231            println!("{:?}-{:?}", &first, &second);
1232            assert!(strict_eq(&first, &second));
1233        });
1234        ne_values().iter().for_each(|(first, second)| {
1235            println!("{:?}-{:?}", &first, &second);
1236            assert!(!strict_eq(&first, &second));
1237        });
1238    }
1239
1240    #[test]
1241    fn test_strict_eq_same_obj() {
1242        let obj = json!({});
1243        assert!(strict_eq(&obj, &obj))
1244    }
1245
1246    #[test]
1247    fn test_strict_ne() {
1248        ne_values().iter().for_each(|(first, second)| {
1249            println!("{:?}-{:?}", &first, &second);
1250            assert!(strict_ne(&first, &second));
1251        });
1252        eq_values().iter().for_each(|(first, second)| {
1253            println!("{:?}-{:?}", &first, &second);
1254            assert!(!strict_ne(&first, &second));
1255        });
1256    }
1257
1258    #[test]
1259    fn test_strict_ne_same_obj() {
1260        let obj = json!({});
1261        assert!(!strict_ne(&obj, &obj))
1262    }
1263}
1264
1265#[cfg(test)]
1266mod test_parse_float {
1267    use super::*;
1268    use serde_json::json;
1269
1270    fn cases() -> Vec<(Value, Option<f64>)> {
1271        vec![
1272            (json!(1), Some(1.0)),
1273            (json!(1.5), Some(1.5)),
1274            (json!(-1.5), Some(-1.5)),
1275            (json!("1"), Some(1.0)),
1276            (json!("1e2"), Some(100.0)),
1277            (json!("1E2"), Some(100.0)),
1278            (json!("1.1e2"), Some(110.0)),
1279            (json!("-1.1e2"), Some(-110.0)),
1280            (json!("1e-2"), Some(0.01)),
1281            (json!("1.0"), Some(1.0)),
1282            (json!("1.1"), Some(1.1)),
1283            (json!("1.1.1"), Some(1.1)),
1284            (json!("1234abc"), Some(1234.0)),
1285            (json!("1e"), Some(1.0)),
1286            (json!("1E"), Some(1.0)),
1287            (json!(false), None),
1288            (json!(true), None),
1289            (json!(null), None),
1290            (json!("+5"), Some(5.0)),
1291            (json!("-5"), Some(-5.0)),
1292            (json!([]), None),
1293            (json!([1]), Some(1.0)),
1294            // this is weird, but correct. it converts to a string first
1295            // "1,2" and then parses up to the first comma as a number
1296            (json!([1, 2]), Some(1.0)),
1297            (json!({}), None),
1298        ]
1299    }
1300
1301    #[test]
1302    fn test_parse_float() {
1303        cases()
1304            .into_iter()
1305            .for_each(|(input, exp)| assert_eq!(parse_float(&input), exp));
1306    }
1307}