use std::fmt;
use std::future::Future;
use std::time::Duration;

use crate::backend::{Active, Backend};

/// Error returned when a `timeout` expires.
#[derive(Debug, Clone, Copy)]
pub struct Elapsed;

impl fmt::Display for Elapsed {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str("deadline elapsed")
    }
}

impl std::error::Error for Elapsed {}

/// Sleep for the given duration.
pub async fn sleep(dur: Duration) {
    Active::sleep(dur).await
}

/// Run a future with a timeout. Returns `Err(Elapsed)` if the deadline passes
/// before the future completes.
pub async fn timeout<T, F: Future<Output = T>>(dur: Duration, fut: F) -> Result<T, Elapsed> {
    futures_lite::future::or(async { Ok(fut.await) }, async {
        sleep(dur).await;
        Err(Elapsed)
    })
    .await
}
