use crate::expression::core::Expression;
use crate::traits::expressive::{DeferredFn, DeferredFuture, ExpressiveEnum};
pub trait ExpressionMapper<From, To> {
fn map_expression(expr: Expression<From>) -> Expression<To>
where
From: Into<To> + Send + Clone + 'static,
To: Send + 'static;
}
impl<From, To> ExpressionMapper<From, To> for Expression<From> {
fn map_expression(expr: Expression<From>) -> Expression<To>
where
From: Into<To> + Send + Clone + 'static,
To: Send + 'static,
{
Expression::new(
expr.template,
expr.parameters
.into_iter()
.map(|param| map_expressive_enum(param))
.collect(),
)
}
}
fn map_expressive_enum<From, To>(enum_value: ExpressiveEnum<From>) -> ExpressiveEnum<To>
where
From: Into<To> + Send + Clone + 'static,
To: Send + 'static,
{
match enum_value {
ExpressiveEnum::Scalar(value) => ExpressiveEnum::Scalar(value.into()),
ExpressiveEnum::Nested(expr) => ExpressiveEnum::Nested(Expression::map_expression(expr)),
ExpressiveEnum::Deferred(deferred) => ExpressiveEnum::Deferred(map_deferred_fn(deferred)),
}
}
fn map_deferred_fn<From, To>(deferred: DeferredFn<From>) -> DeferredFn<To>
where
From: Into<To> + Send + Clone + 'static,
To: Send + 'static,
{
DeferredFn::new(move || {
let deferred = deferred.clone();
Box::pin(async move {
let result = deferred.call().await?;
Ok(map_expressive_enum(result))
}) as DeferredFuture<To>
})
}
pub trait ExpressionMap<From> {
fn map<To>(self) -> Expression<To>
where
From: Into<To> + Send + Clone + 'static,
To: Send + 'static;
}
impl<From> ExpressionMap<From> for Expression<From> {
fn map<To>(self) -> Expression<To>
where
From: Into<To> + Send + Clone + 'static,
To: Send + 'static,
{
Expression::map_expression(self)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::traits::datasource::{DataSource, ExprDataSource};
use crate::traits::expressive::{DeferredFn, ExpressiveEnum};
use serde_json::Value;
use vantage_core::Result;
#[derive(Clone)]
struct StringDatabase {
result: String,
}
impl StringDatabase {
fn new(result: String) -> Self {
Self { result }
}
}
impl DataSource for StringDatabase {}
impl ExprDataSource<String> for StringDatabase {
async fn execute(&self, _expr: &Expression<String>) -> Result<String> {
Ok(self.result.clone())
}
fn defer(&self, _expr: Expression<String>) -> DeferredFn<String>
where
String: Clone + Send + Sync + 'static,
{
let result = self.result.clone();
DeferredFn::new(move || {
let result = result.clone();
Box::pin(async move { Ok(ExpressiveEnum::Scalar(result)) })
})
}
}
#[derive(Clone)]
struct JsonDatabase {
result: Value,
}
impl JsonDatabase {
fn new(result: Value) -> Self {
Self { result }
}
}
impl DataSource for JsonDatabase {}
impl ExprDataSource<Value> for JsonDatabase {
async fn execute(&self, _expr: &Expression<Value>) -> Result<Value> {
Ok(self.result.clone())
}
fn defer(&self, _expr: Expression<Value>) -> DeferredFn<Value>
where
Value: Clone + Send + Sync + 'static,
{
let result = self.result.clone();
DeferredFn::new(move || {
let result = result.clone();
Box::pin(async move { Ok(ExpressiveEnum::Scalar(result)) })
})
}
}
#[test]
fn test_scalar_mapping() {
let string_expr: Expression<String> =
Expression::new("age > {}", vec![ExpressiveEnum::Scalar("25".to_string())]);
let value_expr: Expression<Value> = string_expr.map();
assert_eq!(value_expr.template, "age > {}");
assert_eq!(value_expr.parameters.len(), 1);
}
#[test]
fn test_nested_mapping() {
let inner_expr: Expression<String> = Expression::new(
"status = {}",
vec![ExpressiveEnum::Scalar("active".to_string())],
);
let outer_expr: Expression<String> = Expression::new(
"SELECT * FROM users WHERE {}",
vec![ExpressiveEnum::Nested(inner_expr)],
);
let mapped_expr: Expression<Value> = outer_expr.map();
assert_eq!(mapped_expr.template, "SELECT * FROM users WHERE {}");
assert_eq!(mapped_expr.parameters.len(), 1);
}
#[tokio::test]
async fn test_deferred_mapping() {
let deferred_string =
DeferredFn::new(|| Box::pin(async { Ok(ExpressiveEnum::Scalar("test".to_string())) }));
let string_expr: Expression<String> = Expression::new(
"SELECT * WHERE name = {}",
vec![ExpressiveEnum::Deferred(deferred_string)],
);
let value_expr: Expression<Value> = string_expr.map();
assert_eq!(value_expr.template, "SELECT * WHERE name = {}");
assert_eq!(value_expr.parameters.len(), 1);
if let ExpressiveEnum::Deferred(ref deferred) = value_expr.parameters[0] {
let result = deferred.call().await.unwrap();
match result {
ExpressiveEnum::Scalar(Value::String(s)) => assert_eq!(s, "test"),
_ => panic!("Expected string value"),
}
} else {
panic!("Expected deferred parameter");
}
}
#[tokio::test]
async fn test_cross_database_defer_map() {
let db1 = StringDatabase::new("user123,user456".to_string());
let db2 = JsonDatabase::new(Value::String("processed".to_string()));
let string_query = Expression::new(
"SELECT user_ids FROM active_users WHERE department = {}",
vec![ExpressiveEnum::Scalar("engineering".to_string())],
);
let deferred_query = db1.defer(string_query);
let mapped_deferred = DeferredFn::new(move || {
let deferred_query = deferred_query.clone();
Box::pin(async move {
let result = deferred_query.call().await?;
Ok(map_expressive_enum(result))
})
});
let json_expr = Expression::new(
"PROCESS_USERS({})",
vec![ExpressiveEnum::Deferred(mapped_deferred)],
);
let result = db2.execute(&json_expr).await;
assert_eq!(result.unwrap(), Value::String("processed".to_string()));
}
}