use std::{
future::{Future, IntoFuture},
panic::Location,
time::Duration,
};
use tokio::time::Instant;
pub trait TimeoutExt: IntoFuture + Sized {
#[track_caller]
fn within(self, timeout: Duration) -> impl Future<Output = Self::Output> {
let caller = Location::caller();
async move {
tokio::time::timeout(timeout, self)
.await
.unwrap_or_else(|_elapsed| {
panic!("future at {caller} did not complete within {timeout:?}")
})
}
}
}
impl<F: IntoFuture> TimeoutExt for F {}
#[track_caller]
pub fn eventually(
timeout: Duration,
mut condition: impl AsyncFnMut() -> bool,
) -> impl Future<Output = ()> {
let caller = Location::caller();
async move {
let deadline = Instant::now() + timeout;
while !condition().await {
assert!(
Instant::now() < deadline,
"condition at {caller} did not become true within {timeout:?}"
);
tokio::time::sleep(Duration::from_millis(10)).await;
}
}
}