use query_flow::{query, Cachable, Db, Query, QueryError, QueryRuntime};
#[query]
fn constant(db: &impl Db, value: i32) -> Result<i32, QueryError> {
let _ = db;
Ok(value)
}
#[query]
fn double(db: &impl Db, value: i32) -> Result<i32, QueryError> {
let _ = db;
Ok(value * 2)
}
#[query]
fn square(db: &impl Db, value: i32) -> Result<i32, QueryError> {
let _ = db;
Ok(value * value)
}
#[query]
fn add_queries<T, U>(db: &impl Db, query1: T, query2: U) -> Result<i32, QueryError>
where
T: Query<Output = i32> + Cachable,
U: Query<Output = i32> + Cachable,
{
let result1 = db.query(query1)?;
let result2 = db.query(query2)?;
Ok(*result1 + *result2)
}
#[test]
fn test_add_two_constant_queries() {
let runtime = QueryRuntime::new();
let result = runtime.query(AddQueries::new(Constant::new(10), Constant::new(20)));
assert_eq!(*result.unwrap(), 30);
}
#[test]
fn test_add_different_query_types() {
let runtime = QueryRuntime::new();
let result = runtime.query(AddQueries::new(
Constant::new(5), Double::new(10), ));
assert_eq!(*result.unwrap(), 25);
}
#[test]
fn test_add_queries_caching() {
let runtime = QueryRuntime::new();
let result1 = runtime.query(AddQueries::new(Constant::new(1), Constant::new(2)));
let result2 = runtime.query(AddQueries::new(Constant::new(1), Constant::new(2)));
assert!(std::sync::Arc::ptr_eq(&result1.unwrap(), &result2.unwrap()));
}
#[query]
fn map_query<T, F>(db: &impl Db, inner: T, func: F) -> Result<i32, QueryError>
where
T: Query<Output = i32> + Cachable,
F: Fn(i32) -> i32 + Cachable,
{
let result = db.query(inner)?;
Ok(func(*result))
}
#[test]
fn test_map_query_with_closure() {
let runtime = QueryRuntime::new();
fn add_one(x: i32) -> i32 {
x + 1
}
let result = runtime.query(MapQuery::new(Constant::new(10), add_one as fn(i32) -> i32));
assert_eq!(*result.unwrap(), 11);
}
#[query]
fn chain<T, U>(db: &impl Db, first: T, second_factory: U) -> Result<i32, QueryError>
where
T: Query<Output = i32> + Cachable,
U: Fn(i32) -> Double + Cachable,
{
let first_result = db.query(first)?;
let second_query = second_factory(*first_result);
let second_result = db.query(second_query)?;
Ok(*second_result)
}
#[test]
fn test_chain_queries() {
let runtime = QueryRuntime::new();
fn make_double(x: i32) -> Double {
Double::new(x)
}
let result = runtime.query(Chain::new(
Constant::new(5),
make_double as fn(i32) -> Double,
));
assert_eq!(*result.unwrap(), 10);
}
#[query]
fn sum_queries<T>(db: &impl Db, queries: Vec<T>) -> Result<i32, QueryError>
where
T: Query<Output = i32> + Cachable,
{
let mut sum = 0;
for q in queries {
sum += *db.query(q)?;
}
Ok(sum)
}
#[test]
fn test_sum_multiple_queries() {
let runtime = QueryRuntime::new();
let result = runtime.query(SumQueries::new(vec![
Constant::new(1),
Constant::new(2),
Constant::new(3),
Constant::new(4),
]));
assert_eq!(*result.unwrap(), 10);
}
#[test]
fn test_sum_empty_queries() {
let runtime = QueryRuntime::new();
let result = runtime.query(SumQueries::<Constant>::new(vec![]));
assert_eq!(*result.unwrap(), 0);
}
#[query]
fn select_query<T, U>(
db: &impl Db,
condition: bool,
if_true: T,
if_false: U,
) -> Result<i32, QueryError>
where
T: Query<Output = i32> + Cachable,
U: Query<Output = i32> + Cachable,
{
if condition {
Ok(*db.query(if_true)?)
} else {
Ok(*db.query(if_false)?)
}
}
#[test]
fn test_select_true_branch() {
let runtime = QueryRuntime::new();
let result = runtime.query(SelectQuery::new(
true,
Constant::new(100),
Constant::new(200),
));
assert_eq!(*result.unwrap(), 100);
}
#[test]
fn test_select_false_branch() {
let runtime = QueryRuntime::new();
let result = runtime.query(SelectQuery::new(
false,
Constant::new(100),
Constant::new(200),
));
assert_eq!(*result.unwrap(), 200);
}
#[test]
fn test_deeply_nested_composition() {
let runtime = QueryRuntime::new();
let inner1 = AddQueries::new(Constant::new(5), Constant::new(10));
let inner2 = Square::new(3);
let result = runtime.query(AddQueries::new(inner1, inner2));
assert_eq!(*result.unwrap(), 24);
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
struct QueryResult<T> {
value: T,
computed: bool,
}
#[query]
fn wrap_result<T>(db: &impl Db, inner: T) -> Result<QueryResult<i32>, QueryError>
where
T: Query<Output = i32> + Cachable,
{
let value = *db.query(inner)?;
Ok(QueryResult {
value,
computed: true,
})
}
#[test]
fn test_wrap_query_result() {
let runtime = QueryRuntime::new();
let result = runtime.query(WrapResult::new(Constant::new(42)));
let wrapped = result.unwrap();
assert_eq!(wrapped.value, 42);
assert!(wrapped.computed);
}
#[query]
fn failing_query(db: &impl Db, should_fail: bool) -> Result<i32, QueryError> {
let _ = db;
if should_fail {
Err(anyhow::anyhow!("intentional failure").into())
} else {
Ok(42)
}
}
#[test]
fn test_composed_query_propagates_error() {
let runtime = QueryRuntime::new();
let result = runtime.query(AddQueries::new(Constant::new(10), FailingQuery::new(true)));
assert!(result.is_err());
}
#[test]
fn test_composed_query_succeeds_when_all_succeed() {
let runtime = QueryRuntime::new();
let result = runtime.query(AddQueries::new(Constant::new(10), FailingQuery::new(false)));
assert_eq!(*result.unwrap(), 52); }