use ciborium::Value as CborValue;
use vantage_core::{Result, error};
use vantage_table::column::core::Column as TableColumn;
use vantage_table::table::{Table, VistaMetadataOptions};
use vantage_types::{EmptyEntity, Entity};
use vantage_vista::{Capability, NoExtras, Vista, VistaCapabilities, VistaFactory, VistaMetadata};
use crate::cmd::{Cmd, CmdSpec};
use crate::vista::source::CmdTableShell;
use crate::vista::spec::{CmdColumnExtras, CmdTableExtras, CmdVistaSpec};
pub struct CmdVistaFactory {
cmd: Cmd,
}
impl CmdVistaFactory {
pub fn new(cmd: Cmd) -> Self {
Self { cmd }
}
pub fn from_table<E>(&self, table: Table<Cmd, E>) -> Result<Vista>
where
E: Entity<CborValue> + 'static,
{
let name = table.table_name().to_string();
let metadata = metadata_from_table(&table);
let any_table = table.into_entity::<EmptyEntity>();
Ok(wrap(any_table, name, metadata))
}
pub(crate) fn build_columns_table(
&self,
spec: &CmdVistaSpec,
) -> Result<Table<Cmd, EmptyEntity>> {
let cmd_spec = {
let mut cs = CmdSpec::new(spec.driver.cmd.rhai.clone());
cs.command = spec.driver.cmd.command.clone();
cs.env = spec.driver.cmd.env.clone();
cs.detail = spec.driver.cmd.detail.clone().map(Into::into);
cs
};
let cmd = self.cmd.clone().with_table(&spec.name, cmd_spec);
let mut table = Table::<Cmd, EmptyEntity>::new(&spec.name, cmd);
table.add_spec_columns(&spec.columns, build_column)?;
let id_column = spec.resolve_id_column("id");
if table.columns().contains_key(&id_column) {
table.set_id_field(&id_column);
}
Ok(table)
}
}
fn wrap(table: Table<Cmd, EmptyEntity>, name: String, metadata: VistaMetadata) -> Vista {
let capabilities: VistaCapabilities = [Capability::Window, Capability::Traverse]
.into_iter()
.collect();
Vista::new(name, CmdTableShell::new(table, capabilities, metadata))
}
impl VistaFactory for CmdVistaFactory {
type TableExtras = CmdTableExtras;
type ColumnExtras = CmdColumnExtras;
type ReferenceExtras = NoExtras;
fn build_from_spec(&self, spec: CmdVistaSpec) -> Result<Vista> {
let name = spec.name.clone();
let table = self.build_columns_table(&spec)?;
let metadata = metadata_from_table(&table);
Ok(wrap(table, name, metadata))
}
}
fn build_column(
name: &str,
col_spec: &vantage_vista::ColumnSpec<CmdColumnExtras>,
) -> Result<TableColumn<CborValue>> {
TableColumn::from_spec(name, col_spec, None, column_for_type)
}
fn column_for_type(name: &str, ty: &str) -> Result<TableColumn<CborValue>> {
let col: TableColumn<CborValue> = match ty {
"int" | "integer" | "i64" | "i32" => {
TableColumn::from_column(TableColumn::<i64>::new(name))
}
"float" | "double" | "f64" | "f32" => {
TableColumn::from_column(TableColumn::<f64>::new(name))
}
"bool" | "boolean" => TableColumn::from_column(TableColumn::<bool>::new(name)),
"string" | "text" | "str" => TableColumn::from_column(TableColumn::<String>::new(name)),
"json" => TableColumn::from_column(TableColumn::<serde_json::Value>::new(name)),
other => {
return Err(error!(
"Unknown YAML column type",
column = name.to_string(),
ty = other.to_string()
));
}
};
Ok(col)
}
pub(crate) fn metadata_from_table<E>(table: &Table<Cmd, E>) -> VistaMetadata
where
E: Entity<CborValue>,
{
table.vista_metadata(VistaMetadataOptions {
id_flag: true,
references: true,
..VistaMetadataOptions::default()
})
}