use crate::cnf::GENERATION_ALLOCATION_LIMIT;
use crate::ctx::Context;
use crate::dbs::Options;
use crate::doc::CursorDoc;
use crate::err::Error;
use crate::sql::array::Array;
use crate::sql::array::Clump;
use crate::sql::array::Combine;
use crate::sql::array::Complement;
use crate::sql::array::Difference;
use crate::sql::array::Flatten;
use crate::sql::array::Intersect;
use crate::sql::array::Matches;
use crate::sql::array::Transpose;
use crate::sql::array::Union;
use crate::sql::array::Uniq;
use crate::sql::array::Windows;
use crate::sql::value::Value;
use crate::sql::Closure;
use crate::sql::Function;
use rand::prelude::SliceRandom;
use reblessive::tree::Stk;
use std::mem::size_of_val;
fn limit(name: &str, n: usize) -> Result<(), Error> {
if n > *GENERATION_ALLOCATION_LIMIT {
Err(Error::InvalidArguments {
name: name.to_owned(),
message: format!("Output must not exceed {} bytes.", *GENERATION_ALLOCATION_LIMIT),
})
} else {
Ok(())
}
}
pub fn add((mut array, value): (Array, Value)) -> Result<Value, Error> {
match value {
Value::Array(value) => {
for v in value.0 {
if !array.0.iter().any(|x| *x == v) {
array.0.push(v)
}
}
Ok(array.into())
}
value => {
if !array.0.iter().any(|x| *x == value) {
array.0.push(value)
}
Ok(array.into())
}
}
}
pub async fn all(
(stk, ctx, opt, doc): (&mut Stk, &Context, Option<&Options>, Option<&CursorDoc>),
(array, check): (Array, Option<Value>),
) -> Result<Value, Error> {
Ok(match check {
Some(closure) if closure.is_closure() => {
if let Some(opt) = opt {
for val in array.iter() {
let arg = val.compute(stk, ctx, opt, doc).await?;
let fnc = Function::Anonymous(closure.clone(), vec![arg], true);
if fnc.compute(stk, ctx, opt, doc).await?.is_truthy() {
continue;
} else {
return Ok(Value::Bool(false));
}
}
Value::Bool(true)
} else {
Value::None
}
}
Some(value) => array.iter().all(|v: &Value| *v == value).into(),
None => array.iter().all(Value::is_truthy).into(),
})
}
pub async fn any(
(stk, ctx, opt, doc): (&mut Stk, &Context, Option<&Options>, Option<&CursorDoc>),
(array, check): (Array, Option<Value>),
) -> Result<Value, Error> {
Ok(match check {
Some(closure) if closure.is_closure() => {
if let Some(opt) = opt {
for val in array.iter() {
let arg = val.compute(stk, ctx, opt, doc).await?;
let fnc = Function::Anonymous(closure.clone(), vec![arg], true);
if fnc.compute(stk, ctx, opt, doc).await?.is_truthy() {
return Ok(Value::Bool(true));
} else {
continue;
}
}
Value::Bool(false)
} else {
Value::None
}
}
Some(value) => array.iter().any(|v: &Value| *v == value).into(),
None => array.iter().any(Value::is_truthy).into(),
})
}
pub fn append((mut array, value): (Array, Value)) -> Result<Value, Error> {
array.push(value);
Ok(array.into())
}
pub fn at((array, i): (Array, i64)) -> Result<Value, Error> {
let mut idx = i as usize;
if i < 0 {
idx = (array.len() as i64 + i) as usize;
}
Ok(array.get(idx).cloned().unwrap_or_default())
}
pub fn boolean_and((lh, rh): (Array, Array)) -> Result<Value, Error> {
let longest_length = lh.len().max(rh.len());
let mut results = Array::with_capacity(longest_length);
for i in 0..longest_length {
let lhv = lh.get(i);
let rhv = rh.get(i);
results
.push((lhv.is_some_and(Value::is_truthy) && rhv.is_some_and(Value::is_truthy)).into());
}
Ok(results.into())
}
pub fn boolean_not((mut array,): (Array,)) -> Result<Value, Error> {
array.iter_mut().for_each(|v| *v = (!v.is_truthy()).into());
Ok(array.into())
}
pub fn boolean_or((lh, rh): (Array, Array)) -> Result<Value, Error> {
let longest_length = lh.len().max(rh.len());
let mut results = Array::with_capacity(longest_length);
for i in 0..longest_length {
let lhv = lh.get(i);
let rhv = rh.get(i);
results
.push((lhv.is_some_and(Value::is_truthy) || rhv.is_some_and(Value::is_truthy)).into());
}
Ok(results.into())
}
pub fn boolean_xor((lh, rh): (Array, Array)) -> Result<Value, Error> {
let longest_length = lh.len().max(rh.len());
let mut results = Array::with_capacity(longest_length);
for i in 0..longest_length {
let lhv = lh.get(i);
let rhv = rh.get(i);
results
.push((lhv.is_some_and(Value::is_truthy) ^ rhv.is_some_and(Value::is_truthy)).into());
}
Ok(results.into())
}
pub fn clump((array, clump_size): (Array, i64)) -> Result<Value, Error> {
let clump_size = clump_size.max(0) as usize;
Ok(array.clump(clump_size)?.into())
}
pub fn combine((array, other): (Array, Array)) -> Result<Value, Error> {
Ok(array.combine(other).into())
}
pub fn complement((array, other): (Array, Array)) -> Result<Value, Error> {
Ok(array.complement(other).into())
}
pub fn concat(mut arrays: Vec<Array>) -> Result<Value, Error> {
let len = match arrays.iter().map(Array::len).reduce(|c, v| c + v) {
None => Err(Error::InvalidArguments {
name: String::from("array::concat"),
message: String::from("Expected at least one argument"),
}),
Some(l) => Ok(l),
}?;
let mut arr = Array::with_capacity(len);
arrays.iter_mut().for_each(|val| {
arr.append(val);
});
Ok(arr.into())
}
pub fn difference((array, other): (Array, Array)) -> Result<Value, Error> {
Ok(array.difference(other).into())
}
pub fn distinct((array,): (Array,)) -> Result<Value, Error> {
Ok(array.uniq().into())
}
pub fn fill(
(mut array, value, start, end): (Array, Value, Option<isize>, Option<isize>),
) -> Result<Value, Error> {
let min = 0;
let max = array.len();
let negative_max = -(max as isize);
let start = match start {
Some(start) if negative_max <= start && start < 0 => (start + max as isize) as usize,
Some(start) if start < negative_max => 0,
Some(start) => start as usize,
None => min,
};
let end = match end {
Some(end) if negative_max <= end && end < 0 => (end + max as isize) as usize,
Some(end) if end < negative_max => 0,
Some(end) => end as usize,
None => max,
};
if start == min && end >= max {
array.fill(value);
} else if end > start {
let end_minus_one = end - 1;
for i in start..end_minus_one {
if let Some(elem) = array.get_mut(i) {
*elem = value.clone();
}
}
if let Some(last_elem) = array.get_mut(end_minus_one) {
*last_elem = value;
}
}
Ok(array.into())
}
pub async fn filter(
(stk, ctx, opt, doc): (&mut Stk, &Context, Option<&Options>, Option<&CursorDoc>),
(array, check): (Array, Value),
) -> Result<Value, Error> {
Ok(match check {
closure if closure.is_closure() => {
if let Some(opt) = opt {
let mut res = Vec::with_capacity(array.len());
for val in array.iter() {
let arg = val.compute(stk, ctx, opt, doc).await?;
let fnc = Function::Anonymous(closure.clone(), vec![arg.clone()], true);
if fnc.compute(stk, ctx, opt, doc).await?.is_truthy() {
res.push(arg)
}
}
Value::from(res)
} else {
Value::None
}
}
value => array.into_iter().filter(|v: &Value| *v == value).collect::<Vec<_>>().into(),
})
}
pub async fn filter_index(
(stk, ctx, opt, doc): (&mut Stk, &Context, Option<&Options>, Option<&CursorDoc>),
(array, value): (Array, Value),
) -> Result<Value, Error> {
Ok(match value {
closure if closure.is_closure() => {
if let Some(opt) = opt {
let mut res = Vec::with_capacity(array.len());
for (i, val) in array.iter().enumerate() {
let arg = val.compute(stk, ctx, opt, doc).await?;
let fnc = Function::Anonymous(closure.clone(), vec![arg, i.into()], true);
if fnc.compute(stk, ctx, opt, doc).await?.is_truthy() {
res.push(i);
}
}
Value::from(res)
} else {
Value::None
}
}
value => array
.iter()
.enumerate()
.filter_map(|(i, v)| {
if *v == value {
Some(Value::from(i))
} else {
None
}
})
.collect::<Vec<_>>()
.into(),
})
}
pub async fn find(
(stk, ctx, opt, doc): (&mut Stk, &Context, Option<&Options>, Option<&CursorDoc>),
(array, value): (Array, Value),
) -> Result<Value, Error> {
Ok(match value {
closure if closure.is_closure() => {
if let Some(opt) = opt {
for val in array.iter() {
let arg = val.compute(stk, ctx, opt, doc).await?;
let fnc = Function::Anonymous(closure.clone(), vec![arg.clone()], true);
if fnc.compute(stk, ctx, opt, doc).await?.is_truthy() {
return Ok(arg);
}
}
Value::None
} else {
Value::None
}
}
value => array.into_iter().find(|v: &Value| *v == value).into(),
})
}
pub async fn find_index(
(stk, ctx, opt, doc): (&mut Stk, &Context, Option<&Options>, Option<&CursorDoc>),
(array, value): (Array, Value),
) -> Result<Value, Error> {
Ok(match value {
closure if closure.is_closure() => {
if let Some(opt) = opt {
for (i, val) in array.iter().enumerate() {
let arg = val.compute(stk, ctx, opt, doc).await?;
let fnc = Function::Anonymous(closure.clone(), vec![arg, i.into()], true);
if fnc.compute(stk, ctx, opt, doc).await?.is_truthy() {
return Ok(i.into());
}
}
Value::None
} else {
Value::None
}
}
value => array
.iter()
.enumerate()
.find_map(|(i, v)| {
if *v == value {
Some(Value::from(i))
} else {
None
}
})
.into(),
})
}
pub fn first((array,): (Array,)) -> Result<Value, Error> {
if let [first, ..] = &array[0..] {
Ok(first.to_owned())
} else {
Ok(Value::None)
}
}
pub fn flatten((array,): (Array,)) -> Result<Value, Error> {
Ok(array.flatten().into())
}
pub async fn fold(
(stk, ctx, opt, doc): (&mut Stk, &Context, Option<&Options>, Option<&CursorDoc>),
(array, init, mapper): (Array, Value, Closure),
) -> Result<Value, Error> {
if let Some(opt) = opt {
let mut accum = init;
for (i, val) in array.into_iter().enumerate() {
let fnc = Function::Anonymous(mapper.clone().into(), vec![accum, val, i.into()], true);
accum = fnc.compute(stk, ctx, opt, doc).await?;
}
Ok(accum)
} else {
Ok(Value::None)
}
}
pub fn group((array,): (Array,)) -> Result<Value, Error> {
Ok(array.flatten().uniq().into())
}
pub fn insert((mut array, value, index): (Array, Value, Option<i64>)) -> Result<Value, Error> {
match index {
Some(mut index) => {
if index < 0 {
index += array.len() as i64;
}
if index > array.len() as i64 || index < 0 {
return Ok(array.into());
}
array.insert(index as usize, value);
Ok(array.into())
}
None => {
array.push(value);
Ok(array.into())
}
}
}
pub fn intersect((array, other): (Array, Array)) -> Result<Value, Error> {
Ok(array.intersect(other).into())
}
pub fn is_empty((array,): (Array,)) -> Result<Value, Error> {
Ok(array.is_empty().into())
}
pub fn join((arr, sep): (Array, String)) -> Result<Value, Error> {
Ok(arr.into_iter().map(Value::as_raw_string).collect::<Vec<_>>().join(&sep).into())
}
pub fn last((array,): (Array,)) -> Result<Value, Error> {
if let [.., last] = &array[0..] {
Ok(last.to_owned())
} else {
Ok(Value::None)
}
}
pub fn len((array,): (Array,)) -> Result<Value, Error> {
Ok(array.len().into())
}
pub fn logical_and((lh, rh): (Array, Array)) -> Result<Value, Error> {
let mut result_arr = Array::with_capacity(lh.len().max(rh.len()));
let mut iters = (lh.into_iter(), rh.into_iter());
for (lhv, rhv) in std::iter::from_fn(|| {
let r = (iters.0.next(), iters.1.next());
if r.0.is_none() && r.1.is_none() {
None
} else {
Some((r.0.unwrap_or(Value::Null), r.1.unwrap_or(Value::Null)))
}
}) {
let truth = lhv.is_truthy() && rhv.is_truthy();
let r = if lhv.is_truthy() == truth {
lhv
} else if rhv.is_truthy() == truth {
rhv
} else {
truth.into()
};
result_arr.push(r);
}
Ok(result_arr.into())
}
pub fn logical_or((lh, rh): (Array, Array)) -> Result<Value, Error> {
let mut result_arr = Array::with_capacity(lh.len().max(rh.len()));
let mut iters = (lh.into_iter(), rh.into_iter());
for (lhv, rhv) in std::iter::from_fn(|| {
let r = (iters.0.next(), iters.1.next());
if r.0.is_none() && r.1.is_none() {
None
} else {
Some((r.0.unwrap_or(Value::Null), r.1.unwrap_or(Value::Null)))
}
}) {
let truth = lhv.is_truthy() || rhv.is_truthy();
let r = if lhv.is_truthy() == truth {
lhv
} else if rhv.is_truthy() == truth {
rhv
} else {
truth.into()
};
result_arr.push(r);
}
Ok(result_arr.into())
}
pub fn logical_xor((lh, rh): (Array, Array)) -> Result<Value, Error> {
let mut result_arr = Array::with_capacity(lh.len().max(rh.len()));
let mut iters = (lh.into_iter(), rh.into_iter());
for (lhv, rhv) in std::iter::from_fn(|| {
let r = (iters.0.next(), iters.1.next());
if r.0.is_none() && r.1.is_none() {
None
} else {
Some((r.0.unwrap_or(Value::Null), r.1.unwrap_or(Value::Null)))
}
}) {
let truth = lhv.is_truthy() ^ rhv.is_truthy();
let r = if lhv.is_truthy() == truth {
lhv
} else if rhv.is_truthy() == truth {
rhv
} else {
truth.into()
};
result_arr.push(r);
}
Ok(result_arr.into())
}
pub async fn map(
(stk, ctx, opt, doc): (&mut Stk, &Context, Option<&Options>, Option<&CursorDoc>),
(array, mapper): (Array, Closure),
) -> Result<Value, Error> {
if let Some(opt) = opt {
let mut res = Vec::with_capacity(array.len());
for (i, val) in array.into_iter().enumerate() {
let arg = val.compute(stk, ctx, opt, doc).await?;
let fnc = Function::Anonymous(mapper.clone().into(), vec![arg, i.into()], true);
res.push(fnc.compute(stk, ctx, opt, doc).await?);
}
Ok(res.into())
} else {
Ok(Value::None)
}
}
pub fn matches((array, compare_val): (Array, Value)) -> Result<Value, Error> {
Ok(array.matches(compare_val).into())
}
pub fn max((array,): (Array,)) -> Result<Value, Error> {
Ok(array.into_iter().max().unwrap_or_default())
}
pub fn min((array,): (Array,)) -> Result<Value, Error> {
Ok(array.into_iter().min().unwrap_or_default())
}
pub fn pop((mut array,): (Array,)) -> Result<Value, Error> {
Ok(array.pop().into())
}
pub fn prepend((mut array, value): (Array, Value)) -> Result<Value, Error> {
array.insert(0, value);
Ok(array.into())
}
pub fn push((mut array, value): (Array, Value)) -> Result<Value, Error> {
array.push(value);
Ok(array.into())
}
pub fn range((start, count): (i64, i64)) -> Result<Value, Error> {
if count < 0 {
return Err(Error::InvalidArguments {
name: String::from("array::range"),
message: format!(
"Argument 1 was the wrong type. Expected a positive number but found {count}"
),
});
}
if let Some(end) = start.checked_add(count - 1) {
Ok(Array((start..=end).map(Value::from).collect::<Vec<_>>()).into())
} else {
Err(Error::InvalidArguments {
name: String::from("array::range"),
message: String::from("The range overflowed the maximum value for an integer"),
})
}
}
pub async fn reduce(
(stk, ctx, opt, doc): (&mut Stk, &Context, Option<&Options>, Option<&CursorDoc>),
(array, mapper): (Array, Closure),
) -> Result<Value, Error> {
if let Some(opt) = opt {
match array.len() {
0 => Ok(Value::None),
1 => {
let Some(val) = array.into_iter().next() else {
return Err(Error::Unreachable(
"Iterator should have an item at this point".into(),
));
};
Ok(val)
}
_ => {
let mut iter = array.into_iter();
let Some(mut accum) = iter.next() else {
return Ok(Value::None);
};
for (idx, val) in iter.enumerate() {
let fnc = Function::Anonymous(
mapper.clone().into(),
vec![accum, val, idx.into()],
true,
);
accum = fnc.compute(stk, ctx, opt, doc).await?;
}
Ok(accum)
}
}
} else {
Ok(Value::None)
}
}
pub fn remove((mut array, mut index): (Array, i64)) -> Result<Value, Error> {
if index < 0 {
index += array.len() as i64;
}
if index >= array.len() as i64 || index < 0 {
return Ok(array.into());
}
array.remove(index as usize);
Ok(array.into())
}
pub fn repeat((value, count): (Value, usize)) -> Result<Value, Error> {
limit("array::repeat", size_of_val(&value).saturating_mul(count))?;
Ok(Array(std::iter::repeat(value).take(count).collect()).into())
}
pub fn reverse((mut array,): (Array,)) -> Result<Value, Error> {
array.reverse();
Ok(array.into())
}
pub fn shuffle((mut array,): (Array,)) -> Result<Value, Error> {
let mut rng = rand::thread_rng();
array.shuffle(&mut rng);
Ok(array.into())
}
pub fn slice((array, beg, lim): (Array, Option<isize>, Option<isize>)) -> Result<Value, Error> {
let skip = match beg {
Some(v) if v < 0 => array.len().saturating_sub(v.unsigned_abs()),
Some(v) => v as usize,
None => 0,
};
let take = match lim {
Some(v) if v < 0 => array.len().saturating_sub(skip).saturating_sub(v.unsigned_abs()),
Some(v) => v as usize,
None => usize::MAX,
};
Ok(if skip > 0 || take < usize::MAX {
array.into_iter().skip(skip).take(take).collect::<Vec<_>>().into()
} else {
array
}
.into())
}
pub fn sort((mut array, order): (Array, Option<Value>)) -> Result<Value, Error> {
match order {
Some(Value::Strand(s)) if s.as_str() == "asc" => {
array.sort_unstable();
Ok(array.into())
}
Some(Value::Strand(s)) if s.as_str() == "desc" => {
array.sort_unstable_by(|a, b| b.cmp(a));
Ok(array.into())
}
Some(Value::Bool(true)) => {
array.sort_unstable();
Ok(array.into())
}
Some(Value::Bool(false)) => {
array.sort_unstable_by(|a, b| b.cmp(a));
Ok(array.into())
}
_ => {
array.sort_unstable();
Ok(array.into())
}
}
}
pub fn swap((mut array, from, to): (Array, isize, isize)) -> Result<Value, Error> {
let min = 0;
let max = array.len();
let negative_max = -(max as isize);
let from = match from {
from if from < negative_max || from >= max as isize => Err(Error::InvalidArguments {
name: String::from("array::swap"),
message: format!(
"Argument 1 is out of range. Expected a number between {negative_max} and {max}"
),
}),
from if negative_max <= from && from < min => Ok((from + max as isize) as usize),
from => Ok(from as usize),
}?;
let to = match to {
to if to < negative_max || to >= max as isize => Err(Error::InvalidArguments {
name: String::from("array::swap"),
message: format!(
"Argument 2 is out of range. Expected a number between {negative_max} and {max}"
),
}),
to if negative_max <= to && to < min => Ok((to + max as isize) as usize),
to => Ok(to as usize),
}?;
array.swap(from, to);
Ok(array.into())
}
pub fn transpose((array,): (Array,)) -> Result<Value, Error> {
Ok(array.transpose().into())
}
pub fn union((array, other): (Array, Array)) -> Result<Value, Error> {
Ok(array.union(other).into())
}
pub fn windows((array, window_size): (Array, i64)) -> Result<Value, Error> {
let window_size = window_size.max(0) as usize;
Ok(array.windows(window_size)?.into())
}
pub mod sort {
use crate::err::Error;
use crate::sql::array::Array;
use crate::sql::value::Value;
pub fn asc((mut array,): (Array,)) -> Result<Value, Error> {
array.sort_unstable();
Ok(array.into())
}
pub fn desc((mut array,): (Array,)) -> Result<Value, Error> {
array.sort_unstable_by(|a, b| b.cmp(a));
Ok(array.into())
}
}
#[cfg(test)]
mod tests {
use super::{at, first, join, last, slice};
use crate::sql::{Array, Value};
#[test]
fn array_slice() {
fn test(initial: &[u8], beg: Option<isize>, lim: Option<isize>, expected: &[u8]) {
let initial_values =
initial.iter().map(|n| Value::from(*n as i64)).collect::<Vec<_>>().into();
let expected_values: Array =
expected.iter().map(|n| Value::from(*n as i64)).collect::<Vec<_>>().into();
assert_eq!(slice((initial_values, beg, lim)).unwrap(), expected_values.into());
}
let array = b"abcdefg";
test(array, None, None, array);
test(array, Some(2), None, &array[2..]);
test(array, Some(2), Some(3), &array[2..5]);
test(array, Some(2), Some(-1), b"cdef");
test(array, Some(-2), None, b"fg");
test(array, Some(-4), Some(2), b"de");
test(array, Some(-4), Some(-1), b"def");
}
#[test]
fn array_join() {
fn test(arr: Array, sep: &str, expected: &str) {
assert_eq!(join((arr, sep.to_string())).unwrap(), expected.into());
}
test(Vec::<Value>::new().into(), ",", "");
test(vec!["hello"].into(), ",", "hello");
test(vec!["hello", "world"].into(), ",", "hello,world");
test(vec!["again"; 512].into(), " and ", &vec!["again"; 512].join(" and "));
test(
vec![Value::from(true), Value::from(false), Value::from(true)].into(),
" is ",
"true is false is true",
);
test(
vec![Value::from(3.56), Value::from(2.72), Value::from(1.61)].into(),
" is not ",
"3.56f is not 2.72f is not 1.61f",
);
}
#[test]
fn array_first() {
fn test(arr: Array, expected: Value) {
assert_eq!(first((arr,)).unwrap(), expected);
}
test(vec!["hello", "world"].into(), "hello".into());
test(Array::new(), Value::None);
}
#[test]
fn array_last() {
fn test(arr: Array, expected: Value) {
assert_eq!(last((arr,)).unwrap(), expected);
}
test(vec!["hello", "world"].into(), "world".into());
test(Array::new(), Value::None);
}
#[test]
fn array_at() {
fn test(arr: Array, i: i64, expected: Value) {
assert_eq!(at((arr, i)).unwrap(), expected);
}
test(vec!["hello", "world"].into(), -2, "hello".into());
test(vec!["hello", "world"].into(), -3, Value::None);
}
}