use ciborium::Value as CborValue;
use vantage_core::{Result, error};
use vantage_dataset::traits::ReadableValueSet;
use vantage_table::sorting::{OrderBy, SortDirection as TableSortDirection};
use vantage_types::{Entity, Record};
use vantage_vista::{
Capability, Condition, FilterOp, SortDirection, condition_column_error, unsupported,
};
use super::dialect::{Shell, SqlShellDialect};
use super::shell::cbor_record;
fn comparison(op: FilterOp) -> Option<&'static str> {
Some(match op {
FilterOp::Eq => "{} = {}",
FilterOp::Ne => "{} != {}",
FilterOp::Gt => "{} > {}",
FilterOp::Gte => "{} >= {}",
FilterOp::Lt => "{} < {}",
FilterOp::Lte => "{} <= {}",
_ => return None,
})
}
impl<T, E> Shell<T, E>
where
T: SqlShellDialect,
E: Entity<T::Value> + 'static,
{
pub(super) fn apply_condition(&mut self, condition: &Condition) -> Result<()> {
let field = condition.column.as_str();
if field.contains('.') {
return Err(condition_column_error(&self.metadata, field));
}
let column = self
.table
.columns()
.get(field)
.ok_or_else(|| error!("Unknown column for condition", field = field))?
.clone();
let value = &condition.value;
if let Some(template) = comparison(condition.op) {
let cell = T::cell(value.clone());
self.table
.add_condition(T::compare(&column, template, cell));
return Ok(());
}
match condition.op {
op @ (FilterOp::InSet | FilterOp::NotInSet) => {
let CborValue::Array(items) = value else {
return Err(error!(
"in_set/not_in_set requires an array value",
field = field
));
};
let values: Vec<T::Value> = items.iter().map(|v| T::cell(v.clone())).collect();
let negate = op == FilterOp::NotInSet;
self.table
.add_condition(T::in_list(&column, &values, negate));
}
FilterOp::Like => {
let pattern = crate::like_pattern(value);
self.table.add_condition(T::like(field, pattern));
}
_ => return unsupported(Capability::FilterOps),
}
Ok(())
}
pub(super) fn apply_search(&mut self, text: Option<&str>) -> Result<()> {
let condition = text.map(|text| {
self.table
.data_source()
.search_table_condition(&self.table, text)
});
self.current_search_handle = self
.table
.replace_temp_condition(self.current_search_handle, condition)?;
Ok(())
}
pub(super) async fn read_back(&self, id: &str) -> Result<Record<CborValue>> {
let mut table = self.table.clone();
table.replace_temp_condition(self.current_search_handle, None)?;
table
.get_value(id)
.await?
.map(cbor_record::<T>)
.ok_or_else(|| error!("inserted row can't be read back through the set", id = id))
}
pub(super) fn apply_order(&mut self, order: Option<(&str, SortDirection)>) -> Result<()> {
let Some((field, dir)) = order else {
self.table.clear_orders();
return Ok(());
};
if !self.table.columns().contains_key(field) {
return Err(error!("Unknown column for order", field = field));
}
self.table.clear_orders();
let direction = match dir {
SortDirection::Ascending => TableSortDirection::Ascending,
SortDirection::Descending => TableSortDirection::Descending,
};
self.table.add_order(OrderBy {
expression: T::order_term(field),
direction,
});
Ok(())
}
}