use async_trait::async_trait;
use ciborium::Value as CborValue;
use indexmap::IndexMap;
use vantage_core::error;
use vantage_dataset::traits::{ReadableValueSet, Result};
use vantage_expressions::Expression;
use vantage_expressions::traits::associated_expressions::AssociatedExpression;
use vantage_expressions::traits::datasource::DataSource;
use vantage_expressions::traits::expressive::ExpressiveEnum;
use vantage_table::column::core::{Column, ColumnType};
use vantage_table::table::Table;
use vantage_table::traits::table_source::TableSource;
use vantage_types::{Entity, Record};
use vantage_expressions::traits::datasource::ExprDataSource;
use vantage_expressions::traits::expressive::DeferredFn;
use crate::RestApi;
use crate::rest::api::Order;
fn unsupported(method: &'static str) -> vantage_core::VantageError {
error!("REST data source supports reads only", method = method).mark_unsupported()
}
fn cbor_preview(expr: &Expression<CborValue>) -> String {
let mut parts = expr.template.split("{}");
let mut out = String::from(parts.next().unwrap_or(""));
for param in &expr.parameters {
match param {
ExpressiveEnum::Scalar(CborValue::Text(s)) => out.push_str(&format!("{s:?}")),
ExpressiveEnum::Scalar(v) => out.push_str(&vantage_vista::operand_text(v)),
ExpressiveEnum::Nested(e) => out.push_str(&cbor_preview(e)),
ExpressiveEnum::Deferred(_) => out.push_str("**deferred()"),
}
out.push_str(parts.next().unwrap_or(""));
}
out
}
fn id_field_name<E: Entity<CborValue>>(table: &Table<RestApi, E>) -> Option<String> {
table.id_field().map(|col| col.name().to_string())
}
impl RestApi {
pub(crate) async fn list_ordered<E: Entity<CborValue>>(
&self,
table: &Table<RestApi, E>,
order: Order<'_>,
) -> Result<IndexMap<String, Record<CborValue>>> {
self.fetch_records(
table.table_name(),
id_field_name(table).as_deref(),
&row_fields(table),
table.pagination(),
order,
table.conditions(),
)
.await
}
}
pub(crate) fn row_fields<E: Entity<CborValue>>(table: &Table<RestApi, E>) -> Vec<String> {
let placeholders: Vec<&str> = crate::rest::placeholders(table.table_name()).collect();
let mut fields = table.row_fields();
fields.retain(|name| !placeholders.contains(&name.as_str()));
fields
}
impl ExprDataSource<CborValue> for RestApi {
async fn execute(&self, expr: &Expression<CborValue>) -> vantage_core::Result<CborValue> {
if expr.parameters.is_empty() {
return Ok(CborValue::Text(expr.template.clone()));
}
if expr.template == "{}" && expr.parameters.len() == 1 {
let resolved = expr.resolve_deferred().await?;
if let Some(ExpressiveEnum::Scalar(value)) = resolved.parameters.into_iter().next() {
return Ok(value);
}
}
Ok(CborValue::Null)
}
fn defer(&self, expr: Expression<CborValue>) -> DeferredFn<CborValue> {
let api = self.clone();
DeferredFn::new(move || {
let api = api.clone();
let expr = expr.clone();
Box::pin(async move {
let result = api.execute(&expr).await?;
Ok(ExpressiveEnum::Scalar(result))
})
})
}
}
impl DataSource for RestApi {}
#[async_trait]
impl TableSource for RestApi {
type Column<Type>
= Column<Type>
where
Type: ColumnType;
type AnyType = CborValue;
type Value = CborValue;
type Id = String;
type Condition = vantage_expressions::Expression<Self::Value>;
type Source = String;
fn eq_condition(field: &str, value: &str) -> Result<Self::Condition> {
Ok(crate::eq_condition(field, value.to_string()))
}
fn eq_value_condition(&self, field: &str, value: Self::Value) -> Result<Self::Condition> {
Ok(crate::eq_condition(field, value))
}
fn in_values_condition(
&self,
field: &str,
values: Vec<Self::Value>,
) -> Result<Self::Condition> {
match <[CborValue; 1]>::try_from(values) {
Ok([value]) => Ok(crate::eq_condition(field, value)),
Err(values) => Err(error!(
"a REST filter takes exactly one value",
field = field,
values = values.len()
)
.mark_unsupported()),
}
}
fn describe_condition(&self, condition: &Self::Condition) -> String {
cbor_preview(condition)
}
fn condition_column(&self, condition: &Self::Condition) -> Option<String> {
vantage_table::conditions::condition_column_of(condition)
}
fn create_column<Type: ColumnType>(&self, name: &str) -> Self::Column<Type> {
Column::new(name)
}
fn to_any_column<Type: ColumnType>(
&self,
column: Self::Column<Type>,
) -> Self::Column<Self::AnyType> {
Column::from_column(column)
}
fn convert_any_column<Type: ColumnType>(
&self,
any_column: Self::Column<Self::AnyType>,
) -> Option<Self::Column<Type>> {
Some(Column::from_column(any_column))
}
fn expr(
&self,
template: impl Into<String>,
parameters: Vec<ExpressiveEnum<Self::Value>>,
) -> Expression<Self::Value> {
Expression::new(template, parameters)
}
fn search_table_condition<E>(
&self,
_table: &Table<Self, E>,
search_value: &str,
) -> Expression<Self::Value>
where
E: Entity<Self::Value>,
{
Expression::new(format!("SEARCH '{}'", search_value), vec![])
}
async fn list_table_values<E>(
&self,
table: &Table<Self, E>,
) -> Result<IndexMap<Self::Id, Record<Self::Value>>>
where
E: Entity<Self::Value>,
Self: Sized,
{
self.list_ordered(table, None).await
}
async fn get_table_value<E>(
&self,
table: &Table<Self, E>,
id: &Self::Id,
) -> Result<Option<Record<Self::Value>>>
where
E: Entity<Self::Value>,
Self: Sized,
{
let records = self.list_table_values(table).await?;
Ok(records.get(id).cloned())
}
async fn get_table_some_value<E>(
&self,
table: &Table<Self, E>,
) -> Result<Option<(Self::Id, Record<Self::Value>)>>
where
E: Entity<Self::Value>,
Self: Sized,
{
let records = self.list_table_values(table).await?;
Ok(records.into_iter().next())
}
async fn get_table_count<E>(&self, table: &Table<Self, E>) -> Result<i64>
where
E: Entity<Self::Value>,
Self: Sized,
{
let records = self.list_table_values(table).await?;
Ok(records.len() as i64)
}
async fn get_table_sum<E>(
&self,
_table: &Table<Self, E>,
_column: &Self::Column<Self::AnyType>,
) -> Result<Self::Value>
where
E: Entity<Self::Value>,
Self: Sized,
{
Err(unsupported("get_table_sum"))
}
async fn get_table_max<E>(
&self,
_table: &Table<Self, E>,
_column: &Self::Column<Self::AnyType>,
) -> Result<Self::Value>
where
E: Entity<Self::Value>,
Self: Sized,
{
Err(unsupported("get_table_max"))
}
async fn get_table_min<E>(
&self,
_table: &Table<Self, E>,
_column: &Self::Column<Self::AnyType>,
) -> Result<Self::Value>
where
E: Entity<Self::Value>,
Self: Sized,
{
Err(unsupported("get_table_min"))
}
async fn insert_table_value<E>(
&self,
_table: &Table<Self, E>,
_id: &Self::Id,
_record: &Record<Self::Value>,
) -> Result<Record<Self::Value>>
where
E: Entity<Self::Value>,
Self: Sized,
{
Err(unsupported("insert_table_value"))
}
async fn replace_table_value<E>(
&self,
_table: &Table<Self, E>,
_id: &Self::Id,
_record: &Record<Self::Value>,
) -> Result<Record<Self::Value>>
where
E: Entity<Self::Value>,
Self: Sized,
{
Err(unsupported("replace_table_value"))
}
async fn patch_table_value<E>(
&self,
_table: &Table<Self, E>,
_id: &Self::Id,
_partial: &Record<Self::Value>,
) -> Result<Record<Self::Value>>
where
E: Entity<Self::Value>,
Self: Sized,
{
Err(unsupported("patch_table_value"))
}
async fn delete_table_value<E>(&self, _table: &Table<Self, E>, _id: &Self::Id) -> Result<()>
where
E: Entity<Self::Value>,
Self: Sized,
{
Err(unsupported("delete_table_value"))
}
async fn delete_table_all_values<E>(&self, _table: &Table<Self, E>) -> Result<()>
where
E: Entity<Self::Value>,
Self: Sized,
{
Err(unsupported("delete_table_all_values"))
}
async fn insert_table_return_id_value<E>(
&self,
_table: &Table<Self, E>,
_record: &Record<Self::Value>,
) -> Result<Self::Id>
where
E: Entity<Self::Value>,
Self: Sized,
{
Err(unsupported("insert_table_return_id_value"))
}
fn related_in_condition<SourceE: Entity<Self::Value> + 'static>(
&self,
target_field: &str,
source_table: &Table<Self, SourceE>,
source_column: &str,
) -> Self::Condition
where
Self: Sized,
{
for cond in source_table.conditions() {
if let Some((field, value)) = crate::condition_to_query_param(cond)
&& field == source_column
{
let cbor_value: CborValue = if let Ok(i) = value.parse::<i64>() {
CborValue::Integer(i.into())
} else if let Ok(f) = value.parse::<f64>() {
CborValue::Float(f)
} else {
CborValue::Text(value)
};
return crate::eq_condition(target_field, cbor_value);
}
}
let parent = source_table.clone();
let column = source_column.to_string();
let parent_name = source_table.table_name().to_string();
let deferred = DeferredFn::new(move || {
let parent = parent.clone();
let column = column.clone();
let parent_name = parent_name.clone();
Box::pin(async move {
let records = parent.list_values().await?;
let value = records
.values()
.next()
.and_then(|r| r.get(&column))
.cloned()
.ok_or_else(|| {
error!(
"Deferred FK resolve: parent yielded no row or column missing",
table = parent_name,
column = column
)
})?;
Ok(ExpressiveEnum::Scalar(value))
})
});
Expression::new(
"{} = {}",
vec![
ExpressiveEnum::Nested(Expression::new(target_field.to_string(), vec![])),
ExpressiveEnum::Nested(Expression::new(
"{}",
vec![ExpressiveEnum::Deferred(deferred)],
)),
],
)
}
fn column_table_values_expr<'a, E, Type: ColumnType>(
&'a self,
table: &Table<Self, E>,
column: &Self::Column<Type>,
) -> AssociatedExpression<'a, Self, Self::Value, Vec<Type>>
where
E: Entity<Self::Value> + 'static,
Self: Sized,
{
let api = self.clone();
let table = table.clone();
let column = column.name().to_string();
let values = DeferredFn::new(move || {
let (api, table, column) = (api.clone(), table.clone(), column.clone());
Box::pin(async move {
let records = api.list_table_values(&table).await?;
let values = records
.values()
.filter_map(|r| r.get(&column).cloned())
.collect();
Ok(ExpressiveEnum::Scalar(CborValue::Array(values)))
})
});
let expr = Expression::new("{}", vec![ExpressiveEnum::Deferred(values)]);
AssociatedExpression::new(expr, self)
}
}