use simd_json::OwnedValue as Value;
use simd_json::StaticNode;
use simd_json::prelude::*;
use super::compare::compare_values;
use super::eval;
use crate::error::EvalError;
use crate::filter::ast::{Filter, FunctionCall};
use crate::utils::type_name;
pub fn eval_function(func: &FunctionCall, value: &Value) -> Result<Vec<Value>, EvalError> {
match func {
FunctionCall::Map(inner) => eval_map(inner, value),
FunctionCall::Select(inner) => eval_select(inner, value),
FunctionCall::SortBy(inner) => eval_sort_by(inner, value),
FunctionCall::GroupBy(inner) => eval_group_by(inner, value),
FunctionCall::UniqueBy(inner) => eval_unique_by(inner, value),
FunctionCall::MinBy(inner) => eval_min_by(inner, value),
FunctionCall::MaxBy(inner) => eval_max_by(inner, value),
FunctionCall::Has(key) => eval_has(key, value),
FunctionCall::Split(delim) => eval_split(delim, value),
FunctionCall::Join(sep) => eval_join(sep, value),
FunctionCall::StartsWith(prefix) => eval_startswith(prefix, value),
FunctionCall::EndsWith(suffix) => eval_endswith(suffix, value),
FunctionCall::Contains(inner) => eval_contains(inner, value),
FunctionCall::WithEntries(inner) => eval_with_entries(inner, value),
}
}
fn eval_has(key: &str, value: &Value) -> Result<Vec<Value>, EvalError> {
match value {
Value::Object(map) => Ok(vec![Value::Static(StaticNode::Bool(map.contains_key(key)))]),
Value::Array(arr) => {
if let Ok(idx) = key.parse::<usize>() {
Ok(vec![Value::Static(StaticNode::Bool(idx < arr.len()))])
} else {
Ok(vec![Value::Static(StaticNode::Bool(false))])
}
}
_ => Ok(vec![Value::Static(StaticNode::Bool(false))]),
}
}
fn eval_split(delim: &str, value: &Value) -> Result<Vec<Value>, EvalError> {
match value {
Value::String(s) => {
if s.is_empty() {
return Ok(vec![Value::Array(Box::default())]);
}
if delim.is_empty() {
let parts: Vec<Value> = s.chars().map(|c| Value::String(c.to_string())).collect();
return Ok(vec![Value::Array(Box::new(parts))]);
}
let parts: Vec<Value> = s
.split(delim)
.map(|p| Value::String(p.to_string()))
.collect();
Ok(vec![Value::Array(Box::new(parts))])
}
_ => Err(EvalError::TypeError {
message: format!("split requires string, got {}", type_name(value)),
position: 0,
}),
}
}
fn eval_join(sep: &str, value: &Value) -> Result<Vec<Value>, EvalError> {
match value {
Value::Array(arr) => {
let mut strings = Vec::new();
for v in arr.iter() {
match v {
Value::String(s) => strings.push(s.clone()),
Value::Static(StaticNode::Null) => strings.push(String::new()), _ => {
return Err(EvalError::TypeError {
message: format!(
"join requires array of strings, got {} in array",
type_name(v)
),
position: 0,
});
}
}
}
Ok(vec![Value::String(strings.join(sep))])
}
_ => Err(EvalError::TypeError {
message: format!("join requires array, got {}", type_name(value)),
position: 0,
}),
}
}
fn eval_startswith(prefix: &str, value: &Value) -> Result<Vec<Value>, EvalError> {
match value {
Value::String(s) => Ok(vec![Value::Static(StaticNode::Bool(s.starts_with(prefix)))]),
_ => Err(EvalError::TypeError {
message: format!("startswith requires string, got {}", type_name(value)),
position: 0,
}),
}
}
fn eval_endswith(suffix: &str, value: &Value) -> Result<Vec<Value>, EvalError> {
match value {
Value::String(s) => Ok(vec![Value::Static(StaticNode::Bool(s.ends_with(suffix)))]),
_ => Err(EvalError::TypeError {
message: format!("endswith requires string, got {}", type_name(value)),
position: 0,
}),
}
}
fn eval_contains(inner: &Filter, value: &Value) -> Result<Vec<Value>, EvalError> {
let needle_results = eval(inner, value)?;
let needle = needle_results
.into_iter()
.next()
.unwrap_or(Value::Static(StaticNode::Null));
Ok(vec![Value::Static(StaticNode::Bool(value_contains(
value, &needle,
)))])
}
fn value_contains(a: &Value, b: &Value) -> bool {
match (a, b) {
(Value::String(haystack), Value::String(needle)) => haystack.contains(needle.as_str()),
(Value::Array(arr), Value::Array(needle_arr)) => {
needle_arr
.iter()
.all(|needle_elem| arr.iter().any(|elem| value_contains(elem, needle_elem)))
}
(Value::Object(obj), Value::Object(needle_obj)) => needle_obj
.iter()
.all(|(k, v)| obj.get(k).map(|ov| value_contains(ov, v)).unwrap_or(false)),
_ => compare_values(a, b) == Some(std::cmp::Ordering::Equal),
}
}
fn eval_with_entries(inner: &Filter, value: &Value) -> Result<Vec<Value>, EvalError> {
match value {
Value::Object(obj) => {
let entries: Vec<Value> = obj
.iter()
.map(|(k, v)| {
let mut entry = simd_json::owned::Object::new();
entry.insert("key".to_string(), Value::String(k.clone()));
entry.insert("value".to_string(), v.clone());
Value::Object(Box::new(entry))
})
.collect();
let mut transformed = Vec::new();
for entry in entries {
transformed.extend(eval(inner, &entry)?);
}
let mut result = simd_json::owned::Object::new();
for entry in transformed {
let key = entry
.get("key")
.or_else(|| entry.get("k"))
.or_else(|| entry.get("name"))
.and_then(|k| k.as_str())
.ok_or_else(|| EvalError::TypeError {
message: "Cannot use null as object key".to_string(),
position: 0,
})?;
let val = entry
.get("value")
.or_else(|| entry.get("v"))
.cloned()
.unwrap_or(Value::Static(StaticNode::Null));
result.insert(key.to_string(), val);
}
Ok(vec![Value::Object(Box::new(result))])
}
_ => Err(EvalError::TypeError {
message: format!("with_entries requires object, got {}", type_name(value)),
position: 0,
}),
}
}
fn eval_map(inner: &Filter, value: &Value) -> Result<Vec<Value>, EvalError> {
match value {
Value::Array(arr) => {
let mut results = Vec::new();
for elem in arr.iter() {
results.extend(eval(inner, elem)?);
}
Ok(vec![Value::Array(Box::new(results))])
}
_ => Err(EvalError::TypeError {
message: format!("map requires array, got {}", type_name(value)),
position: 0,
}),
}
}
fn eval_select(inner: &Filter, value: &Value) -> Result<Vec<Value>, EvalError> {
let results = eval(inner, value)?;
let is_truthy = results.iter().any(|v| {
!matches!(
v,
Value::Static(StaticNode::Null) | Value::Static(StaticNode::Bool(false))
)
});
if is_truthy {
Ok(vec![value.clone()])
} else {
Ok(vec![]) }
}
fn eval_sort_by(inner: &Filter, value: &Value) -> Result<Vec<Value>, EvalError> {
match value {
Value::Array(arr) => {
let mut keyed: Vec<(Value, Value)> = arr
.iter()
.map(|elem| {
let key = eval(inner, elem)?
.into_iter()
.next()
.unwrap_or(Value::Static(StaticNode::Null));
Ok((key, elem.clone()))
})
.collect::<Result<Vec<_>, EvalError>>()?;
keyed.sort_by(|(a, _), (b, _)| {
compare_values(a, b).unwrap_or(std::cmp::Ordering::Equal)
});
let sorted: Vec<Value> = keyed.into_iter().map(|(_, v)| v).collect();
Ok(vec![Value::Array(Box::new(sorted))])
}
_ => Err(EvalError::TypeError {
message: format!("sort_by requires array, got {}", type_name(value)),
position: 0,
}),
}
}
fn eval_group_by(inner: &Filter, value: &Value) -> Result<Vec<Value>, EvalError> {
match value {
Value::Array(arr) => {
let mut groups: Vec<(Value, Vec<Value>)> = vec![];
for elem in arr.iter() {
let key = eval(inner, elem)?
.into_iter()
.next()
.unwrap_or(Value::Static(StaticNode::Null));
if let Some((_, group)) = groups
.iter_mut()
.find(|(k, _)| compare_values(k, &key) == Some(std::cmp::Ordering::Equal))
{
group.push(elem.clone());
} else {
groups.push((key, vec![elem.clone()]));
}
}
groups.sort_by(|(a, _), (b, _)| {
compare_values(a, b).unwrap_or(std::cmp::Ordering::Equal)
});
let result: Vec<Value> = groups
.into_iter()
.map(|(_, group)| Value::Array(Box::new(group)))
.collect();
Ok(vec![Value::Array(Box::new(result))])
}
_ => Err(EvalError::TypeError {
message: format!("group_by requires array, got {}", type_name(value)),
position: 0,
}),
}
}
fn eval_unique_by(inner: &Filter, value: &Value) -> Result<Vec<Value>, EvalError> {
match value {
Value::Array(arr) => {
let mut seen: Vec<Value> = vec![];
let mut result: Vec<Value> = vec![];
for elem in arr.iter() {
let key = eval(inner, elem)?
.into_iter()
.next()
.unwrap_or(Value::Static(StaticNode::Null));
let is_new = !seen
.iter()
.any(|k| compare_values(k, &key) == Some(std::cmp::Ordering::Equal));
if is_new {
seen.push(key);
result.push(elem.clone());
}
}
Ok(vec![Value::Array(Box::new(result))])
}
_ => Err(EvalError::TypeError {
message: format!("unique_by requires array, got {}", type_name(value)),
position: 0,
}),
}
}
fn eval_min_by(inner: &Filter, value: &Value) -> Result<Vec<Value>, EvalError> {
eval_extremum_by(inner, value, true)
}
fn eval_max_by(inner: &Filter, value: &Value) -> Result<Vec<Value>, EvalError> {
eval_extremum_by(inner, value, false)
}
fn eval_extremum_by(inner: &Filter, value: &Value, is_min: bool) -> Result<Vec<Value>, EvalError> {
let func_name = if is_min { "min_by" } else { "max_by" };
match value {
Value::Array(arr) if arr.is_empty() => Ok(vec![Value::Static(StaticNode::Null)]),
Value::Array(arr) => {
let mut best_elem = &arr[0];
let mut best_key = eval(inner, best_elem)?
.into_iter()
.next()
.unwrap_or(Value::Static(StaticNode::Null));
for elem in arr.iter().skip(1) {
let key = eval(inner, elem)?
.into_iter()
.next()
.unwrap_or(Value::Static(StaticNode::Null));
let cmp = compare_values(&key, &best_key);
let should_replace = if is_min {
cmp == Some(std::cmp::Ordering::Less)
} else {
cmp == Some(std::cmp::Ordering::Greater)
};
if should_replace {
best_elem = elem;
best_key = key;
}
}
Ok(vec![best_elem.clone()])
}
_ => Err(EvalError::TypeError {
message: format!("{} requires array, got {}", func_name, type_name(value)),
position: 0,
}),
}
}