use std::time::Duration;
use thiserror::Error;
#[derive(Debug, Clone, PartialEq, Eq, Error)]
pub enum TimeError {
#[error("operation timed out")]
Elapsed,
#[error("time provider shut down")]
Shutdown,
}
pub trait TimeProvider: Clone + Send + Sync + 'static {
fn sleep(
&self,
duration: Duration,
) -> impl std::future::Future<Output = Result<(), TimeError>> + Send;
fn now(&self) -> Duration;
fn timer(&self) -> Duration;
fn timeout<F, T>(
&self,
duration: Duration,
future: F,
) -> impl std::future::Future<Output = Result<T, TimeError>> + Send
where
F: std::future::Future<Output = T> + Send,
T: Send;
}
#[cfg(feature = "tokio-providers")]
#[derive(Debug, Clone)]
pub struct TokioTimeProvider {
start_time: std::time::Instant,
}
#[cfg(feature = "tokio-providers")]
impl TokioTimeProvider {
#[must_use]
pub fn new() -> Self {
Self {
start_time: std::time::Instant::now(),
}
}
}
#[cfg(feature = "tokio-providers")]
impl Default for TokioTimeProvider {
fn default() -> Self {
Self::new()
}
}
#[cfg(feature = "tokio-providers")]
impl TimeProvider for TokioTimeProvider {
async fn sleep(&self, duration: Duration) -> Result<(), TimeError> {
tokio::time::sleep(duration).await;
Ok(())
}
fn now(&self) -> Duration {
self.start_time.elapsed()
}
fn timer(&self) -> Duration {
self.now()
}
async fn timeout<F, T>(&self, duration: Duration, future: F) -> Result<T, TimeError>
where
F: std::future::Future<Output = T> + Send,
T: Send,
{
tokio::time::timeout(duration, future)
.await
.map_err(|_| TimeError::Elapsed)
}
}