use serde_json::{Map, Value};
use crate::error::Error;
use crate::op::logic;
use crate::value::{Evaluated, Parsed};
pub fn map(data: &Value, args: &Vec<&Value>) -> Result<Value, Error> {
let (items, expression) = (args[0], args[1]);
let _parsed = Parsed::from_value(items)?;
let evaluated_items = _parsed.evaluate(data)?;
let values: Vec<&Value> = match evaluated_items {
Evaluated::New(Value::Array(ref vals)) => vals.iter().collect(),
Evaluated::Raw(Value::Array(vals)) => vals.iter().collect(),
Evaluated::New(Value::Null) => vec![],
Evaluated::Raw(Value::Null) => vec![],
_ => {
return Err(Error::InvalidArgument {
value: args[0].clone(),
operation: "map".into(),
reason: format!(
"First argument to map must evaluate to an array. Got {:?}",
evaluated_items
),
})
}
};
let parsed_expression = Parsed::from_value(expression)?;
values
.iter()
.map(|v| parsed_expression.evaluate(v).map(Value::from))
.collect::<Result<Vec<Value>, Error>>()
.map(Value::Array)
}
pub fn filter(data: &Value, args: &Vec<&Value>) -> Result<Value, Error> {
let (items, expression) = (args[0], args[1]);
let _parsed = Parsed::from_value(items)?;
let evaluated_items = _parsed.evaluate(data)?;
let values: Vec<Value> = match evaluated_items {
Evaluated::New(Value::Array(vals)) => vals,
Evaluated::Raw(Value::Array(vals)) => {
vals.into_iter().map(|v| v.clone()).collect()
}
Evaluated::New(Value::Null) => vec![],
Evaluated::Raw(Value::Null) => vec![],
_ => {
return Err(Error::InvalidArgument {
value: args[0].clone(),
operation: "map".into(),
reason: format!(
"First argument to filter must evaluate to an array. Got {:?}",
evaluated_items
),
})
}
};
let parsed_expression = Parsed::from_value(expression)?;
let value_vec: Vec<Value> = Vec::with_capacity(values.len());
values
.into_iter()
.fold(Ok(value_vec), |acc, cur| {
let mut filtered = acc?;
let predicate = parsed_expression.evaluate(&cur)?;
match logic::truthy_from_evaluated(&predicate) {
true => {
filtered.push(cur);
Ok(filtered)
}
false => Ok(filtered),
}
})
.map(Value::Array)
}
pub fn reduce(data: &Value, args: &Vec<&Value>) -> Result<Value, Error> {
let (items, expression, initializer) = (args[0], args[1], args[2]);
let _parsed_items = Parsed::from_value(items)?;
let evaluated_items = _parsed_items.evaluate(data)?;
let _parsed_initializer = Parsed::from_value(initializer)?;
let evaluated_initializer = _parsed_initializer.evaluate(data)?;
let values: Vec<Value> = match evaluated_items {
Evaluated::New(Value::Array(vals)) => vals,
Evaluated::Raw(Value::Array(vals)) => vals.iter().map(|v| v.clone()).collect(),
Evaluated::New(Value::Null) => vec![],
Evaluated::Raw(Value::Null) => vec![],
_ => {
return Err(Error::InvalidArgument {
value: args[0].clone(),
operation: "map".into(),
reason: format!(
"First argument to filter must evaluate to an array. Got {:?}",
evaluated_items
),
})
}
};
let parsed_expression = Parsed::from_value(expression)?;
values
.into_iter()
.fold(Ok(Value::from(evaluated_initializer)), |acc, cur| {
let accumulator = acc?;
let mut data = Map::with_capacity(2);
data.insert("current".into(), cur);
data.insert("accumulator".into(), accumulator);
parsed_expression
.evaluate(&Value::Object(data))
.map(Value::from)
})
}
pub fn all(data: &Value, args: &Vec<&Value>) -> Result<Value, Error> {
let (first_arg, second_arg) = (args[0], args[1]);
let _new_item: Value;
let potentially_evaled_first_arg = match first_arg {
Value::Object(_) => {
let parsed = Parsed::from_value(first_arg)?;
let evaluated = parsed.evaluate(data)?;
_new_item = evaluated.into();
&_new_item
}
_ => first_arg,
};
let _new_arr: Vec<Value>;
let items = match potentially_evaled_first_arg {
Value::Array(items) => items,
Value::String(string) => {
_new_arr = string
.chars()
.into_iter()
.map(|c| Value::String(c.to_string()))
.collect();
&_new_arr
}
Value::Null => {
_new_arr = Vec::new();
&_new_arr
}
_ => {
return Err(Error::InvalidArgument {
value: first_arg.clone(),
operation: "all".into(),
reason: format!(
"First argument to all must evaluate to an array, string, or null, got {}",
potentially_evaled_first_arg
),
})
}
};
if items.len() == 0 {
return Ok(Value::Bool(false));
}
let predicate = Parsed::from_value(second_arg)?;
let result = items.into_iter().fold(Ok(true), |acc, i| {
acc.and_then(|res| {
if !res {
return Ok(false);
};
let _parsed_item = Parsed::from_value(i)?;
let evaluated_item = _parsed_item.evaluate(data)?;
Ok(logic::truthy_from_evaluated(
&predicate.evaluate(&evaluated_item.into())?,
))
})
})?;
Ok(Value::Bool(result))
}
pub fn some(data: &Value, args: &Vec<&Value>) -> Result<Value, Error> {
let (first_arg, second_arg) = (args[0], args[1]);
let _new_item: Value;
let potentially_evaled_first_arg = match first_arg {
Value::Object(_) => {
let parsed = Parsed::from_value(first_arg)?;
let evaluated = parsed.evaluate(data)?;
_new_item = evaluated.into();
&_new_item
}
_ => first_arg,
};
let _new_arr: Vec<Value>;
let items = match potentially_evaled_first_arg {
Value::Array(items) => items,
Value::String(string) => {
_new_arr = string
.chars()
.into_iter()
.map(|c| Value::String(c.to_string()))
.collect();
&_new_arr
}
Value::Null => {
_new_arr = Vec::new();
&_new_arr
}
_ => {
return Err(Error::InvalidArgument {
value: first_arg.clone(),
operation: "all".into(),
reason: format!(
"First argument must evaluate to an array, a string, or null, got {}",
potentially_evaled_first_arg
),
})
}
};
if items.len() == 0 {
return Ok(Value::Bool(false));
}
let predicate = Parsed::from_value(second_arg)?;
let result = items.into_iter().fold(Ok(false), |acc, i| {
acc.and_then(|res| {
if res {
return Ok(true);
};
let _parsed_item = Parsed::from_value(i)?;
let evaluated_item = _parsed_item.evaluate(data)?;
Ok(logic::truthy_from_evaluated(
&predicate.evaluate(&evaluated_item.into())?,
))
})
})?;
Ok(Value::Bool(result))
}
pub fn none(data: &Value, args: &Vec<&Value>) -> Result<Value, Error> {
some(data, args).and_then(|had_some| match had_some {
Value::Bool(res) => Ok(Value::Bool(!res)),
_ => Err(Error::UnexpectedError(
"Unexpected return type from op_some".into(),
)),
})
}
pub fn merge(items: &Vec<&Value>) -> Result<Value, Error> {
let rv_vec: Vec<Value> = Vec::new();
Ok(Value::Array(items.into_iter().fold(
rv_vec,
|mut acc, i| {
match i {
Value::Array(i_vals) => {
i_vals.into_iter().for_each(|val| acc.push((*val).clone()));
}
_ => acc.push((**i).clone()),
};
acc
},
)))
}
pub fn in_(items: &Vec<&Value>) -> Result<Value, Error> {
let needle = items[0];
let haystack = items[1];
match haystack {
Value::Null => Ok(Value::Bool(false)),
Value::Array(possibles) => Ok(Value::Bool(possibles.contains(needle))),
Value::String(haystack_string) => {
let needle_string =
match needle {
Value::String(needle_string) => needle_string,
_ => return Err(Error::InvalidArgument {
value: needle.clone(),
operation: "in".into(),
reason:
"If second argument is a string, first argument must also be a string."
.into(),
}),
};
Ok(Value::Bool(haystack_string.contains(needle_string)))
}
_ => Err(Error::InvalidArgument {
value: haystack.clone(),
operation: "in".into(),
reason: "Second argument must be an array or a string".into(),
}),
}
}