use std::future::Future;

use tokio::task::yield_now;

macro_rules! async_fn_mut {
    ($statement:block) => {{
        struct A {}
        impl AsyncFnMut<()> for A {
            type Output = ();

            async fn call(&mut self, _args: ()) -> Self::Output {
                $statement
            }
        }
        A{}
    }};
}


#[tokio::main]
async fn main() {
    println!(">>>>> case 1");
    do_something(|| async {
        println!("in callback");
        yield_now().await;
    }).await;

    println!(">>>>> case 2");
    struct A {}
    impl AsyncFnMut<()> for A {
        type Output = ();

        async fn call(&mut self, _args: ()) -> Self::Output {
            println!("in callback-2");
            yield_now().await;
        }
    }
    do_something2(A {}).await;

    println!(">>>>> case 3");
    do_something2(async_fn_mut!({
            println!("in callback-2");
            yield_now().await;
    })).await;
}

trait AsyncFnMut<A> {
    type Output;
    async fn call(&mut self, args: A) -> Self::Output;
}

async fn do_something<C, F>(mut callback: C)
    where C: FnMut() -> F,
          F: Future<Output=()> {
    println!("before callback");
    callback().await;
    println!("after callback");
}

async fn do_something2<C>(mut callback: C) where C: AsyncFnMut<(), Output=()> {
    println!("before callback-2");
    callback.call(()).await;
    println!("after callback-2");
}
