do-over 0.1.0

Async resilience policies for Rust inspired by Polly
Documentation
//! Timeout policy for time-bounded operations.
//!
//! The timeout policy ensures operations complete within a specified duration,
//! preventing indefinite hangs.
//!
//! # Examples
//!
//! ```rust
//! use do_over::{policy::Policy, timeout::TimeoutPolicy, error::DoOverError};
//! use std::time::Duration;
//!
//! # async fn example() -> Result<(), DoOverError<std::io::Error>> {
//! let policy = TimeoutPolicy::new(Duration::from_secs(5));
//!
//! match policy.execute(|| async {
//!     // Operation that might hang
//!     Ok::<_, DoOverError<std::io::Error>>("completed")
//! }).await {
//!     Ok(result) => println!("Success: {}", result),
//!     Err(DoOverError::Timeout) => println!("Operation timed out"),
//!     Err(e) => println!("Error: {:?}", e),
//! }
//! # Ok(())
//! # }
//! ```

use std::time::Duration;
use tokio::time;
use crate::policy::Policy;
use crate::error::DoOverError;

/// A policy that enforces a maximum duration for operations.
///
/// If the operation doesn't complete within the timeout, it is cancelled
/// and [`DoOverError::Timeout`] is returned.
///
/// # Examples
///
/// ```rust
/// use do_over::{policy::Policy, timeout::TimeoutPolicy, error::DoOverError};
/// use std::time::Duration;
///
/// # async fn example() {
/// let policy = TimeoutPolicy::new(Duration::from_secs(10));
///
/// let result: Result<String, DoOverError<String>> = policy.execute(|| async {
///     // Long-running operation
///     Ok("done".to_string())
/// }).await;
/// # }
/// ```
#[derive(Clone)]
pub struct TimeoutPolicy {
    timeout: Duration,
}

impl TimeoutPolicy {
    /// Create a new timeout policy.
    ///
    /// # Arguments
    ///
    /// * `timeout` - Maximum duration to wait for the operation
    ///
    /// # Examples
    ///
    /// ```rust
    /// use do_over::timeout::TimeoutPolicy;
    /// use std::time::Duration;
    ///
    /// // 5 second timeout
    /// let policy = TimeoutPolicy::new(Duration::from_secs(5));
    /// ```
    pub fn new(timeout: Duration) -> Self {
        Self { timeout }
    }
}

#[async_trait::async_trait]
impl<E> Policy<DoOverError<E>> for TimeoutPolicy
where
    E: Send + Sync,
{
    async fn execute<F, Fut, T>(&self, f: F) -> Result<T, DoOverError<E>>
    where
        F: Fn() -> Fut + Send + Sync,
        Fut: std::future::Future<Output = Result<T, DoOverError<E>>> + Send,
        T: Send,
    {
        match time::timeout(self.timeout, f()).await {
            Ok(r) => r,
            Err(_) => Err(DoOverError::Timeout),
        }
    }
}