use std::sync::Arc;
use async_trait::async_trait;
use ciborium::Value as CborValue;
use indexmap::IndexMap;
use vantage_core::{Result, error};
use vantage_dataset::traits::ReadableValueSet;
use vantage_table::table::Table;
use vantage_table::traits::table_source::TableSource;
use vantage_types::{EmptyEntity, Record};
use vantage_vista::{
Capability, Condition, FilterOp, Page, Preview, Reference as VistaReference, SortDirection,
TableShell, Vista, VistaCapabilities, VistaMetadata, VistaResolver, condition_column_error,
unknown_relation, unsupported,
};
use super::factory::RestApiVistaFactory;
use crate::RestApi;
use crate::rest::api::REST_DRIVER;
#[derive(Clone)]
pub struct RestApiTableShell {
pub(crate) table: Table<RestApi, EmptyEntity>,
pub(crate) capabilities: VistaCapabilities,
pub(crate) metadata: VistaMetadata,
pub(crate) resolver: Option<Arc<dyn VistaResolver>>,
pub(crate) order: Option<(String, SortDirection)>,
}
impl RestApiTableShell {
pub(crate) fn new(
table: Table<RestApi, EmptyEntity>,
capabilities: VistaCapabilities,
metadata: VistaMetadata,
) -> Self {
Self {
table,
capabilities,
metadata,
resolver: None,
order: None,
}
}
pub(crate) fn with_resolver(mut self, resolver: Arc<dyn VistaResolver>) -> Self {
self.resolver = Some(resolver);
self
}
fn order(&self) -> Option<(&str, SortDirection)> {
self.order
.as_ref()
.map(|(field, dir)| (field.as_str(), *dir))
}
fn ref_target(&self, reference: &VistaReference) -> Result<Vista> {
if let Some(resolver) = &self.resolver {
return resolver.vista(&reference.target);
}
let target = self.table.get_ref_target_erased(&reference.name)?;
RestApiVistaFactory::new(self.table.data_source().clone()).from_table(target)
}
}
#[async_trait]
impl TableShell for RestApiTableShell {
fn metadata(&self) -> &VistaMetadata {
&self.metadata
}
fn capabilities(&self) -> &VistaCapabilities {
&self.capabilities
}
fn driver_name(&self) -> &'static str {
REST_DRIVER
}
fn preview(&self) -> Preview {
let window = self.table.pagination().map(|p| (p.skip(), p.limit()));
self.table.data_source().preview_request(
self.table.table_name(),
window,
self.order(),
self.table.conditions(),
)
}
async fn list(&self) -> Result<IndexMap<String, Record<CborValue>>> {
self.table.check_read().await?;
let api = self.table.data_source();
let mut rows = api.list_ordered(&self.table, self.order()).await?;
for rec in rows.values_mut() {
self.table.apply_lazy_expressions(rec).await?;
}
Ok(rows)
}
async fn get(&self, id: &str) -> Result<Option<Record<CborValue>>> {
let mut data = self.table.list_values().await?;
Ok(data.shift_remove(id))
}
async fn count(&self) -> Result<i64> {
self.table.check_read().await?;
let api = self.table.data_source();
if let Some(total) = api
.fetch_total(self.table.table_name(), self.table.conditions())
.await?
{
return Ok(total);
}
api.get_table_count(&self.table).await
}
async fn fetch_window(&self, offset: usize, limit: usize) -> Result<Page> {
if !self.capabilities.has(Capability::Window) {
return Ok(Page::from_listing(self.list().await?, offset, limit));
}
self.table.check_read().await?;
let id_field = self.table.id_field().map(|c| c.name().to_string());
let (records, total) = self
.table
.data_source()
.fetch_window_records_counted(
self.table.table_name(),
id_field.as_deref(),
&crate::rest::row_fields(&self.table),
(offset as i64, limit as i64),
self.order(),
self.table.conditions(),
)
.await?;
let mut rows: Vec<(String, Record<CborValue>)> = records.into_iter().collect();
for (_, rec) in rows.iter_mut() {
self.table.apply_lazy_expressions(rec).await?;
}
let page = Page::new(rows);
Ok(match total {
Some(total) => page.with_total(total),
None => page,
})
}
fn add_condition(&mut self, condition: &Condition) -> Result<()> {
let field = condition.column.as_str();
let known = self.metadata.columns.contains_key(field)
|| crate::rest::placeholders(self.table.table_name()).any(|p| p == field);
if !known {
return Err(condition_column_error(&self.metadata, field));
}
if condition.op != FilterOp::Eq || crate::cbor_to_query_string(&condition.value).is_none() {
return unsupported(Capability::FilterOps);
}
let condition = crate::eq_condition(field, condition.value.clone());
self.table.add_condition(condition);
Ok(())
}
fn set_order(&mut self, order: Option<(&str, SortDirection)>) -> Result<()> {
if !self.capabilities.has(Capability::Order) {
return unsupported(Capability::Order);
}
let Some((field, dir)) = order else {
self.order = None;
return Ok(());
};
if !self.table.columns().contains_key(field) {
return Err(error!("Unknown column for order", field = field));
}
self.order = Some((field.to_string(), dir));
Ok(())
}
fn get_ref(&self, relation: &str, row: Option<&Record<CborValue>>) -> Result<Vista> {
let reference = self
.metadata
.references
.get(relation)
.ok_or_else(|| unknown_relation(relation))?;
let target = self.ref_target(reference)?;
match row {
Some(row) => reference.narrow(target, row),
None => Ok(target),
}
}
}