use std::future::Future;
use std::pin::Pin;
use crate::context::Context;
type BoxedRenderOperation =
Box<dyn Fn() -> Pin<Box<dyn Future<Output = Box<dyn Context>> + Send>> + Send + Sync>;
type BoxedStateOperation =
Box<dyn Fn() -> Pin<Box<dyn Future<Output = ()> + Send>> + Send + Sync>;
pub enum OperationKind {
Render(String,BoxedRenderOperation), State(BoxedStateOperation),
}
pub trait FunctionSignature {
type Params;
type Output;
}
pub trait Operation<F: FunctionSignature> {
type Future: Future<Output = F::Output>;
fn invoke(&self, params: F::Params) -> Self::Future;
}
macro_rules! impl_function_traits {
() => {
impl<Fut, Out> FunctionSignature for fn() -> Fut
where
Fut: Future<Output = Out>
{
type Params = ();
type Output = Out;
}
impl<F, Fut> Operation<fn() -> Fut> for F
where
F: Fn() -> Fut,
Fut: Future,
{
type Future = Fut;
fn invoke(&self, _: ()) -> Self::Future {
self()
}
}
};
(($T:ident, $idx:tt)) => {
impl<$T, Fut, Out> FunctionSignature for fn($T) -> Fut
where
Fut: Future<Output = Out>
{
type Params = $T;
type Output = Out;
}
impl<F, $T, Fut> Operation<fn($T) -> Fut> for F
where
F: Fn($T) -> Fut,
Fut: Future,
{
type Future = Fut;
fn invoke(&self, $idx: $T) -> Self::Future {
self($idx)
}
}
};
($(($T:ident, $idx:tt)),+) => {
impl<$($T,)+ Fut, Out> FunctionSignature for fn($($T),+) -> Fut
where
Fut: Future<Output = Out>
{
type Params = ($($T,)+);
type Output = Out;
}
impl<F, $($T,)+ Fut> Operation<fn($($T),+) -> Fut> for F
where
F: Fn($($T),+) -> Fut,
Fut: Future,
{
type Future = Fut;
fn invoke(&self, ($($idx,)+): ($($T,)+)) -> Self::Future {
self($($idx,)+)
}
}
};
}
impl_function_traits!(); impl_function_traits!((T1, p1)); impl_function_traits!((T1, p1), (T2, p2)); impl_function_traits!((T1, p1), (T2, p2), (T3, p3)); impl_function_traits!((T1, p1), (T2, p2), (T3, p3), (T4, p4));
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_different_arities() {
async fn no_params() -> i32 {
42
}
async fn one_param(x: i32) -> i32 {
x + 1
}
async fn two_params(x: i32, y: i32) -> i32 {
x + y
}
async fn three_params(x: i32, y: i32, z: i32) -> i32 {
x + y + z
}
let _f0: fn() -> _ = no_params;
let _f1: fn(i32) -> _ = one_param;
let _f2: fn(i32, i32) -> _ = two_params;
let _f3: fn(i32, i32, i32) -> _ = three_params;
assert_eq!(no_params.invoke(()).await, 42);
assert_eq!(one_param.invoke(1).await, 2);
assert_eq!(two_params.invoke((1, 2)).await, 3);
assert_eq!(three_params.invoke((1, 2, 3)).await, 6);
}
}