use {
core::{
panic::Location,
pin::Pin,
task::{Context, Poll, Waker},
time::Duration,
},
derive_more::Debug,
futures::TryFutureExt,
std::{process::Output, sync::OnceLock},
tokio::time::Timeout,
};
fn time_factor() -> f32 {
static MULTIPLIER: OnceLock<f32> = OnceLock::new();
*MULTIPLIER.get_or_init(|| {
std::env::var("TIME_FACTOR")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(1.0)
})
}
pub fn secs(count: u64) -> Duration {
let count = (count as f32 * time_factor()) * 1000.0;
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
Duration::from_millis(count as u64)
}
pub fn millis(count: u64) -> Duration {
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
let count = (count as f32 * time_factor()) as u64;
Duration::from_millis(count)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
#[error("timeout elapsed after {0:?} at {1}")]
pub struct TimeoutElapsed(pub Duration, pub &'static Location<'static>);
#[track_caller]
pub fn timeout_s<F>(
count: u64,
future: F,
) -> impl Future<Output = Result<F::Output, TimeoutElapsed>>
where
F: IntoFuture,
{
let caller = Location::caller();
let duration = secs(count);
tokio::time::timeout(duration, future)
.map_err(move |_| TimeoutElapsed(duration, caller))
}
#[track_caller]
pub fn timeout_after<F>(
duration: Duration,
future: F,
) -> impl Future<Output = Result<F::Output, TimeoutElapsed>>
where
F: IntoFuture,
{
let caller = Location::caller();
tokio::time::timeout(duration, future)
.map_err(move |_| TimeoutElapsed(duration, caller))
}
#[track_caller]
pub fn timeout_ms<F>(
count: u64,
future: F,
) -> impl Future<Output = Result<F::Output, TimeoutElapsed>>
where
F: IntoFuture,
{
let caller = Location::caller();
let duration = millis(count);
tokio::time::timeout(duration, future)
.map_err(move |_| TimeoutElapsed(duration, caller))
}
pub fn sleep_s(count: u64) -> impl Future<Output = ()> {
tokio::time::sleep(secs(count))
}
pub fn sleep_ms(count: u64) -> impl Future<Output = ()> {
tokio::time::sleep(millis(count))
}