use std::fmt;
use std::future::Future;
use std::sync::{Mutex, MutexGuard, PoisonError};
use std::task::Poll;
use std::time::Duration;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Elapsed {
pub seconds: f64,
}
impl fmt::Display for Elapsed {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "nothing arrived within {}s", self.seconds)
}
}
impl std::error::Error for Elapsed {}
pub struct Lock;
impl Lock {
pub fn on<T>(mutex: &Mutex<T>) -> MutexGuard<'_, T> {
mutex.lock().unwrap_or_else(PoisonError::into_inner)
}
}
pub struct Timeout;
impl Timeout {
#[inline]
pub fn armed(seconds: f64) -> bool {
seconds.is_finite() && seconds > 0.0
}
pub fn duration(seconds: f64) -> Option<Duration> {
Self::armed(seconds).then(|| Duration::try_from_secs_f64(seconds).unwrap_or(Duration::MAX))
}
pub async fn within<T>(seconds: f64, operation: impl Future<Output = T>) -> Result<T, Elapsed> {
if !Self::armed(seconds) {
return Ok(operation.await);
}
let mut operation = std::pin::pin!(operation);
if let Poll::Ready(value) = std::future::poll_fn(|cx| Poll::Ready(operation.as_mut().poll(cx))).await {
return Ok(value);
}
let wait = Self::duration(seconds).unwrap_or(Duration::MAX);
tokio::time::timeout(wait, operation).await.map_err(|_| Elapsed { seconds })
}
}