use serde_json::Value;
use crate::error::Error;
use crate::value::{Evaluated, Parsed};
use crate::NULL;
pub fn if_(data: &Value, args: &Vec<&Value>) -> Result<Value, Error> {
match args.len() {
0 => {
return Ok(NULL);
}
1 => {
let parsed = Parsed::from_value(args[0])?;
let evaluated = parsed.evaluate(&data)?;
return Ok(evaluated.into());
}
_ => {}
}
args.into_iter()
.enumerate()
.fold(Ok((NULL, false, false)), |last_res, (i, val)| {
let (last_eval, was_truthy, should_return) = last_res?;
if should_return {
Ok((last_eval, was_truthy, should_return))
}
else if i % 2 == 0 {
let parsed = Parsed::from_value(val)?;
let eval = parsed.evaluate(data)?;
let is_truthy = match eval {
Evaluated::New(ref v) => truthy(v),
Evaluated::Raw(v) => truthy(v),
};
Ok((eval.into(), is_truthy, false))
}
else {
if was_truthy {
let parsed = Parsed::from_value(val)?;
let t_eval = parsed.evaluate(data)?;
Ok((Value::from(t_eval), true, true))
} else {
Ok((NULL, was_truthy, should_return))
}
}
})
.map(|rv| rv.0)
}
pub fn or(data: &Value, args: &Vec<&Value>) -> Result<Value, Error> {
enum OrResult {
Uninitialized,
Truthy(Value),
Current(Value),
}
let eval =
args.into_iter()
.fold(Ok(OrResult::Uninitialized), |last_res, current| {
let last_eval = last_res?;
if let OrResult::Truthy(_) = last_eval {
return Ok(last_eval);
}
let parsed = Parsed::from_value(current)?;
let evaluated = parsed.evaluate(data)?;
if truthy_from_evaluated(&evaluated) {
return Ok(OrResult::Truthy(evaluated.into()));
}
Ok(OrResult::Current(evaluated.into()))
})?;
match eval {
OrResult::Truthy(v) => Ok(v),
OrResult::Current(v) => Ok(v),
_ => Err(Error::UnexpectedError(
"Or operation had no values to operate on".into(),
)),
}
}
pub fn and(data: &Value, args: &Vec<&Value>) -> Result<Value, Error> {
enum AndResult {
Uninitialized,
Falsey(Value),
Current(Value),
}
let eval =
args.into_iter()
.fold(Ok(AndResult::Uninitialized), |last_res, current| {
let last_eval = last_res?;
if let AndResult::Falsey(_) = last_eval {
return Ok(last_eval);
}
let parsed = Parsed::from_value(current)?;
let evaluated = parsed.evaluate(data)?;
if !truthy_from_evaluated(&evaluated) {
return Ok(AndResult::Falsey(evaluated.into()));
}
Ok(AndResult::Current(evaluated.into()))
})?;
match eval {
AndResult::Falsey(v) => Ok(v),
AndResult::Current(v) => Ok(v),
_ => Err(Error::UnexpectedError(
"And operation had no values to operate on".into(),
)),
}
}
pub fn truthy_from_evaluated(evaluated: &Evaluated) -> bool {
match evaluated {
Evaluated::New(ref v) => truthy(v),
Evaluated::Raw(v) => truthy(v),
}
}
pub fn truthy(val: &Value) -> bool {
match val {
Value::Null => false,
Value::Bool(v) => *v,
Value::Number(v) => v
.as_f64()
.map(|v_num| if v_num == 0.0 { false } else { true })
.unwrap_or(false),
Value::String(v) => {
if v == "" {
false
} else {
true
}
}
Value::Array(v) => {
if v.len() == 0 {
false
} else {
true
}
}
Value::Object(_) => true,
}
}
#[cfg(test)]
mod test_truthy {
use super::*;
use serde_json::json;
#[test]
fn test_truthy() {
let trues = [
json!(true),
json!([1]),
json!([1, 2]),
json!({}),
json!({"a": 1}),
json!(1),
json!(-1),
json!("foo"),
];
let falses = [json!(false), json!([]), json!(""), json!(0), json!(null)];
trues.iter().for_each(|v| assert!(truthy(&v)));
falses.iter().for_each(|v| assert!(!truthy(&v)));
}
}