use std::marker::PhantomData;
use futures::{future::BoxFuture, Future};
pub struct ShortBoxFuture<'b, 'a: 'b, T>(pub BoxFuture<'b, T>, PhantomData<&'a ()>);
impl<'b, 'a: 'b, T, F: Future<Output = T> + Send + 'b> From<F> for ShortBoxFuture<'b, 'a, T> {
fn from(f: F) -> Self {
Self(Box::pin(f), PhantomData)
}
}
#[cfg(test)]
mod tests {
use super::ShortBoxFuture;
pub async fn with_retries<'a, F>(f: F) -> usize
where
F: for<'b> Fn(&'b str) -> ShortBoxFuture<'b, 'a, Result<(), ()>>,
{
for i in 0..100 {
let transaction = format!("{i} transaction");
let result = f(&transaction).0.await;
drop(transaction);
match result {
Ok(()) => return i,
Err(()) => {}
}
}
0
}
pub async fn str_eq<'a, 'b>(a: &'a str, b: &'b str) -> Result<(), ()> {
if a == b {
Ok(())
} else {
Err(())
}
}
#[tokio::test]
async fn test_retries_closure() {
let data = format!("11 transaction");
let result = with_retries(|session| async { str_eq(session, &data).await }.into()).await;
assert_eq!(result, 11);
}
#[tokio::test]
async fn test_retries_fn() {
let data = format!("11 transaction");
let result = with_retries(|session| str_eq(session, &data).into()).await;
assert_eq!(result, 11);
}
#[tokio::test]
async fn test_retries_semi_inline() {
struct WrapStr<'a>(&'a str);
let data = format!("11 transaction");
let result = with_retries(|session| {
async {
if WrapStr(session).0 == &data {
Ok(())
} else {
Err(())
}
}
.into()
})
.await;
assert_eq!(result, 11);
}
}
#[cfg(test)]
#[allow(unused_imports)]
#[allow(dead_code)]
mod failing_tests {
use super::{
tests::{str_eq, with_retries},
ShortBoxFuture,
};
use futures::{
future::{BoxFuture, Future},
FutureExt,
};
use std::{marker::PhantomData, pin::Pin};
#[cfg(any())]
async fn with_retries_boxfuture_multi_bound<'a, F>(f: F)
where
F: for<'b> Fn(&'b str) -> BoxFuture<'a + 'b, ()>,
{
for i in 0..100 {
let transaction = format!("{i} transaction");
let result = f(&transaction).await;
drop(transaction);
}
}
#[cfg(any())]
async fn with_retries_multi_bound<'a, F>(f: F)
where
F: for<'b> Fn(&'b str) -> Pin<Box<dyn Future<Output = ()> + 'a + 'b>>,
{
for i in 0..100 {
let transaction = format!("{i} transaction");
f(&transaction).await;
drop(transaction);
}
}
#[cfg(any())]
async fn with_retries_impl_future<'a, F>(f: F)
where
F: for<'b> Fn(&'b str) -> (impl Future<Output = ()> + 'a + 'b),
{
for i in 0..100 {
let transaction = format!("{i} transaction");
f(&transaction).await;
drop(transaction);
}
}
async fn with_retries_single_hrtb<F>(f: F)
where
F: for<'a> Fn(&'a str) -> BoxFuture<'a, ()>,
{
for i in 0..100 {
let transaction = format!("{i} transaction");
f(&transaction).await;
drop(transaction);
}
}
#[cfg(any())]
async fn test_single_hrtb() {
let data = format!("11 transaction");
with_retries_single_hrtb(|session| {
async {
str_eq(session, &data);
}
.boxed()
})
.await;
}
#[cfg(any())]
async fn with_retries_no_hrtb<'a, F>(f: F)
where
F: Fn(&'a str) -> BoxFuture<'a, ()>,
{
for i in 0..100 {
let transaction = format!("{i} transaction");
f(&transaction).await;
drop(transaction);
}
}
#[cfg(any())]
pub async fn with_retries_hrtb_dependency<'a, F>(f: F)
where
F: for<'b: 'a> Fn(&'b str) -> BoxFuture<'b, ()>,
{
for i in 0..100 {
let transaction = format!("{i} transaction");
f(&transaction).await;
drop(transaction);
}
}
#[cfg(any())]
#[tokio::test]
async fn test_retries_fully_inline() {
let data = format!("11 transaction");
let result = with_retries(|session| {
async {
if session == &data {
Ok(())
} else {
Err(())
}
}
.into()
})
.await;
assert_eq!(result, 11);
}
#[cfg(any())]
async fn with_retries_flipped_bound_order<'a, F>(f: F)
where
F: for<'b> Fn(&'b str) -> ShortBoxFuture<'a, 'b, ()>,
{
for i in 0..100 {
let transaction = format!("{i} transaction");
f(&transaction).0.await;
drop(transaction);
}
}
}