use async_trait::async_trait;
use ciborium::Value as CborValue;
use indexmap::IndexMap;
use vantage_core::Result;
use vantage_dataset::traits::ReadableValueSet;
use vantage_table::table::Table;
use vantage_types::{EmptyEntity, Record};
use vantage_vista::{
Capability, Condition, FilterOp, Page, Preview, TableShell, Vista, VistaCapabilities,
VistaMetadata, condition_column_error, unknown_relation, unsupported,
};
use crate::csv::Csv;
use crate::operation::CsvOperation;
use crate::type_system::AnyCsvType;
use crate::vista::factory::CsvVistaFactory;
#[derive(Clone)]
pub struct CsvTableShell {
pub(crate) table: Table<Csv, EmptyEntity>,
pub(crate) capabilities: VistaCapabilities,
pub(crate) metadata: VistaMetadata,
}
impl CsvTableShell {
pub(crate) fn new(
table: Table<Csv, EmptyEntity>,
capabilities: VistaCapabilities,
metadata: VistaMetadata,
) -> Self {
Self {
table,
capabilities,
metadata,
}
}
async fn read_all(&self) -> Result<IndexMap<String, Record<CborValue>>> {
let raw = self.table.list_values().await?;
Ok(raw
.into_iter()
.map(|(id, record)| (id, csv_record_to_cbor(record)))
.collect())
}
}
fn csv_record_to_cbor(record: Record<AnyCsvType>) -> Record<CborValue> {
record.into_iter().map(|(k, v)| (k, v.into())).collect()
}
#[async_trait]
impl TableShell for CsvTableShell {
fn metadata(&self) -> &VistaMetadata {
&self.metadata
}
fn capabilities(&self) -> &VistaCapabilities {
&self.capabilities
}
fn driver_name(&self) -> &'static str {
"csv"
}
fn preview(&self) -> Preview {
let file = self
.table
.data_source()
.base_dir()
.join(format!("{}.csv", self.table.table_name()));
let mut query = format!("read {}", file.display());
let conditions: Vec<String> = self.table.conditions().map(|c| c.preview()).collect();
if !conditions.is_empty() {
query.push_str(" where ");
query.push_str(&conditions.join(" and "));
}
Preview::new().with_query(query)
}
async fn list(&self) -> Result<IndexMap<String, Record<CborValue>>> {
self.read_all().await
}
async fn get(&self, id: &str) -> Result<Option<Record<CborValue>>> {
Ok(self.read_all().await?.shift_remove(id))
}
async fn count(&self) -> Result<i64> {
Ok(self.read_all().await?.len() as i64)
}
async fn fetch_window(&self, offset: usize, limit: usize) -> Result<Page> {
Ok(Page::from_listing(self.read_all().await?, offset, limit))
}
fn add_condition(&mut self, condition: &Condition) -> Result<()> {
let field = condition.column.as_str();
let column = self
.table
.columns()
.get(field)
.ok_or_else(|| condition_column_error(&self.metadata, field))?
.clone();
if condition.op != FilterOp::Eq {
return unsupported(Capability::FilterOps);
}
let value: AnyCsvType = condition.value.clone().into();
self.table.add_condition(column.eq(value));
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))?;
self.table.traverse_erased(reference, row, |target| {
CsvVistaFactory::new(self.table.data_source().clone()).from_table(target)
})
}
}