use futures_util::future::BoxFuture;
use crate::async_closure::with_lifetime::WithLifetime;
pub(crate) mod with_lifetime;
pub trait AsyncFnMut<Args: WithLifetime>: Send {
type Output;
fn call<'a>(&'a mut self, args: Args::Type<'a>) -> BoxFuture<'a, Self::Output>;
}
pub struct DynAsyncFnMut<'a, Args: WithLifetime, Output>(
Box<dyn AsyncFnMut<Args, Output = Output> + 'a>,
);
impl<'c, Args: WithLifetime, Output> AsyncFnMut<Args> for DynAsyncFnMut<'c, Args, Output> {
type Output = Output;
fn call<'a>(&'a mut self, args: <Args as WithLifetime>::Type<'a>) -> BoxFuture<'a, Output> {
self.0.call(args)
}
}
#[doc(hidden)]
pub fn __make_closure<'c, Args, Output, C, F>(
context: C,
function: F,
) -> DynAsyncFnMut<'c, Args, Output>
where
Args: WithLifetime,
C: Send + 'c,
F: for<'a> FnMut(&'a mut C, Args::Type<'a>) -> BoxFuture<'a, Output> + Send + 'static,
{
struct AsyncMutClosure<C, F> {
context: C,
function: F,
}
impl<Args, Output, F, C> AsyncFnMut<Args> for AsyncMutClosure<C, F>
where
Args: WithLifetime,
C: Send,
F: for<'a> FnMut(&'a mut C, Args::Type<'a>) -> BoxFuture<'a, Output> + Send,
{
type Output = Output;
fn call<'a>(&'a mut self, args: <Args as WithLifetime>::Type<'a>) -> BoxFuture<'a, Output> {
(self.function)(&mut self.context, args)
}
}
DynAsyncFnMut(Box::new(AsyncMutClosure { context, function }))
}
#[macro_export]
macro_rules! closure {
([$($tt:tt)*], $body:expr) => {
$crate::__make_closure(
$crate::__closure_make_tuple!($($tt)*),
move |ctx, args| {
$crate::__closure_destruct_tuple!(ctx => $($tt)*);
::std::boxed::Box::pin(async move {($body)(args).await})
}
)
};
($body:expr) => {
$crate::closure!([], $body)
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __closure_make_tuple {
($(,)?) => {
()
};
($var:ident $(, $( $rest:tt )*)?) => {
($var, $crate::__closure_make_tuple!($($($rest)*)?))
};
(&mut $var:ident $(, $( $rest:tt )*)?) => {
(&mut $var, $crate::__closure_make_tuple!($($($rest)*)?))
};
(& $var:ident $(, $( $rest:tt )*)?) => {
(& $var, $crate::__closure_make_tuple!($($($rest)*)?))
};
($alias:ident = $expr:expr $(, $( $rest:tt )*)?) => {
($expr, $crate::__closure_make_tuple!($($($rest)*)?))
};
(&mut $alias:ident = $expr:expr $(, $( $rest:tt )*)?) => {
($expr, $crate::__closure_make_tuple!($($($rest)*)?))
};
(& $alias:ident = $expr:expr $(, $( $rest:tt )*)?) => {
($expr, $crate::__closure_make_tuple!($($($rest)*)?))
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __closure_destruct_tuple {
($ctx:ident => $(,)?) => {
let () = $ctx;
};
($ctx:ident => $var:ident $(, $($rest:tt)*)?) => {
let $var = &mut $ctx.0;
let $ctx = &mut $ctx.1;
$crate::__closure_destruct_tuple!($ctx => $($($rest)*)?);
};
($ctx:ident => &mut $var:ident $(, $($rest:tt)*)?) => {
let $var = &mut *$ctx.0;
let $ctx = &mut $ctx.1;
$crate::__closure_destruct_tuple!($ctx => $($($rest)*)?);
};
($ctx:ident => & $var:ident $(, $($rest:tt)*)?) => {
let $var = & *$ctx.0;
let $ctx = &mut $ctx.1;
$crate::__closure_destruct_tuple!($ctx => $($($rest)*)?);
};
($ctx:ident => $alias:ident = $expr:expr $(, $($rest:tt)*)?) => {
let $alias = &mut $ctx.0;
let $ctx = &mut $ctx.1;
$crate::__closure_destruct_tuple!($ctx => $($($rest)*)?);
};
($ctx:ident => &mut $alias:ident = $expr:expr $(, $($rest:tt)*)?) => {
let $alias = &mut *$ctx.0;
let $ctx = &mut $ctx.1;
$crate::__closure_destruct_tuple!($ctx => $($($rest)*)?);
};
($ctx:ident => & $alias:ident = $expr:expr $(, $($rest:tt)*)?) => {
let $alias = & *$ctx.0;
let $ctx = &mut $ctx.1;
$crate::__closure_destruct_tuple!($ctx => $($($rest)*)?);
};
}
#[cfg(test)]
mod tests {
use super::*;
use crate::async_closure::with_lifetime::MutWithLifetime;
async fn call_in_loop<F: AsyncFnMut<MutWithLifetime<i32>, Output = bool>>(mut f: F) {
let mut x = 10;
while !f.call(&mut x).await {
x += 1;
}
}
#[tokio::test]
async fn test_closure_macro() {
call_in_loop(closure!(async |x: &mut i32| {
*x -= 2;
*x == 0
}))
.await;
let mut d = 1;
call_in_loop(closure!([&mut d], async |x: &mut i32| {
*x -= *d;
*d += 1;
*x < 0
}))
.await;
assert!(d > 1);
call_in_loop(closure!([&d], async |x: &mut i32| {
*x -= d;
*x < 0
}))
.await;
call_in_loop(closure!([d], async |x: &mut i32| {
*x -= *d;
*x < 0
}))
.await;
let mut f = 0;
call_in_loop(closure!([&mut d, &mut f], async |x: &mut i32| {
*d += *f;
*f += 1;
*x -= *d;
*x < 0
}))
.await;
call_in_loop(closure!([&mut d, &mut f,], async |x: &mut i32| {
*d += *f;
*f += 1;
*x -= *d;
*x < 0
}))
.await;
call_in_loop(closure!(
[&mut d = &mut f, &mut f = &mut d,],
async |x: &mut i32| {
*d += *f;
*f += 1;
*x -= *d;
*x < 0
}
))
.await;
}
}