Skip to main content

vantage_api_client/
table_source.rs

1use async_trait::async_trait;
2use indexmap::IndexMap;
3use serde_json::Value;
4use vantage_core::error;
5use vantage_dataset::traits::Result;
6use vantage_expressions::Expression;
7use vantage_expressions::traits::associated_expressions::AssociatedExpression;
8use vantage_expressions::traits::datasource::DataSource;
9use vantage_expressions::traits::expressive::ExpressiveEnum;
10use vantage_table::column::core::{Column, ColumnType};
11use vantage_table::table::Table;
12use vantage_table::traits::table_source::TableSource;
13use vantage_types::{Entity, Record};
14
15use vantage_expressions::traits::datasource::ExprDataSource;
16use vantage_expressions::traits::expressive::DeferredFn;
17
18use crate::RestApi;
19
20/// Extract the id field name from a table's column flags.
21fn id_field_name<E: Entity<Value>>(table: &Table<RestApi, E>) -> Option<String> {
22    table.id_field().map(|col| col.name().to_string())
23}
24
25impl ExprDataSource<Value> for RestApi {
26    async fn execute(&self, expr: &Expression<Value>) -> vantage_core::Result<Value> {
27        if expr.parameters.is_empty() {
28            Ok(Value::String(expr.template.clone()))
29        } else {
30            Ok(Value::Null)
31        }
32    }
33
34    fn defer(&self, expr: Expression<Value>) -> DeferredFn<Value> {
35        let api = self.clone();
36        DeferredFn::new(move || {
37            let api = api.clone();
38            let expr = expr.clone();
39            Box::pin(async move {
40                let result = api.execute(&expr).await?;
41                Ok(ExpressiveEnum::Scalar(result))
42            })
43        })
44    }
45}
46
47impl DataSource for RestApi {}
48
49#[async_trait]
50impl TableSource for RestApi {
51    type Column<Type>
52        = Column<Type>
53    where
54        Type: ColumnType;
55    type AnyType = Value;
56    type Value = Value;
57    type Id = String;
58    type Condition = vantage_expressions::Expression<Self::Value>;
59
60    fn create_column<Type: ColumnType>(&self, name: &str) -> Self::Column<Type> {
61        Column::new(name)
62    }
63
64    fn to_any_column<Type: ColumnType>(
65        &self,
66        column: Self::Column<Type>,
67    ) -> Self::Column<Self::AnyType> {
68        Column::from_column(column)
69    }
70
71    fn convert_any_column<Type: ColumnType>(
72        &self,
73        any_column: Self::Column<Self::AnyType>,
74    ) -> Option<Self::Column<Type>> {
75        Some(Column::from_column(any_column))
76    }
77
78    fn expr(
79        &self,
80        template: impl Into<String>,
81        parameters: Vec<ExpressiveEnum<Self::Value>>,
82    ) -> Expression<Self::Value> {
83        Expression::new(template, parameters)
84    }
85
86    fn search_table_condition<E>(
87        &self,
88        _table: &Table<Self, E>,
89        search_value: &str,
90    ) -> Expression<Self::Value>
91    where
92        E: Entity<Self::Value>,
93    {
94        Expression::new(format!("SEARCH '{}'", search_value), vec![])
95    }
96
97    async fn list_table_values<E>(
98        &self,
99        table: &Table<Self, E>,
100    ) -> Result<IndexMap<Self::Id, Record<Self::Value>>>
101    where
102        E: Entity<Self::Value>,
103        Self: Sized,
104    {
105        self.fetch_records(table.table_name(), id_field_name(table).as_deref())
106            .await
107    }
108
109    async fn get_table_value<E>(
110        &self,
111        table: &Table<Self, E>,
112        id: &Self::Id,
113    ) -> Result<Option<Record<Self::Value>>>
114    where
115        E: Entity<Self::Value>,
116        Self: Sized,
117    {
118        let records = self
119            .fetch_records(table.table_name(), id_field_name(table).as_deref())
120            .await?;
121        Ok(records.get(id).cloned())
122    }
123
124    async fn get_table_some_value<E>(
125        &self,
126        table: &Table<Self, E>,
127    ) -> Result<Option<(Self::Id, Record<Self::Value>)>>
128    where
129        E: Entity<Self::Value>,
130        Self: Sized,
131    {
132        let records = self
133            .fetch_records(table.table_name(), id_field_name(table).as_deref())
134            .await?;
135        Ok(records.into_iter().next())
136    }
137
138    async fn get_table_count<E>(&self, table: &Table<Self, E>) -> Result<i64>
139    where
140        E: Entity<Self::Value>,
141        Self: Sized,
142    {
143        let records = self
144            .fetch_records(table.table_name(), id_field_name(table).as_deref())
145            .await?;
146        Ok(records.len() as i64)
147    }
148
149    async fn get_table_sum<E>(
150        &self,
151        _table: &Table<Self, E>,
152        _column: &Self::Column<Self::AnyType>,
153    ) -> Result<Self::Value>
154    where
155        E: Entity<Self::Value>,
156        Self: Sized,
157    {
158        Err(error!("Sum not implemented for API backend"))
159    }
160
161    async fn get_table_max<E>(
162        &self,
163        _table: &Table<Self, E>,
164        _column: &Self::Column<Self::AnyType>,
165    ) -> Result<Self::Value>
166    where
167        E: Entity<Self::Value>,
168        Self: Sized,
169    {
170        Err(error!("Max not implemented for API backend"))
171    }
172
173    async fn get_table_min<E>(
174        &self,
175        _table: &Table<Self, E>,
176        _column: &Self::Column<Self::AnyType>,
177    ) -> Result<Self::Value>
178    where
179        E: Entity<Self::Value>,
180        Self: Sized,
181    {
182        Err(error!("Min not implemented for API backend"))
183    }
184
185    async fn insert_table_value<E>(
186        &self,
187        _table: &Table<Self, E>,
188        _id: &Self::Id,
189        _record: &Record<Self::Value>,
190    ) -> Result<Record<Self::Value>>
191    where
192        E: Entity<Self::Value>,
193        Self: Sized,
194    {
195        Err(error!("REST API is a read-only data source"))
196    }
197
198    async fn replace_table_value<E>(
199        &self,
200        _table: &Table<Self, E>,
201        _id: &Self::Id,
202        _record: &Record<Self::Value>,
203    ) -> Result<Record<Self::Value>>
204    where
205        E: Entity<Self::Value>,
206        Self: Sized,
207    {
208        Err(error!("REST API is a read-only data source"))
209    }
210
211    async fn patch_table_value<E>(
212        &self,
213        _table: &Table<Self, E>,
214        _id: &Self::Id,
215        _partial: &Record<Self::Value>,
216    ) -> Result<Record<Self::Value>>
217    where
218        E: Entity<Self::Value>,
219        Self: Sized,
220    {
221        Err(error!("REST API is a read-only data source"))
222    }
223
224    async fn delete_table_value<E>(&self, _table: &Table<Self, E>, _id: &Self::Id) -> Result<()>
225    where
226        E: Entity<Self::Value>,
227        Self: Sized,
228    {
229        Err(error!("REST API is a read-only data source"))
230    }
231
232    async fn delete_table_all_values<E>(&self, _table: &Table<Self, E>) -> Result<()>
233    where
234        E: Entity<Self::Value>,
235        Self: Sized,
236    {
237        Err(error!("REST API is a read-only data source"))
238    }
239
240    async fn insert_table_return_id_value<E>(
241        &self,
242        _table: &Table<Self, E>,
243        _record: &Record<Self::Value>,
244    ) -> Result<Self::Id>
245    where
246        E: Entity<Self::Value>,
247        Self: Sized,
248    {
249        Err(error!("REST API is a read-only data source"))
250    }
251
252    fn related_in_condition<SourceE: Entity<Self::Value> + 'static>(
253        &self,
254        _target_field: &str,
255        _source_table: &Table<Self, SourceE>,
256        _source_column: &str,
257    ) -> Self::Condition
258    where
259        Self: Sized,
260    {
261        unimplemented!("related_in_condition not yet supported for REST API")
262    }
263
264    fn column_table_values_expr<'a, E, Type: ColumnType>(
265        &'a self,
266        _table: &Table<Self, E>,
267        _column: &Self::Column<Type>,
268    ) -> AssociatedExpression<'a, Self, Self::Value, Vec<Type>>
269    where
270        E: Entity<Self::Value> + 'static,
271        Self: Sized,
272    {
273        unimplemented!("column_table_values_expr not yet supported for REST API")
274    }
275}