cb_fut

This crate provide utility macros to convert a call to function that need a callback to handle return values into a function that either return a Future that resolve to values or a Stream that yield values. It introduce extra syntax -> () into functional call signature.

Limitation

Following are limitations of this crate. - If the callback is not intended to return a value, don't use these two macros. It'll break the function. - If the function also return value, the returned value will be silently dropped. - The macro support only single callback conversion. If the function take more than one callbacks to return value on different circumstances, it cannot be use. - It will be slower compare to using original callback. This is because it require some kind of indirection to direct a callback through channel into a Future.

How to use

This crate provide two macros. 1. once - for convert a call to a function with a callback into a Future that resolve to values which used to be return as callback parameters. 1. stream - for convert a call to a function with a callback into a Stream that yield values which usually pass to callback as parameters.

The main difference between the two is once shall be used if a callback will be called exactly once while stream shall be used if a callback will be called multiple times.

These two macro need a special syntax to identify number of expected return variables. The syntax is -> () which resemble the function signature for returning type, e.g. Fn(i32) -> ().

These conversion result in Future that resolve to tuple of values. See examples below.

Examples

Turn a function with callback on last parameter into a future. ```rust // A function that need a callback fn func(v: i32, cb: impl FnOnce(i32, i32)) { std::thread::sleep(std::time::Duration::from_secs(2)); cb(v, v * 2) }

// Use once! to convert the call to func to return a Future instead. // We use ->(a, b) to tell macro that Future shall return two variables. let (a, b) = futures::executor::block_on(once!(func(2 + 3, ->(a, b))));

asserteq!(5, a); asserteq!(10, b); The callback can be in anywhere in function signature. The callback placeholder just need to reflect that too. rust // A function that put callback in the middle between other two parameters fn func(u: i32, cb: impl FnOnce(i32, i32), v: i32) { std::thread::sleep(std::time::Duration::fromsecs(2)); cb(u, v) } // We use ->(a, b) between 1, and, 2 + 3 to tell macro that this parameter is a callback and it take 2 parameters. let (a, b) = futures::executor::blockon(once!(func(1, ->(a, b), 2 + 3))); asserteq!(1, a); asserteq!(5, b); In case the callback take no argument, we need to put `-> ()` rust // a function that take no arguments callback fn func(v: i32, cb: impl FnOnce()) { std::thread::sleep(std::time::Duration::fromsecs(2)); cb() } // A callback placeholder with no argument futures::executor::block_on(once!(func(2 + 3, -> ()))); If callback will be called multiple times, use `stream!` rust use futures::stream::StreamExt; // A function that take callback as first argument. // It'll call callback 5 times with two arguments, an original value and the original value times number of called. fn func(mut cb: impl FnMut(i32, i32), v: i32) { for i in 0..5 { cb(v, v * i) } } let mut counter = 0;

// stream! will return CBStream which implement Stream trait. We use enumerate and for_each from StreamExt trait to iterate over each values tuples that suppose to be passed to callback function. // The for_each method signature require a return value of type Future for given callback. The final return value from for_each is a single consolidated Future which when resolve, all Futures inside it are all resolved. futures::executor::blockon(stream!(func(->(a, b), 2 + 3)).enumerate().foreach(|(i, fut)| { counter += 1; async move { let (a, b) = fut; asserteq!(5, a); asserteq!(5 * i as i32, b); } }));

assert_eq!(5, counter); ```