#![allow(
clippy::unnecessary_wraps,
reason = "every function has an overload's signature, which can fail"
)]
use super::{Md, number_of, whole};
use crate::cel::overload::{list, text};
use crate::cel::{Key, Value};
use indexmap::IndexMap;
use std::cmp::Ordering;
use std::collections::HashSet;
use std::rc::Rc;
pub(super) fn sort(_: &Md, args: &[Value]) -> Result<Value, String> {
sorted(list(args, 0), |item| Ok(item.clone()))
}
pub(super) fn sort_by(_: &Md, args: &[Value]) -> Result<Value, String> {
let field = text(args, 1);
sorted(list(args, 0), |item| field_of(item, field).cloned())
}
pub(super) fn reverse(_: &Md, args: &[Value]) -> Result<Value, String> {
Ok(Value::list(list(args, 0).iter().rev().cloned().collect()))
}
pub(super) fn slice(_: &Md, args: &[Value]) -> Result<Value, String> {
let items = list(args, 0);
let bound = |n: &Value| whole(n).map(|n| usize::try_from(n).unwrap_or(0).min(items.len()));
let (start, end) = (bound(&args[1])?, bound(&args[2])?);
Ok(Value::list(items[start..end.max(start)].to_vec()))
}
pub(super) fn sum(_: &Md, args: &[Value]) -> Result<Value, String> {
Ok(Value::Double(numbers("sum", args)?.into_iter().sum()))
}
pub(super) fn min(_: &Md, args: &[Value]) -> Result<Value, String> {
Ok(numbers("min", args)?
.into_iter()
.reduce(f64::min)
.map_or(Value::Null, Value::Double))
}
pub(super) fn max(_: &Md, args: &[Value]) -> Result<Value, String> {
Ok(numbers("max", args)?
.into_iter()
.reduce(f64::max)
.map_or(Value::Null, Value::Double))
}
pub(super) fn written(items: &[Value]) -> Result<Vec<String>, String> {
items
.iter()
.map(|item| {
item.written()
.map(Option::unwrap_or_default)
.map_err(str::to_string)
})
.collect()
}
fn field_of<'v>(item: &'v Value, field: &str) -> Result<&'v Value, String> {
let Value::Map(entries) = item else {
return Err(format!(
"expected records with the field '{field}', not a {}",
item.kind()
));
};
entries
.get(&Key::String(Rc::from(field)))
.ok_or_else(|| format!("an element has no field '{field}'"))
}
fn sorted(items: &[Value], key: impl Fn(&Value) -> Result<Value, String>) -> Result<Value, String> {
let mut keyed = items
.iter()
.map(|item| Ok((key(item)?, item)))
.collect::<Result<Vec<_>, String>>()?;
let mut error = None;
keyed.sort_by(|(a, _), (b, _)| {
a.order(b).map_or_else(
|()| {
error.get_or_insert_with(|| {
format!("can't order a {} and a {}", a.kind(), b.kind())
});
Ordering::Equal
},
|ordering| ordering.unwrap_or_else(|| is_nan(a).cmp(&is_nan(b))),
)
});
match error {
Some(error) => Err(error),
None => Ok(Value::list(
keyed.into_iter().map(|(_, item)| item.clone()).collect(),
)),
}
}
fn is_nan(value: &Value) -> bool {
matches!(value, Value::Double(d) if d.is_nan())
}
fn numbers(name: &str, args: &[Value]) -> Result<Vec<f64>, String> {
list(args, 0)
.iter()
.map(|item| {
number_of(item).ok_or_else(|| format!("md.{name}() needs numbers, not {}", item.kind()))
})
.collect()
}
pub(super) fn unique(_: &Md, args: &[Value]) -> Result<Value, String> {
let mut keys = HashSet::new();
let mut others: Vec<&Value> = Vec::new();
let mut out = Vec::new();
for item in list(args, 0) {
let first = match Key::to_find(item) {
Some(key) => keys.insert(key),
None if others.iter().any(|other| other.equals(item)) => false,
None => {
others.push(item);
true
}
};
if first {
out.push(item.clone());
}
}
Ok(Value::list(out))
}
pub(super) fn group_by(_: &Md, args: &[Value]) -> Result<Value, String> {
let field = text(args, 1);
let mut groups: IndexMap<Key, Vec<Value>> = IndexMap::new();
for item in list(args, 0) {
let value = field_of(item, field)?;
let key = Key::to_find(value)
.ok_or_else(|| format!("can't group by '{field}': {} isn't a map key", value.text()))?;
groups.entry(key).or_default().push(item.clone());
}
Ok(Value::Map(Rc::new(
groups
.into_iter()
.map(|(key, group)| (key, Value::list(group)))
.collect(),
)))
}