do-over 0.1.0

Async resilience policies for Rust inspired by Polly
Documentation
//! Timeout Policy Example
//!
//! This example demonstrates the TimeoutPolicy which enforces
//! time-bounded operations.

use do_over::{error::DoOverError, policy::Policy, timeout::TimeoutPolicy};
use std::time::{Duration, Instant};

#[tokio::main]
async fn main() {
    println!("=== Do-Over Timeout Policy Example ===\n");

    // Scenario 1: Fast operation completes within timeout
    fast_operation_example().await;

    println!("\n{}\n", "".repeat(60));

    // Scenario 2: Slow operation exceeds timeout
    slow_operation_example().await;

    println!("\n{}\n", "".repeat(60));

    // Scenario 3: Edge case - operation completes just before timeout
    edge_case_example().await;
}

async fn fast_operation_example() {
    println!("📌 Scenario 1: Fast Operation (completes within timeout)");
    println!("   Configuration: timeout=500ms, operation_time=100ms\n");

    let policy = TimeoutPolicy::new(Duration::from_millis(500));
    let start = Instant::now();

    let result: Result<String, DoOverError<String>> = policy
        .execute(|| async {
            println!("   → Operation started...");
            tokio::time::sleep(Duration::from_millis(100)).await;
            println!("   → Operation completed!");
            Ok("Fast operation result".to_string())
        })
        .await;

    let elapsed = start.elapsed();
    println!("\n   Result: {:?}", result.unwrap());
    println!("   Elapsed: {:?} (well under 500ms timeout)", elapsed);
}

async fn slow_operation_example() {
    println!("📌 Scenario 2: Slow Operation (exceeds timeout)");
    println!("   Configuration: timeout=200ms, operation_time=500ms\n");

    let policy = TimeoutPolicy::new(Duration::from_millis(200));
    let start = Instant::now();

    let result: Result<String, DoOverError<String>> = policy
        .execute(|| async {
            println!("   → Operation started (will take 500ms)...");
            tokio::time::sleep(Duration::from_millis(500)).await;
            // This line should not be reached
            println!("   → This should not print!");
            Ok("Slow operation result".to_string())
        })
        .await;

    let elapsed = start.elapsed();

    match result {
        Err(DoOverError::Timeout) => {
            println!("   → Operation cancelled due to timeout!");
            println!("\n   Result: DoOverError::Timeout");
        }
        _ => println!("   Unexpected result: {:?}", result),
    }

    println!("   Elapsed: {:?} (stopped at ~200ms timeout)", elapsed);
    println!("\n   💡 The operation was cancelled - no resources wasted!");
}

async fn edge_case_example() {
    println!("📌 Scenario 3: Edge Case (operation completes just in time)");
    println!("   Configuration: timeout=300ms, operation_time=250ms\n");

    let policy = TimeoutPolicy::new(Duration::from_millis(300));
    let start = Instant::now();

    let result: Result<String, DoOverError<String>> = policy
        .execute(|| async {
            println!("   → Operation started (will take 250ms)...");
            tokio::time::sleep(Duration::from_millis(250)).await;
            println!("   → Operation completed just in time!");
            Ok("Completed with 50ms to spare".to_string())
        })
        .await;

    let elapsed = start.elapsed();

    match result {
        Ok(msg) => {
            println!("\n   Result: ✅ {}", msg);
        }
        Err(DoOverError::Timeout) => {
            println!("\n   Result: ❌ Timeout (race condition)");
        }
        Err(e) => {
            println!("\n   Result: {:?}", e);
        }
    }

    println!("   Elapsed: {:?}", elapsed);
}