use std::sync::Arc;
use ciborium::Value as CborValue;
use indexmap::IndexMap;
use vantage_vista::{Column, Reference, SortDirection, Vista, VistaCapabilities, flags};
use super::DioInner;
#[derive(Default, Clone)]
pub(crate) struct FacadeQuery {
pub(crate) conditions: Vec<(String, CborValue)>,
pub(crate) order: Option<(String, SortDirection)>,
pub(crate) search: Option<String>,
}
impl FacadeQuery {
pub(crate) fn is_empty(&self) -> bool {
self.conditions.is_empty() && self.order.is_none() && self.search.is_none()
}
}
pub struct DioShell {
pub(crate) dio: Arc<DioInner>,
pub(crate) capabilities: VistaCapabilities,
pub(crate) columns: IndexMap<String, Column>,
pub(crate) references: IndexMap<String, Reference>,
pub(crate) id_column: Option<String>,
pub(crate) query: FacadeQuery,
}
impl DioShell {
pub(crate) fn new(dio: Arc<DioInner>) -> Self {
let master = dio.master.read().unwrap();
let master_caps = master.capabilities().clone();
let columns = master
.source
.columns()
.iter()
.map(|(name, column)| {
let mut column = column.clone();
for flag in [flags::ORDERABLE, flags::SEARCHABLE] {
if !column.has_flag(flag) {
column = column.with_flag(flag);
}
}
(name.clone(), column)
})
.collect();
let references = master.source.references().clone();
let id_column = master.source.id_column().map(str::to_string);
drop(master);
let has_on_event = dio.lens.callbacks.on_event.is_some();
let write_caps = dio.write_capabilities();
let capabilities = VistaCapabilities {
can_count: true,
can_insert: write_caps.can_insert,
can_update: write_caps.can_update,
can_delete: write_caps.can_delete,
can_subscribe: true,
can_invalidate: master_caps.can_invalidate || has_on_event,
can_order: true,
can_search: true,
can_filter_operators: master_caps.can_filter_operators,
can_set_page_size: master_caps.can_set_page_size,
can_fetch_page: master_caps.can_fetch_page,
can_fetch_next: master_caps.can_fetch_next,
can_fetch_window: true,
can_traverse_to_record: master_caps.can_traverse_to_record,
can_traverse_to_set: master_caps.can_traverse_to_set,
can_build_ref_via_script: master_caps.can_build_ref_via_script,
can_traverse_in_columns: master_caps.can_traverse_in_columns,
};
Self {
dio,
capabilities,
columns,
references,
id_column,
query: FacadeQuery::default(),
}
}
pub(crate) fn plan(&self) -> (Option<Vista>, FacadeQuery) {
if self.query.is_empty() {
return (None, FacadeQuery::default());
}
if self.dio.is_two_pass() {
return (None, self.query.clone());
}
let master = self.dio.master.read().unwrap();
let Some(shell) = master.source.clone_shell() else {
return (None, self.query.clone());
};
let mut narrowed = Vista::new(master.name(), shell);
drop(master);
let mut local = FacadeQuery::default();
let mut pushed = false;
for (field, value) in &self.query.conditions {
if narrowed
.add_condition_eq(field.clone(), value.clone())
.is_ok()
{
pushed = true;
} else {
local.conditions.push((field.clone(), value.clone()));
}
}
if let Some((column, direction)) = &self.query.order {
if narrowed.add_order(column, *direction).is_ok() {
pushed = true;
} else {
local.order = Some((column.clone(), *direction));
}
}
if let Some(text) = &self.query.search {
if narrowed.add_search(text.clone()).is_ok() {
pushed = true;
} else {
local.search = Some(text.clone());
}
}
(pushed.then_some(narrowed), local)
}
}