use std::cmp::Ordering;
use ciborium::Value as CborValue;
use vantage_core::{Result, error};
use vantage_types::Record;
use crate::{
filter::{FilterOp, operand_text},
metadata::VistaMetadata,
query::{Condition, VistaQuery},
sort::SortDirection,
};
mod compare;
mod path;
mod search;
use compare::datetime_as_text;
use search::matches_search;
pub use compare::cbor_cmp;
pub use path::cell_get;
pub(crate) use path::path_get;
type Rec = Record<CborValue>;
impl Condition {
pub fn matches(&self, row: &Rec, metadata: &VistaMetadata) -> bool {
let cell = cell_get(row, &self.column, metadata);
let operand = &self.value;
match self.op {
FilterOp::Eq => cell.is_some_and(|c| same(c, operand)),
FilterOp::Ne => !cell.is_some_and(|c| same(c, operand)),
FilterOp::InSet => in_set(cell, operand),
FilterOp::NotInSet => !in_set(cell, operand),
FilterOp::Like => cell.is_some_and(|c| {
operand_text(c)
.to_lowercase()
.contains(&operand_text(operand).to_lowercase())
}),
op => cell.is_some_and(|c| op.matches_ordering(cbor_cmp(c, operand))),
}
}
}
impl VistaQuery {
pub fn matches(&self, row: &Rec, metadata: &VistaMetadata) -> Result<bool> {
if !self.is_locally_evaluable() {
return Err(error!(
"native conditions can't be evaluated in memory",
native = self.native.join(", ")
)
.mark_incorrect_usage());
}
Ok(self.conditions.iter().all(|c| c.matches(row, metadata))
&& self
.search
.as_deref()
.is_none_or(|needle| matches_search(needle, row, metadata)))
}
pub fn sort_rows(&self, rows: &mut [(String, Rec)], metadata: &VistaMetadata) {
if self.order.is_some() {
rows.sort_by(|(a_id, a), (b_id, b)| self.cmp_rows((a_id, a), (b_id, b), metadata));
}
}
pub fn cmp_rows(
&self,
(a_id, a): (&str, &Rec),
(b_id, b): (&str, &Rec),
metadata: &VistaMetadata,
) -> Ordering {
fn cell<'a>(row: &'a Rec, column: &str, metadata: &VistaMetadata) -> Option<&'a CborValue> {
cell_get(row, column, metadata).filter(|v| !v.is_null())
}
let ordering = match &self.order {
None => Ordering::Equal,
Some((column, dir)) => match (cell(a, column, metadata), cell(b, column, metadata)) {
(None, None) => Ordering::Equal,
(None, Some(_)) => Ordering::Greater,
(Some(_), None) => Ordering::Less,
(Some(l), Some(r)) => match dir {
SortDirection::Ascending => cbor_cmp(l, r),
SortDirection::Descending => cbor_cmp(r, l),
},
},
};
ordering.then_with(|| a_id.cmp(b_id))
}
pub fn is_locally_evaluable(&self) -> bool {
self.native.is_empty()
}
}
fn same(a: &CborValue, b: &CborValue) -> bool {
let (a, b) = (datetime_as_text(a), datetime_as_text(b));
let int_and_float = matches!(
(a, b),
(CborValue::Integer(_), CborValue::Float(_)) | (CborValue::Float(_), CborValue::Integer(_))
);
a == b || (int_and_float && cbor_cmp(a, b) == Ordering::Equal)
}
fn in_set(cell: Option<&CborValue>, operand: &CborValue) -> bool {
match (cell, operand) {
(Some(cell), CborValue::Array(items)) => items.iter().any(|item| same(cell, item)),
_ => false,
}
}