vantage_table/table/impls/
selectable.rs1use vantage_core::Result;
2use vantage_expressions::traits::selectable::Selectable;
3use vantage_expressions::{Expression, Expressive, SelectableDataSource, expr_any};
4use vantage_types::Entity;
5
6use crate::{
7 column::core::ColumnType,
8 source::{SelectSeed, SelectSource},
9 table::Table,
10 traits::column_like::ColumnLike,
11 traits::table_source::TableSource,
12};
13
14impl<T, E> Table<T, E>
15where
16 T: SelectableDataSource<T::Value, T::Condition> + TableSource,
17 T::Source: SelectSeed<T::Select, T::Value, T::Condition>,
18 T::Value: From<String>, E: Entity<T::Value>,
20{
21 pub fn select_empty(&self) -> T::Select {
25 let mut select = self.data_source.select();
26 self.source.seed(&mut select);
27
28 for condition in self.conditions.values() {
29 select.add_where_condition(condition.clone());
30 }
31
32 for (expr, direction) in self.order_by.values() {
33 let order = match direction {
34 crate::sorting::SortDirection::Ascending => vantage_expressions::Order::Asc,
35 crate::sorting::SortDirection::Descending => vantage_expressions::Order::Desc,
36 };
37 select.add_order_by(expr.clone(), order);
38 }
39
40 if let Some(pagination) = &self.pagination {
41 select.set_limit(Some(pagination.limit()), Some(pagination.skip()));
42 }
43
44 select
45 }
46
47 pub fn select(&self) -> T::Select {
49 let mut select = self.select_empty();
50
51 for column in self.columns.values() {
53 if self.lazy_expressions.contains_key(column.name()) {
58 continue;
59 }
60 if let Some(expr_fn) = self.expressions.get(column.name()) {
61 let expr = expr_fn(self.as_entity_erased());
62 self.data_source.add_select_column(
63 &mut select,
64 expr_any!("({})", (expr)),
65 Some(column.name()),
66 );
67 } else if let Some(alias) = column.alias() {
68 let expr = self.data_source.expr(column.name(), vec![]);
69 self.data_source
70 .add_select_column(&mut select, expr, Some(alias));
71 } else {
72 select.add_field(column.name());
73 }
74 }
75
76 for (name, expr_fn) in &self.expressions {
78 if !self.columns.contains_key(name) {
79 let expr = expr_fn(self.as_entity_erased());
80 self.data_source.add_select_column(
81 &mut select,
82 expr_any!("({})", (expr)),
83 Some(name),
84 );
85 }
86 }
87
88 select
89 }
90 pub async fn get_count(&self) -> Result<i64> {
92 self.data_source.get_table_count(self).await
93 }
94
95 pub async fn get_sum(&self, column: &T::Column<T::AnyType>) -> Result<T::Value> {
97 self.data_source.get_table_sum(self, column).await
98 }
99
100 pub async fn get_max(&self, column: &T::Column<T::AnyType>) -> Result<T::Value> {
102 self.data_source.get_table_max(self, column).await
103 }
104
105 pub async fn get_min(&self, column: &T::Column<T::AnyType>) -> Result<T::Value> {
107 self.data_source.get_table_min(self, column).await
108 }
109
110 pub fn get_count_query(&self) -> Expression<T::Value> {
113 expr_any!("({})", (self.select_empty().as_count()))
114 }
115
116 pub fn get_sum_query<Type>(&self, column: &T::Column<Type>) -> Expression<T::Value>
119 where
120 Type: ColumnType,
121 T::Column<Type>: Expressive<T::Value>,
122 {
123 expr_any!("({})", (self.select_empty().as_sum(column.expr())))
124 }
125
126 pub fn select_column(&self, field: &str) -> Option<Expression<T::Value>>
141 where
142 T::Column<T::AnyType>: Expressive<T::Value>,
143 T::Select: Expressive<T::Value>,
144 {
145 let expr = self.get_column_expr(field)?;
146 let mut select = self.select_empty();
147 select.clear_fields();
148 select.clear_order_by();
149 select.add_expression(expr);
150 Some(select.expr())
151 }
152}
153
154impl<T, E, V, C, S> Table<T, E>
159where
160 T: SelectableDataSource<V, C, Select = S>
161 + TableSource<Value = V, Condition = C, Source = SelectSource<S>>,
162 V: Clone + Send + Sync + 'static + From<String>,
163 C: Clone + Send + Sync + 'static,
164 S: Expressive<V> + Clone,
165 E: Entity<V>,
166{
167 pub fn from_select(data_source: T, alias: impl Into<String>, select: S) -> Self {
170 let alias = alias.into();
171 let mut table = Table::new(alias.clone(), data_source);
172 table.source = SelectSource::query(select, alias);
173 table
174 }
175
176 pub fn derive_from<E2: Entity<V> + 'static>(
185 source: &Table<T, E2>,
186 alias: impl Into<String>,
187 modifier: impl FnOnce(S) -> S,
188 columns: &[&str],
189 relations: &[&str],
190 ) -> Self
191 where
192 T: 'static,
193 E: 'static,
194 {
195 let alias = alias.into();
196 let select = modifier(source.select());
197 let mut table = Table::new(alias.clone(), source.data_source().clone());
198 table.source = SelectSource::query(select, alias);
199 table.copy_columns_from(source, Some(columns));
200 table.copy_relations_from(source, Some(relations));
201 table.id_field = source.id_field.clone();
202 table.title_field = source.title_field.clone();
203 table.title_fields = source.title_fields.clone();
204 table
205 }
206}
207
208impl<T, E> Table<T, E>
210where
211 T: SelectableDataSource<serde_json::Value, T::Condition>
212 + TableSource<Value = serde_json::Value>
213 + vantage_expressions::traits::datasource::ExprDataSource<serde_json::Value>,
214 T::Source: SelectSeed<T::Select, serde_json::Value, T::Condition>,
215 T::Value: From<String>,
216 E: Entity<serde_json::Value>,
217{
218 pub async fn get_count_via_query(&self) -> Result<i64> {
220 let count_query = self.get_count_query();
221 let result = self.data_source.execute(&count_query).await?;
222
223 let result = match result.as_array().map(Vec::as_slice) {
226 Some([single]) => single,
227 _ => &result,
228 };
229
230 if let Some(count) = result.get("count").and_then(|v| v.as_i64()) {
234 Ok(count)
235 } else if let Some(count) = result.as_i64() {
236 Ok(count)
237 } else {
238 Err(vantage_core::util::error::vantage_error!(
239 "count query returned an unexpected result shape: {result}"
240 ))
241 }
242 }
243}
244
245#[cfg(test)]
246mod tests {
247 use super::*;
248 use crate::mocks::mock_table_source::MockTableSource;
249 use serde_json::json;
250 use vantage_expressions::mocks::datasource::MockSelectableDataSource;
251 use vantage_expressions::traits::datasource::ExprDataSource;
252
253 #[tokio::test]
254 async fn test_selectable_functionality() {
255 let mock_select_source = MockSelectableDataSource::new(json!([
256 {"id": "1", "name": "Alice", "age": 30},
257 {"id": "2", "name": "Bob", "age": 25}
258 ]));
259
260 let mock_query_source = vantage_expressions::mocks::mock_builder::new()
261 .on_exact_select("(SELECT COUNT(*) FROM \"users\")", json!(42));
262
263 let table = MockTableSource::new()
264 .with_data(
265 "users",
266 vec![
267 json!({"id": "1", "name": "Alice", "age": 30}),
268 json!({"id": "2", "name": "Bob", "age": 25}),
269 ],
270 )
271 .await
272 .with_select_source(mock_select_source)
273 .with_query_source(mock_query_source);
274 let table = Table::<_, vantage_types::EmptyEntity>::new("users", table);
275
276 let select = table.select();
278 assert_eq!(select.source(), Some("users"));
279
280 let query_expr: vantage_expressions::Expression<serde_json::Value> = select.into();
282 assert_eq!(query_expr.preview(), "SELECT * FROM users");
283
284 let count_query = table.get_count_query();
286 assert_eq!(count_query.preview(), "(SELECT COUNT(*) FROM \"users\")");
287
288 let count = table.get_count_via_query().await.unwrap();
296 assert_eq!(count, 42);
297 }
298
299 async fn count_table_returning(
300 count_result: serde_json::Value,
301 ) -> Table<MockTableSource, vantage_types::EmptyEntity> {
302 let mock_select_source = MockSelectableDataSource::new(json!([]));
303 let mock_query_source = vantage_expressions::mocks::mock_builder::new()
304 .on_exact_select("(SELECT COUNT(*) FROM \"users\")", count_result);
305 let source = MockTableSource::new()
306 .with_select_source(mock_select_source)
307 .with_query_source(mock_query_source);
308 Table::<_, vantage_types::EmptyEntity>::new("users", source)
309 }
310
311 #[tokio::test]
312 async fn test_count_unwraps_single_element_array() {
313 let table = count_table_returning(json!([{"count": 7}])).await;
315 assert_eq!(table.get_count_via_query().await.unwrap(), 7);
316 }
317
318 #[tokio::test]
319 async fn test_count_errors_on_unexpected_shape() {
320 let table = count_table_returning(json!({"total": 5})).await;
322 assert!(table.get_count_via_query().await.is_err());
323 }
324
325 #[tokio::test]
326 #[should_panic(expected = "MockTableSource select source not set")]
327 async fn test_panics_without_select_source() {
328 let table = Table::<_, vantage_types::EmptyEntity>::new("users", MockTableSource::new());
329 let _select = table.select();
330 }
331
332 #[tokio::test]
333 #[should_panic(expected = "MockTableSource query source not set")]
334 async fn test_panics_without_query_source() {
335 let table = Table::<_, vantage_types::EmptyEntity>::new("users", MockTableSource::new());
336 let query = table.data_source().expr("SELECT COUNT(*)", vec![]);
337 let _result = table.data_source().execute(&query).await;
338 }
339}