Skip to main content

vantage_csv/
expr_data_source.rs

1//! ExprDataSource implementation for CSV
2//!
3//! Enables executing expressions against CSV by resolving DeferredFn closures.
4//! This is used by `column_values_expression` and other deferred-based expressions.
5
6use vantage_expressions::traits::datasource::ExprDataSource;
7use vantage_expressions::traits::expressive::DeferredFn;
8use vantage_expressions::{Expression, defer_execute, execute_by_resolving};
9
10use crate::Csv;
11use crate::condition::resolve_param;
12use crate::type_system::AnyCsvType;
13
14impl ExprDataSource<AnyCsvType> for Csv {
15    async fn execute(&self, expr: &Expression<AnyCsvType>) -> vantage_core::Result<AnyCsvType> {
16        // For CSV, execution means resolving deferred params. Multi-param
17        // expressions resolve each param and join the values comma-separated.
18        execute_by_resolving(
19            expr,
20            |template| AnyCsvType::new(template.to_string()),
21            |params| async move {
22                let mut results = Vec::with_capacity(params.len());
23                for param in params {
24                    results.push(resolve_param(param).await?.value().clone());
25                }
26                Ok(AnyCsvType::new(results.join(",")))
27            },
28        )
29        .await
30    }
31
32    fn defer(&self, expr: Expression<AnyCsvType>) -> DeferredFn<AnyCsvType>
33    where
34        AnyCsvType: Clone + Send + Sync + 'static,
35    {
36        defer_execute(self, expr)
37    }
38}
39
40#[cfg(test)]
41mod tests {
42    use super::*;
43    use vantage_table::table::Table;
44    use vantage_table::traits::table_source::TableSource;
45    use vantage_types::EmptyEntity;
46
47    fn test_csv() -> Csv {
48        Csv::new(format!("{}/data", env!("CARGO_MANIFEST_DIR")))
49    }
50
51    #[tokio::test]
52    async fn test_execute_column_table_values_expr() {
53        let csv = test_csv();
54        let table = Table::<Csv, EmptyEntity>::new("client", csv.clone())
55            .with_column_of::<String>("name")
56            .with_column_of::<bool>("is_paying_client");
57
58        let name_col = csv.create_column::<String>("name");
59        let associated = csv.column_table_values_expr(&table, &name_col);
60        let result = csv.execute(associated.expression()).await.unwrap();
61
62        // Result is a List of AnyCsvType values
63        let names = result.try_get::<Vec<AnyCsvType>>().unwrap();
64        let name_strings: Vec<&str> = names.iter().map(|v| v.value().as_str()).collect();
65        assert!(name_strings.contains(&"Marty McFly"));
66        assert!(name_strings.contains(&"Doc Brown"));
67        assert!(name_strings.contains(&"Biff Tannen"));
68    }
69
70    #[tokio::test]
71    async fn test_execute_column_values_with_condition() {
72        use crate::operation::CsvOperation;
73
74        let csv = test_csv();
75        let mut table = Table::<Csv, EmptyEntity>::new("client", csv.clone())
76            .with_column_of::<String>("name")
77            .with_column_of::<bool>("is_paying_client");
78
79        table.add_condition(table["is_paying_client"].eq(AnyCsvType::new(true)));
80
81        let name_col = csv.create_column::<String>("name");
82        let associated = csv.column_table_values_expr(&table, &name_col);
83        let result = csv.execute(associated.expression()).await.unwrap();
84
85        let names = result.try_get::<Vec<AnyCsvType>>().unwrap();
86        let name_strings: Vec<&str> = names.iter().map(|v| v.value().as_str()).collect();
87        assert!(name_strings.contains(&"Marty McFly"));
88        assert!(name_strings.contains(&"Doc Brown"));
89        assert!(!name_strings.contains(&"Biff Tannen"));
90    }
91}