use serde_json::{Number, Value};
use std::f64;
use std::str::FromStr;
use crate::error::Error;
const NUMERICS: &'static [char] = &[
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '.', '-', '+', 'e', 'E',
];
pub fn to_string(value: &Value) -> String {
match value {
Value::Object(_) => String::from("[object Object]"),
Value::Bool(val) => val.to_string(),
Value::Null => String::from("null"),
Value::Number(val) => val.to_string(),
Value::String(val) => String::from(val),
Value::Array(val) => val
.iter()
.map(|i| match i {
Value::Null => String::from(""),
_ => to_string(i),
})
.collect::<Vec<String>>()
.join(","),
}
}
fn to_primitive_number(value: &Value) -> Option<f64> {
match value {
Value::Object(_) => None,
Value::Array(_) => None,
Value::Bool(val) => {
if *val {
Some(1.0)
} else {
Some(0.0)
}
}
Value::Null => Some(0.0),
Value::Number(val) => val.as_f64(),
Value::String(_) => None,
}
}
pub fn str_to_number<S: AsRef<str>>(string: S) -> Option<f64> {
let s = string.as_ref();
if s == "" {
Some(0.0)
} else {
f64::from_str(s).ok()
}
}
enum Primitive {
String(String),
Number(f64),
}
#[allow(dead_code)]
enum PrimitiveHint {
String,
Number,
Default,
}
fn to_primitive(value: &Value, hint: PrimitiveHint) -> Primitive {
match hint {
PrimitiveHint::String => Primitive::String(to_string(value)),
_ => to_primitive_number(value)
.map(Primitive::Number)
.unwrap_or(Primitive::String(to_string(value))),
}
}
pub fn to_number(value: &Value) -> Option<f64> {
match to_primitive(value, PrimitiveHint::Number) {
Primitive::Number(num) => Some(num),
Primitive::String(string) => str_to_number(string),
}
}
pub fn abstract_eq(first: &Value, second: &Value) -> bool {
match (first, second) {
(Value::Null, Value::Null) => true,
(Value::Number(x), Value::Number(y)) => {
x.as_f64()
.map(|x_val| y.as_f64().map(|y_val| x_val == y_val).unwrap_or(false))
.unwrap_or(false)
}
(Value::String(x), Value::String(y)) => x == y,
(Value::Bool(x), Value::Bool(y)) => x == y,
(Value::Number(x), Value::String(y)) => {
let y_res = str_to_number(y);
y_res
.map(|y_number| {
x.as_f64()
.map(|x_number| x_number == y_number)
.unwrap_or(false)
})
.unwrap_or(false)
}
(Value::String(x), Value::Number(y)) => {
let x_res = str_to_number(x);
x_res
.map(|x_number| {
y.as_f64()
.map(|y_number| x_number == y_number)
.unwrap_or(false)
})
.unwrap_or(false)
}
(Value::Bool(x), _) => match x {
true => Number::from_f64(1 as f64)
.map(|num| {
let value = Value::Number(num);
abstract_eq(&value, second)
})
.unwrap_or(false),
false => Number::from_f64(0 as f64)
.map(|num| {
let value = Value::Number(num);
abstract_eq(&value, second)
})
.unwrap_or(false),
},
(_, Value::Bool(y)) => match y {
true => Number::from_f64(1 as f64)
.map(|num| {
let value = Value::Number(num);
abstract_eq(first, &value)
})
.unwrap_or(false),
false => Number::from_f64(0 as f64)
.map(|num| {
let value = Value::Number(num);
abstract_eq(first, &value)
})
.unwrap_or(false),
},
(Value::String(_), Value::Array(_)) | (Value::Number(_), Value::Array(_)) => {
abstract_eq(first, &Value::String(to_string(second)))
}
(Value::String(_), Value::Object(_)) | (Value::Number(_), Value::Object(_)) => {
abstract_eq(first, &Value::String(to_string(second)))
}
(Value::Object(_), Value::String(_)) | (Value::Object(_), Value::Number(_)) => {
abstract_eq(&Value::String(to_string(first)), second)
}
(Value::Array(_), Value::String(_)) | (Value::Array(_), Value::Number(_)) => {
abstract_eq(&Value::String(to_string(first)), second)
}
_ => false,
}
}
pub fn strict_eq(first: &Value, second: &Value) -> bool {
if std::ptr::eq(first, second) {
return true;
};
match (first, second) {
(Value::Null, Value::Null) => true,
(Value::Bool(x), Value::Bool(y)) => x == y,
(Value::Number(x), Value::Number(y)) => x
.as_f64()
.and_then(|x_val| y.as_f64().map(|y_val| x_val == y_val))
.unwrap_or(false),
(Value::String(x), Value::String(y)) => x == y,
_ => false,
}
}
pub fn strict_ne(first: &Value, second: &Value) -> bool {
!strict_eq(first, second)
}
pub fn abstract_lt(first: &Value, second: &Value) -> bool {
match (
to_primitive(first, PrimitiveHint::Number),
to_primitive(second, PrimitiveHint::Number),
) {
(Primitive::String(f), Primitive::String(s)) => f < s,
(Primitive::Number(f), Primitive::Number(s)) => f < s,
(Primitive::String(f), Primitive::Number(s)) => {
if let Some(f) = str_to_number(f) {
f < s
} else {
false
}
}
(Primitive::Number(f), Primitive::String(s)) => {
if let Some(s) = str_to_number(s) {
f < s
} else {
false
}
}
}
}
pub fn abstract_gt(first: &Value, second: &Value) -> bool {
match (
to_primitive(first, PrimitiveHint::Number),
to_primitive(second, PrimitiveHint::Number),
) {
(Primitive::String(f), Primitive::String(s)) => f > s,
(Primitive::Number(f), Primitive::Number(s)) => f > s,
(Primitive::String(f), Primitive::Number(s)) => {
if let Some(f) = str_to_number(f) {
f > s
} else {
false
}
}
(Primitive::Number(f), Primitive::String(s)) => {
if let Some(s) = str_to_number(s) {
f > s
} else {
false
}
}
}
}
pub fn abstract_ne(first: &Value, second: &Value) -> bool {
!abstract_eq(first, second)
}
pub fn abstract_lte(first: &Value, second: &Value) -> bool {
abstract_lt(first, second) || abstract_eq(first, second)
}
pub fn abstract_gte(first: &Value, second: &Value) -> bool {
abstract_gt(first, second) || abstract_eq(first, second)
}
pub fn abstract_max(items: &Vec<&Value>) -> Result<f64, Error> {
items
.into_iter()
.map(|v| {
to_number(v).ok_or(Error::InvalidArgument {
value: (*v).clone(),
operation: "max".into(),
reason: "Could not convert value to number".into(),
})
})
.fold(Ok(f64::NEG_INFINITY), |acc, cur| {
let max = acc?;
match cur {
Ok(num) => {
if num > max {
Ok(num)
} else {
Ok(max)
}
}
_ => cur,
}
})
}
pub fn abstract_min(items: &Vec<&Value>) -> Result<f64, Error> {
items
.into_iter()
.map(|v| {
to_number(v).ok_or(Error::InvalidArgument {
value: (*v).clone(),
operation: "max".into(),
reason: "Could not convert value to number".into(),
})
})
.fold(Ok(f64::INFINITY), |acc, cur| {
let min = acc?;
match cur {
Ok(num) => {
if num < min {
Ok(num)
} else {
Ok(min)
}
}
_ => cur,
}
})
}
pub fn abstract_plus(first: &Value, second: &Value) -> Value {
let first_num = to_primitive_number(first);
let second_num = to_primitive_number(second);
match (first_num, second_num) {
(Some(f), Some(s)) => {
return Value::Number(Number::from_f64(f + s).unwrap());
}
_ => {}
};
let first_string = to_string(first);
let second_string = to_string(second);
Value::String(first_string.chars().chain(second_string.chars()).collect())
}
pub fn parse_float_add(vals: &Vec<&Value>) -> Result<f64, Error> {
vals.into_iter()
.map(|&v| {
parse_float(v).ok_or(Error::InvalidArgument {
value: v.clone(),
operation: "+".into(),
reason: "Argument could not be converted to a float".into(),
})
})
.fold(Ok(0.0), |acc, cur| {
let total = acc?;
match cur {
Ok(num) => Ok(total + num),
_ => cur,
}
})
}
pub fn parse_float_mul(vals: &Vec<&Value>) -> Result<f64, Error> {
vals.into_iter()
.map(|&v| {
parse_float(v).ok_or(Error::InvalidArgument {
value: v.clone(),
operation: "*".into(),
reason: "Argument could not be converted to a float".into(),
})
})
.fold(Ok(1.0), |acc, cur| {
let total = acc?;
match cur {
Ok(num) => Ok(total * num),
_ => cur,
}
})
}
pub fn abstract_minus(first: &Value, second: &Value) -> Result<f64, Error> {
let first_num = to_number(first);
let second_num = to_number(second);
if let None = first_num {
return Err(Error::InvalidArgument {
value: first.clone(),
operation: "-".into(),
reason: "Could not convert value to number.".into(),
});
}
if let None = second_num {
return Err(Error::InvalidArgument {
value: second.clone(),
operation: "-".into(),
reason: "Could not convert value to number.".into(),
});
}
Ok(first_num.unwrap() - second_num.unwrap())
}
pub fn abstract_div(first: &Value, second: &Value) -> Result<f64, Error> {
let first_num = to_number(first);
let second_num = to_number(second);
if let None = first_num {
return Err(Error::InvalidArgument {
value: first.clone(),
operation: "/".into(),
reason: "Could not convert value to number.".into(),
});
}
if let None = second_num {
return Err(Error::InvalidArgument {
value: second.clone(),
operation: "/".into(),
reason: "Could not convert value to number.".into(),
});
}
Ok(first_num.unwrap() / second_num.unwrap())
}
pub fn abstract_mod(first: &Value, second: &Value) -> Result<f64, Error> {
let first_num = to_number(first);
let second_num = to_number(second);
if let None = first_num {
return Err(Error::InvalidArgument {
value: first.clone(),
operation: "%".into(),
reason: "Could not convert value to number.".into(),
});
}
if let None = second_num {
return Err(Error::InvalidArgument {
value: second.clone(),
operation: "%".into(),
reason: "Could not convert value to number.".into(),
});
}
Ok(first_num.unwrap() % second_num.unwrap())
}
pub fn to_negative(val: &Value) -> Result<f64, Error> {
to_number(val)
.map(|v| -1.0 * v)
.ok_or(Error::InvalidArgument {
value: val.clone(),
operation: "to_negative".into(),
reason: "Could not convert value to a number".into(),
})
}
fn parse_float_string(val: &String) -> Option<f64> {
let (mut leading_numerics, _, _) = val.trim().chars().fold(
(Vec::new(), false, false),
|(mut acc, broke, saw_decimal), c| {
if broke {
(acc, broke, saw_decimal)
} else if NUMERICS.contains(&c) {
let is_decimal = c == '.';
if saw_decimal && is_decimal {
(acc, true, is_decimal)
} else {
acc.push(c);
(acc, broke, saw_decimal || is_decimal)
}
} else {
(acc, true, saw_decimal)
}
},
);
if leading_numerics.len() == 0 {
return None;
};
if let Some('e') | Some('E') = leading_numerics.last() {
leading_numerics.pop();
}
leading_numerics
.iter()
.collect::<String>()
.parse::<f64>()
.ok()
}
pub fn parse_float(val: &Value) -> Option<f64> {
match val {
Value::Number(num) => num.as_f64(),
Value::String(string) => parse_float_string(string),
_ => parse_float(&Value::String(to_string(&val))),
}
}
#[cfg(test)]
mod abstract_operations {
use super::*;
use serde_json::json;
fn equal_values() -> Vec<(Value, Value)> {
vec![
(json!(null), json!(null)),
(json!(1), json!(1)),
(json!(1), json!(1.0)),
(json!(1.0), json!(1)),
(json!(0), json!(-0)),
(json!(-0), json!(0)),
(json!("foo"), json!("foo")),
(json!(""), json!("")),
(json!(true), json!(true)),
(json!(false), json!(false)),
(json!(1), json!("1")),
(json!(1), json!("1.0")),
(json!(1.0), json!("1.0")),
(json!(1.0), json!("1")),
(json!(0), json!("")),
(json!(0), json!("0")),
(json!(0), json!("-0")),
(json!(0), json!("+0")),
(json!(-1), json!("-1")),
(json!(-1.0), json!("-1")),
(json!(true), json!(1)),
(json!(true), json!("1")),
(json!(true), json!("1.0")),
(json!(true), json!([1])),
(json!(true), json!(["1"])),
(json!(false), json!(0)),
(json!(false), json!([])),
(json!(false), json!([0])),
(json!(false), json!("")),
(json!(false), json!("0")),
(json!("[object Object]"), json!({})),
(json!("[object Object]"), json!({"a": "a"})),
(json!(""), json!([])),
(json!(""), json!([null])),
(json!(","), json!([null, null])),
(json!("1,2"), json!([1, 2])),
(json!("a,b"), json!(["a", "b"])),
(json!(0), json!([])),
(json!(false), json!([])),
(json!(true), json!([1])),
(json!([]), json!("")),
(json!([null]), json!("")),
(json!([null, null]), json!(",")),
(json!([1, 2]), json!("1,2")),
(json!(["a", "b"]), json!("a,b")),
(json!([]), json!(0)),
(json!([0]), json!(0)),
(json!([]), json!(false)),
(json!([0]), json!(false)),
(json!([1]), json!(true)),
]
}
fn lt_values() -> Vec<(Value, Value)> {
vec![
(json!(-1), json!(0)),
(json!("-1"), json!(0)),
(json!(0), json!(1)),
(json!(0), json!("1")),
(json!("foo"), json!("foos")),
(json!(""), json!("a")),
(json!(""), json!([1])),
(json!(""), json!([1, 2])),
(json!(""), json!("1")),
(json!(""), json!({})),
(json!(""), json!({"a": 1})),
(json!(false), json!(true)),
(json!(false), json!(1)),
(json!(false), json!("1")),
(json!(false), json!([1])),
(json!(null), json!(1)),
(json!(null), json!(true)),
(json!(null), json!("1")),
(json!([]), json!([1])),
(json!([]), json!([1, 2])),
(json!(0), json!([1])),
(json!("0"), json!({})),
(json!("0"), json!({"a": 1})),
(json!("0"), json!([1, 2])),
]
}
fn gt_values() -> Vec<(Value, Value)> {
vec![
(json!(0), json!(-1)),
(json!(0), json!("-1")),
(json!(1), json!(0)),
(json!("1"), json!(0)),
(json!("foos"), json!("foo")),
(json!("a"), json!("")),
(json!([1]), json!("")),
(json!("1"), json!("")),
(json!("1"), json!("0")),
(json!(true), json!(false)),
(json!(1), json!(false)),
(json!("1"), json!(false)),
(json!([1]), json!(false)),
(json!(1), json!(null)),
(json!(true), json!(null)),
(json!("1"), json!(null)),
(json!([1]), json!([])),
(json!([1, 2]), json!([])),
]
}
fn ne_values() -> Vec<(Value, Value)> {
vec![
(json!([]), json!([])),
(json!([1]), json!([1])),
(json!([1, 1]), json!([1, 1])),
(json!({}), json!({})),
(json!({"a": 1}), json!({"a": 1})),
(json!([]), json!({})),
(json!(0), json!(1)),
(json!("a"), json!("b")),
(json!(true), json!(false)),
(json!(true), json!([0])),
(json!(1.0), json!(1.1)),
(json!(null), json!(0)),
(json!(null), json!("")),
(json!(null), json!(false)),
(json!(null), json!(true)),
]
}
fn not_gt_not_lt_not_eq() -> Vec<(Value, Value)> {
vec![
(json!(null), json!("")),
(json!(null), json!("a")),
(json!(0), json!("a")),
(json!(0), json!([1, 2])),
(json!([]), json!([])),
(json!([1]), json!([1])),
(json!([1, 2]), json!([1, 2])),
(json!({}), json!({})),
(json!(false), json!({})),
(json!(true), json!({})),
(json!(false), json!([1, 2])),
(json!(true), json!([1, 2])),
]
}
fn plus_cases() -> Vec<(Value, Value, Value)> {
vec![
(json!(1), json!(1), json!(2.0)),
(json!(1), json!(true), json!(2.0)),
(json!(true), json!(true), json!(2.0)),
(json!(1), json!(false), json!(1.0)),
(json!(false), json!(false), json!(0.0)),
(json!(1), json!(null), json!(1.0)),
(json!(null), json!(null), json!(0.0)),
(json!(1), json!("1"), json!("11")),
(json!(1), json!([1]), json!("11")),
(json!(1), json!([1, 2]), json!("11,2")),
(json!(1), json!([1, null, 3]), json!("11,,3")),
(json!(1), json!({}), json!("1[object Object]")),
]
}
#[test]
fn test_to_string_obj() {
assert_eq!(&to_string(&json!({})), "[object Object]");
assert_eq!(&to_string(&json!({"a": "b"})), "[object Object]");
}
#[test]
fn test_to_string_array() {
assert_eq!(&to_string(&json!([])), "");
assert_eq!(&to_string(&json!([1, 2, 3])), "1,2,3");
assert_eq!(&to_string(&json!([1, [2, 3], 4])), "1,2,3,4");
assert_eq!(&to_string(&json!([1, {}, 2])), "1,[object Object],2");
assert_eq!(&to_string(&json!(["a", "b"])), "a,b");
assert_eq!(&to_string(&json!([null])), "");
assert_eq!(&to_string(&json!([null, 1, 2, null])), ",1,2,");
assert_eq!(&to_string(&json!([true, false])), "true,false");
}
#[test]
fn test_to_string_null() {
assert_eq!(&to_string(&json!(null)), "null");
}
#[test]
fn test_to_string_bool() {
assert_eq!(&to_string(&json!(true)), "true");
assert_eq!(&to_string(&json!(false)), "false");
}
#[test]
fn test_to_string_number() {
assert_eq!(&to_string(&json!(1.0)), "1");
assert_eq!(&to_string(&json!(1)), "1");
}
#[test]
fn test_abstract_eq() {
equal_values().iter().for_each(|(first, second)| {
println!("{:?}-{:?}", &first, &second);
assert!(abstract_eq(&first, &second), true);
})
}
#[test]
fn test_abstract_ne() {
ne_values().iter().for_each(|(first, second)| {
println!("{:?}-{:?}", &first, &second);
assert_eq!(abstract_ne(&first, &second), true);
})
}
#[test]
fn test_abstract_lt() {
lt_values().iter().for_each(|(first, second)| {
println!("{:?}-{:?}", &first, &second);
assert_eq!(abstract_lt(&first, &second), true);
})
}
#[test]
fn test_abstract_gt() {
gt_values().iter().for_each(|(first, second)| {
println!("{:?}-{:?}", &first, &second);
assert_eq!(abstract_gt(&first, &second), true);
})
}
#[test]
fn test_eq_values_are_not_lt() {
equal_values().iter().for_each(|(first, second)| {
println!("{:?}-{:?}", &first, &second);
assert_eq!(abstract_lt(&first, &second), false);
})
}
#[test]
fn test_eq_values_are_not_gt() {
equal_values().iter().for_each(|(first, second)| {
println!("{:?}-{:?}", &first, &second);
assert_eq!(abstract_gt(&first, &second), false);
})
}
#[test]
fn test_eq_values_are_not_ne() {
equal_values().iter().for_each(|(first, second)| {
println!("{:?}-{:?}", &first, &second);
assert_eq!(abstract_ne(&first, &second), false);
})
}
#[test]
fn test_lt_values_are_not_eq() {
lt_values().iter().for_each(|(first, second)| {
println!("{:?}-{:?}", &first, &second);
assert_eq!(abstract_eq(&first, &second), false);
})
}
#[test]
fn test_lt_values_are_not_gt() {
lt_values().iter().for_each(|(first, second)| {
println!("{:?}-{:?}", &first, &second);
assert_eq!(abstract_gt(&first, &second), false);
})
}
#[test]
fn test_lt_values_are_ne() {
lt_values().iter().for_each(|(first, second)| {
println!("{:?}-{:?}", &first, &second);
assert_eq!(abstract_ne(&first, &second), true);
})
}
#[test]
fn test_gt_values_are_not_eq() {
gt_values().iter().for_each(|(first, second)| {
println!("{:?}-{:?}", &first, &second);
assert_eq!(abstract_eq(&first, &second), false);
})
}
#[test]
fn test_gt_values_are_not_lt() {
gt_values().iter().for_each(|(first, second)| {
println!("{:?}-{:?}", &first, &second);
assert_eq!(abstract_lt(&first, &second), false);
})
}
#[test]
fn test_gt_values_are_ne() {
gt_values().iter().for_each(|(first, second)| {
println!("{:?}-{:?}", &first, &second);
assert_eq!(abstract_ne(&first, &second), true);
})
}
#[test]
fn test_incomparable() {
not_gt_not_lt_not_eq().iter().for_each(|(first, second)| {
println!("{:?}-{:?}", &first, &second);
assert_eq!(abstract_lt(&first, &second), false);
assert_eq!(abstract_gt(&first, &second), false);
assert_eq!(abstract_eq(&first, &second), false);
})
}
#[test]
fn test_lt_values_are_lte() {
lt_values().iter().for_each(|(first, second)| {
println!("{:?}-{:?}", &first, &second);
assert_eq!(abstract_lte(&first, &second), true);
})
}
#[test]
fn test_eq_values_are_lte() {
equal_values().iter().for_each(|(first, second)| {
println!("{:?}-{:?}", &first, &second);
assert_eq!(abstract_lte(&first, &second), true);
})
}
#[test]
fn test_gt_values_are_not_lte() {
gt_values().iter().for_each(|(first, second)| {
println!("{:?}-{:?}", &first, &second);
assert_eq!(abstract_lte(&first, &second), false);
})
}
#[test]
fn test_gt_values_are_gte() {
gt_values().iter().for_each(|(first, second)| {
println!("{:?}-{:?}", &first, &second);
assert_eq!(abstract_gte(&first, &second), true);
})
}
#[test]
fn test_eq_values_are_gte() {
equal_values().iter().for_each(|(first, second)| {
println!("{:?}-{:?}", &first, &second);
assert_eq!(abstract_gte(&first, &second), true);
})
}
#[test]
fn test_lt_values_are_not_gte() {
lt_values().iter().for_each(|(first, second)| {
println!("{:?}-{:?}", &first, &second);
assert_eq!(abstract_gte(&first, &second), false);
})
}
#[test]
fn test_abstract_plus() {
plus_cases().iter().for_each(|(first, second, exp)| {
println!("{:?}-{:?}", &first, &second);
let result = abstract_plus(&first, &second);
match result {
Value::Number(ref i) => match exp {
Value::Number(j) => assert_eq!(i, j),
_ => assert!(false),
},
Value::String(ref i) => match exp {
Value::String(j) => assert_eq!(i, j),
_ => assert!(false),
},
_ => assert!(false),
}
})
}
}
#[cfg(test)]
mod test_abstract_max {
use super::*;
use serde_json::json;
fn max_cases() -> Vec<(Vec<Value>, Result<f64, ()>)> {
vec![
(vec![json!(1), json!(2), json!(3)], Ok(3.0)),
(vec![json!("1"), json!(true), json!([1])], Ok(1.0)),
(
vec![json!(""), json!(null), json!([]), json!(false)],
Ok(0.0),
),
(vec![json!("foo")], Err(())),
(vec![], Ok(f64::NEG_INFINITY)),
]
}
#[test]
fn test_abstract_max() {
max_cases().into_iter().for_each(|(items, exp)| {
println!("Max: {:?}", items);
let res = abstract_max(&items.iter().collect());
println!("Res: {:?}", res);
match exp {
Ok(exp) => assert_eq!(res.unwrap(), exp),
_ => {
res.unwrap_err();
}
};
})
}
}
#[cfg(test)]
mod test_abstract_min {
use super::*;
use serde_json::json;
fn min_cases() -> Vec<(Vec<Value>, Result<f64, ()>)> {
vec![
(vec![json!(1), json!(2), json!(3)], Ok(1.0)),
(vec![json!("1"), json!(true), json!([1])], Ok(1.0)),
(
vec![json!(""), json!(null), json!([]), json!(false)],
Ok(0.0),
),
(vec![json!("foo")], Err(())),
(vec![], Ok(f64::INFINITY)),
]
}
#[test]
fn test_abstract_min() {
min_cases().into_iter().for_each(|(items, exp)| {
println!("Min: {:?}", items);
let res = abstract_min(&items.iter().collect());
println!("Res: {:?}", res);
match exp {
Ok(exp) => assert_eq!(res.unwrap(), exp),
_ => {
res.unwrap_err();
}
};
})
}
}
#[cfg(test)]
mod test_abstract_minus {
use super::*;
use serde_json::json;
fn minus_cases() -> Vec<(Value, Value, Result<f64, ()>)> {
vec![
(json!(5), json!(2), Ok(3.0)),
(json!(0), json!(2), Ok(-2.0)),
(json!("5"), json!(2), Ok(3.0)),
(json!(["5"]), json!(2), Ok(3.0)),
(json!(["5"]), json!(true), Ok(4.0)),
(json!("foo"), json!(true), Err(())),
]
}
#[test]
fn test_abstract_minus() {
minus_cases().into_iter().for_each(|(first, second, exp)| {
println!("Minus: {:?} - {:?}", first, second);
let res = abstract_minus(&first, &second);
println!("Res: {:?}", res);
match exp {
Ok(exp) => assert_eq!(res.unwrap(), exp),
_ => {
res.unwrap_err();
}
}
})
}
}
#[cfg(test)]
mod test_strict {
use super::*;
use serde_json::json;
fn eq_values() -> Vec<(Value, Value)> {
vec![
(json!(""), json!("")),
(json!("foo"), json!("foo")),
(json!(1), json!(1)),
(json!(1), json!(1.0)),
(json!(null), json!(null)),
(json!(true), json!(true)),
(json!(false), json!(false)),
]
}
fn ne_values() -> Vec<(Value, Value)> {
vec![
(json!({}), json!({})),
(json!({"a": "a"}), json!({"a": "a"})),
(json!([]), json!([])),
(json!("foo"), json!("noop")),
(json!(1), json!(2)),
(json!(0), json!([])),
(json!(0), json!([0])),
(json!(false), json!(null)),
(json!(true), json!(false)),
(json!(false), json!(true)),
(json!(false), json!([])),
(json!(false), json!("")),
]
}
#[test]
fn test_strict_eq() {
eq_values().iter().for_each(|(first, second)| {
println!("{:?}-{:?}", &first, &second);
assert!(strict_eq(&first, &second));
});
ne_values().iter().for_each(|(first, second)| {
println!("{:?}-{:?}", &first, &second);
assert!(!strict_eq(&first, &second));
});
}
#[test]
fn test_strict_eq_same_obj() {
let obj = json!({});
assert!(strict_eq(&obj, &obj))
}
#[test]
fn test_strict_ne() {
ne_values().iter().for_each(|(first, second)| {
println!("{:?}-{:?}", &first, &second);
assert!(strict_ne(&first, &second));
});
eq_values().iter().for_each(|(first, second)| {
println!("{:?}-{:?}", &first, &second);
assert!(!strict_ne(&first, &second));
});
}
#[test]
fn test_strict_ne_same_obj() {
let obj = json!({});
assert!(!strict_ne(&obj, &obj))
}
}
#[cfg(test)]
mod test_parse_float {
use super::*;
use serde_json::json;
fn cases() -> Vec<(Value, Option<f64>)> {
vec![
(json!(1), Some(1.0)),
(json!(1.5), Some(1.5)),
(json!(-1.5), Some(-1.5)),
(json!("1"), Some(1.0)),
(json!("1e2"), Some(100.0)),
(json!("1E2"), Some(100.0)),
(json!("1.1e2"), Some(110.0)),
(json!("-1.1e2"), Some(-110.0)),
(json!("1e-2"), Some(0.01)),
(json!("1.0"), Some(1.0)),
(json!("1.1"), Some(1.1)),
(json!("1.1.1"), Some(1.1)),
(json!("1234abc"), Some(1234.0)),
(json!("1e"), Some(1.0)),
(json!("1E"), Some(1.0)),
(json!(false), None),
(json!(true), None),
(json!(null), None),
(json!("+5"), Some(5.0)),
(json!("-5"), Some(-5.0)),
(json!([]), None),
(json!([1]), Some(1.0)),
(json!([1, 2]), Some(1.0)),
(json!({}), None),
]
}
#[test]
fn test_parse_float() {
cases()
.into_iter()
.for_each(|(input, exp)| assert_eq!(parse_float(&input), exp));
}
}