use ciborium::Value as CborValue;
use vantage_core::{Result, error};
use super::Vista;
use crate::{
capabilities::{Capability, unsupported},
column::ColumnFlag,
filter::FilterOp,
page::NativeCondition,
query::{Condition, VistaQuery},
sort::SortDirection,
};
impl Vista {
pub fn with_condition(
mut self,
column: &str,
op: FilterOp,
value: impl Into<CborValue>,
) -> Result<Self> {
self.add_condition(column, op, value)?;
Ok(self)
}
pub fn with_id(self, id: impl Into<CborValue>) -> Result<Self> {
let id_column = self
.id_column()
.ok_or_else(|| error!("vista has no id column", table = self.name()))?
.to_string();
self.with_condition(&id_column, FilterOp::Eq, id)
}
pub fn with_search(mut self, text: impl Into<String>) -> Result<Self> {
self.set_search(Some(&text.into()))?;
Ok(self)
}
pub fn with_order(mut self, column: &str, dir: SortDirection) -> Result<Self> {
self.set_order(Some((column, dir)))?;
Ok(self)
}
pub fn with_native_condition(mut self, condition: NativeCondition) -> Result<Self> {
self.add_native_condition(condition)?;
Ok(self)
}
pub fn with_query(mut self, query: &VistaQuery) -> Result<(Self, VistaQuery)> {
refuse_natives(query)?;
let mut refused = VistaQuery::default();
for c in &query.conditions {
if refusal(self.add_condition(&c.column, c.op, c.value.clone()))? {
refused.conditions.push(c.clone());
}
}
if let Some(text) = &query.search
&& refusal(self.set_search(Some(text)))?
{
refused.search = Some(text.clone());
}
if let Some((column, dir)) = &query.order
&& refusal(self.set_order(Some((column, *dir))))?
{
refused.order = Some((column.clone(), *dir));
}
Ok((self, refused))
}
pub fn add_condition(
&mut self,
column: &str,
op: FilterOp,
value: impl Into<CborValue>,
) -> Result<()> {
self.refuse_computed(column, "condition")?;
if op != FilterOp::Eq && !self.capabilities().has(Capability::FilterOps) {
self.require_declared(column, "add_condition")?;
return unsupported(Capability::FilterOps);
}
let condition = Condition::new(column, op, value);
self.shell_mut().add_condition(&condition)?;
self.applied.conditions.push(condition);
Ok(())
}
pub fn set_search(&mut self, text: Option<&str>) -> Result<()> {
let Some(text) = text else {
if self.applied.search.is_some() {
self.shell_mut().set_search(None)?;
self.applied.search = None;
}
return Ok(());
};
if !self.capabilities().has(Capability::Search) {
return unsupported(Capability::Search);
}
self.shell_mut().set_search(Some(text))?;
self.applied.search = Some(text.to_string());
Ok(())
}
pub fn set_order(&mut self, order: Option<(&str, SortDirection)>) -> Result<()> {
let Some((column, dir)) = order else {
if self.applied.order.is_some() {
self.shell_mut().set_order(None)?;
self.applied.order = None;
}
return Ok(());
};
self.require_declared(column, "set_order")?;
self.refuse_computed(column, "order")?;
if !self.capabilities().has(Capability::Order) {
return unsupported(Capability::Order);
}
if !self
.column(column)
.is_some_and(|c| c.has_flag(ColumnFlag::Orderable))
{
return Err(error!("Column is not orderable", column = column).mark_unsupported());
}
self.shell_mut().set_order(Some((column, dir)))?;
self.applied.order = Some((column.to_string(), dir));
Ok(())
}
pub fn add_native_condition(&mut self, condition: NativeCondition) -> Result<()> {
let key = condition.key().to_string();
self.shell_mut().add_native_condition(condition)?;
self.applied.native.push(key);
Ok(())
}
fn require_declared(&self, column: &str, verb: &'static str) -> Result<()> {
let root = column.split('.').next().unwrap_or(column);
if self.columns().is_empty() || self.column(column).is_some() || self.column(root).is_some()
{
return Ok(());
}
Err(error!(
"Unknown column",
column = column,
verb = verb,
table = self.name()
))
}
}
fn refuse_natives(query: &VistaQuery) -> Result<()> {
if query.native.is_empty() {
return Ok(());
}
Err(error!(
"native conditions can't be re-applied from a VistaQuery",
native = query.native.join(", ")
)
.mark_incorrect_usage())
}
fn refusal(result: Result<()>) -> Result<bool> {
match result {
Ok(()) => Ok(false),
Err(e) if e.is_unsupported() || e.is_unimplemented() => Ok(true),
Err(e) => Err(e),
}
}