use do_over::{error::DoOverError, policy::Policy, rate_limit::RateLimiter};
use std::time::{Duration, Instant};
#[tokio::main]
async fn main() {
println!("=== Do-Over Rate Limiter Example ===\n");
burst_example().await;
println!("\n{}\n", "─".repeat(60));
replenishment_example().await;
}
async fn burst_example() {
println!("📌 Scenario 1: Burst of Requests");
println!(" Configuration: capacity=5, interval=1s");
println!(" Sending 8 requests in rapid succession...\n");
let limiter = RateLimiter::new(5, Duration::from_secs(1));
let start = Instant::now();
let mut allowed = 0;
let mut rejected = 0;
for i in 1..=8 {
let result: Result<String, DoOverError<String>> = limiter
.execute(|| async move { Ok(format!("Request {} processed", i)) })
.await;
let elapsed = start.elapsed().as_millis();
match result {
Ok(msg) => {
println!(" [+{:>4}ms] Request {}: ✅ {}", elapsed, i, msg);
allowed += 1;
}
Err(DoOverError::BulkheadFull) => {
println!(" [+{:>4}ms] Request {}: 🚫 Rate limited", elapsed, i);
rejected += 1;
}
Err(e) => println!(" [+{:>4}ms] Request {}: Error - {:?}", elapsed, i, e),
}
}
println!("\n Summary:");
println!(" - Allowed: {} (capacity was 5)", allowed);
println!(" - Rejected: {} (exceeded capacity)", rejected);
println!("\n 💡 First 5 requests used the burst capacity, remaining 3 were rate limited");
}
async fn replenishment_example() {
println!("📌 Scenario 2: Token Replenishment");
println!(" Configuration: capacity=3, interval=500ms");
println!(" Demonstrating token refill after interval...\n");
let limiter = RateLimiter::new(3, Duration::from_millis(500));
let start = Instant::now();
println!(" Phase 1: Using up all 3 tokens");
for i in 1..=4 {
let result: Result<String, DoOverError<String>> = limiter
.execute(|| async move { Ok(format!("Request {}", i)) })
.await;
let elapsed = start.elapsed().as_millis();
match result {
Ok(_) => println!(" [+{:>4}ms] Request {}: ✅ Token consumed", elapsed, i),
Err(DoOverError::BulkheadFull) => {
println!(" [+{:>4}ms] Request {}: 🚫 No tokens available", elapsed, i)
}
Err(e) => println!(" [+{:>4}ms] Request {}: Error - {:?}", elapsed, i, e),
}
}
println!("\n Phase 2: Waiting 500ms for token replenishment...");
tokio::time::sleep(Duration::from_millis(500)).await;
let elapsed = start.elapsed().as_millis();
println!(" [+{:>4}ms] → Tokens should be replenished now", elapsed);
println!("\n Phase 3: Sending requests after replenishment");
for i in 5..=8 {
let result: Result<String, DoOverError<String>> = limiter
.execute(|| async move { Ok(format!("Request {}", i)) })
.await;
let elapsed = start.elapsed().as_millis();
match result {
Ok(_) => println!(" [+{:>4}ms] Request {}: ✅ Token consumed", elapsed, i),
Err(DoOverError::BulkheadFull) => {
println!(" [+{:>4}ms] Request {}: 🚫 No tokens available", elapsed, i)
}
Err(e) => println!(" [+{:>4}ms] Request {}: Error - {:?}", elapsed, i, e),
}
}
println!(
"\n 💡 After 500ms, all 3 tokens were replenished and available for use"
);
}