use indexmap::IndexMap;
use vantage_core::Result;
use vantage_expressions::Expression;
use vantage_expressions::traits::expressive::ExpressiveEnum;
use vantage_types::Record;
use crate::operation::{OP_EQ, OP_IN};
use crate::type_system::AnyCsvType;
pub(crate) async fn apply_condition(
records: IndexMap<String, Record<AnyCsvType>>,
condition: &Expression<AnyCsvType>,
) -> Result<IndexMap<String, Record<AnyCsvType>>> {
let params = &condition.parameters;
if params.len() < 2 {
return Ok(records); }
let field_name = match ¶ms[0] {
ExpressiveEnum::Nested(expr) => expr.template.clone(),
_ => return Ok(records),
};
match condition.template.as_str() {
OP_EQ => {
let expected = resolve_param(¶ms[1]).await?;
Ok(records
.into_iter()
.filter(|(_id, record)| {
record
.get(&field_name)
.map(|v| v.value() == expected.value())
.unwrap_or(false)
})
.collect())
}
OP_IN => {
let resolved = resolve_param(¶ms[1]).await?;
let match_values: Vec<AnyCsvType> =
resolved.try_get::<Vec<AnyCsvType>>().unwrap_or_else(|| {
vec![resolved.clone()]
});
Ok(records
.into_iter()
.filter(|(_id, record)| {
record
.get(&field_name)
.map(|v| match_values.iter().any(|m| m.value() == v.value()))
.unwrap_or(false)
})
.collect())
}
other => Err(vantage_core::error!(
"Unsupported CSV condition operator",
template = other.to_string()
)),
}
}
pub(crate) fn resolve_param(
param: &ExpressiveEnum<AnyCsvType>,
) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<AnyCsvType>> + Send + '_>> {
Box::pin(async move {
match param {
ExpressiveEnum::Scalar(v) => Ok(v.clone()),
ExpressiveEnum::Deferred(deferred) => {
let result = deferred.call().await?;
match result {
ExpressiveEnum::Scalar(v) => Ok(v),
other => resolve_param(&other).await,
}
}
ExpressiveEnum::Nested(expr) => {
if expr.parameters.is_empty() {
Ok(AnyCsvType::new(expr.template.clone()))
} else if expr.parameters.len() == 1 {
resolve_param(&expr.parameters[0]).await
} else {
Ok(AnyCsvType::new(expr.template.clone()))
}
}
}
})
}
#[cfg(test)]
mod tests {
use crate::operation::CsvOperation;
use crate::{AnyCsvType, Csv};
use vantage_dataset::prelude::ReadableValueSet;
use vantage_table::table::Table;
use vantage_types::EmptyEntity;
fn test_csv() -> Csv {
Csv::new(format!("{}/data", env!("CARGO_MANIFEST_DIR")))
}
#[tokio::test]
async fn test_eq_condition_bool() {
let csv = test_csv();
let mut table = Table::<Csv, EmptyEntity>::new("client", csv)
.with_column_of::<String>("name")
.with_column_of::<bool>("is_paying_client");
table.add_condition(table["is_paying_client"].eq(AnyCsvType::new(true)));
let values = table.list_values().await.unwrap();
assert_eq!(values.len(), 2);
assert!(values.contains_key("marty"));
assert!(values.contains_key("doc"));
assert!(!values.contains_key("biff"));
}
#[tokio::test]
async fn test_eq_condition_string() {
let csv = test_csv();
let mut table = Table::<Csv, EmptyEntity>::new("client", csv)
.with_column_of::<String>("name")
.with_column_of::<String>("email");
table.add_condition(table["name"].eq(AnyCsvType::new("Doc Brown".to_string())));
let values = table.list_values().await.unwrap();
assert_eq!(values.len(), 1);
assert!(values.contains_key("doc"));
}
#[tokio::test]
async fn test_eq_condition_int() {
let csv = test_csv();
let mut table = Table::<Csv, EmptyEntity>::new("product", csv)
.with_column_of::<String>("name")
.with_column_of::<i64>("calories");
table.add_condition(table["calories"].eq(AnyCsvType::new(300_i64)));
let values = table.list_values().await.unwrap();
assert_eq!(values.len(), 1);
assert!(values.contains_key("flux_cupcake"));
}
#[tokio::test]
async fn test_multiple_conditions() {
let csv = test_csv();
let mut table = Table::<Csv, EmptyEntity>::new("product", csv)
.with_column_of::<String>("name")
.with_column_of::<i64>("calories")
.with_column_of::<bool>("is_deleted");
table.add_condition(table["is_deleted"].eq(AnyCsvType::new(false)));
table.add_condition(table["calories"].eq(AnyCsvType::new(300_i64)));
let values = table.list_values().await.unwrap();
assert_eq!(values.len(), 1);
assert!(values.contains_key("flux_cupcake"));
}
#[tokio::test]
async fn test_in_condition_with_column_values() {
use vantage_expressions::Expressive;
use vantage_table::traits::table_source::TableSource;
let csv = test_csv();
let mut clients = Table::<Csv, EmptyEntity>::new("client", csv.clone())
.with_column_of::<String>("name")
.with_column_of::<bool>("is_paying_client");
clients.add_condition(clients["is_paying_client"].eq(AnyCsvType::new(true)));
let name_col = csv.create_column::<String>("name");
let paying_names = csv.column_table_values_expr(&clients, &name_col);
let mut all_clients = Table::<Csv, EmptyEntity>::new("client", csv.clone())
.with_column_of::<String>("name")
.with_column_of::<bool>("is_paying_client");
all_clients.add_condition(all_clients["name"].in_(paying_names.expr()));
let values = all_clients.list_values().await.unwrap();
assert_eq!(values.len(), 2);
assert!(values.contains_key("marty"));
assert!(values.contains_key("doc"));
}
}