#![cfg_attr(
all(
feature = "generate-async",
feature = "generate-blocking",
feature = "macros"
),
doc = r#"
This code may look innocent, but it will fail to compile with error [`E0720`]:
```compile_fail
# use desaturate::desaturate;
#[desaturate]
async fn recursive(arg: i32) -> i32 {
if arg <= 1 {
1
} else {
arg * recurse(arg - 1).await
}
}
```
The [`impl Desaturated<_>`] patterns removes the ability to perform recursive calls. To perform
recursion, we must use `Box<dyn _>`. Unfortunately, as this uses dynamic dispatch, this comes with
a small runtime performance cost.
```
# use desaturate::{Blocking, desaturate, IntoDesaturatedWith, Desaturated, boxed::{BoxedDesaturated, BoxedDesaturatedWith}};
# use core::{pin::Pin, future::Future};
#[desaturate]
async fn recursive(arg: i32) -> i32 {
if arg <= 1 {
1
} else {
let dyn_recursive =
recursive
.boxed_future_with()
.desaturate_with(arg - 1,
recursive.boxed_blocking_with());
arg * dyn_recursive.await
}
}
# #[tokio::main]
async fn main() {
assert_eq!(recursive(5).await, recursive(5).call())
}
```
[`impl Desaturated<_>`]: Desaturated
[`E0720`]: https://doc.rust-lang.org/error_codes/E0720.html
"#
)]
use crate::{macros::features, Desaturated};
use core::{future::Future, pin::Pin};
pub trait BoxedDesaturated<'a, Out> {
type BoxedFuture: Future<Output = Out>;
type BoxedBlocking: FnOnce() -> Out;
fn boxed_future(self) -> Self::BoxedFuture;
fn boxed_blocking(self) -> Self::BoxedBlocking;
}
impl<'a, Out, D: Desaturated<Out> + 'a> BoxedDesaturated<'a, Out> for D {
type BoxedFuture = Pin<Box<dyn Future<Output = Out> + 'a>>;
#[inline(always)]
fn boxed_future(self) -> Self::BoxedFuture {
features! {async:
Box::pin(self.into_future())
}
features! {!async:
use core::future::IntoFuture;
Box::pin(async {panic!(r#"Called a the blocking function of a Desaturated without the "generate-blocking" feature flag"#) }.into_future())
}
}
type BoxedBlocking = Box<dyn FnOnce() -> Out + 'a>;
#[inline(always)]
fn boxed_blocking(self) -> Self::BoxedBlocking {
features! {fn:
Box::new(move || {
self.call()
})
}
features! {!fn:
Box::new(|| panic!(r#"Called a the blocking function of a Desaturated without the "generate-blocking" feature flag"#))
}
}
}
pub trait BoxedDesaturatedWith<'a, Args, Out> {
type BoxedFuture: FnOnce(Args) -> Pin<Box<dyn Future<Output = Out> + 'a>>;
features! {fn:
type BoxedBlocking: FnOnce(Args) -> Out;
}
features! {!fn:
type BoxedBlocking;
}
fn boxed_future_with(self) -> Self::BoxedFuture;
fn boxed_blocking_with(self) -> Self::BoxedBlocking;
}
impl<'a, Out, Args: 'a, D: Desaturated<Out> + 'a, F: FnOnce(Args) -> D + 'a>
BoxedDesaturatedWith<'a, Args, Out> for F
{
type BoxedFuture = Box<dyn FnOnce(Args) -> Pin<Box<dyn Future<Output = Out> + 'a>> + 'a>;
type BoxedBlocking = Box<dyn FnOnce(Args) -> Out + 'a>;
#[inline(always)]
fn boxed_future_with(self) -> Self::BoxedFuture {
features! {async:
Box::new(move |args| Box::pin(self(args).into_future()))
}
features! {!async:
use core::future::IntoFuture;
Box::new(|_| Box::pin(async { panic!(r#"Called a the async function of a DesaturatedWith without the "generate-async" feature flag"#) }.into_future()))
}
}
#[inline(always)]
fn boxed_blocking_with(self) -> Self::BoxedBlocking {
features! {fn:
Box::new(move |args| self(args).call())
}
features! {!fn:
Box::new(|_| panic!(r#"Called a the blocking function of a BoxedDesaturatedWith without the "generate-blocking" feature flag"#))
}
}
}