use do_over::{circuit_breaker::CircuitBreaker, error::DoOverError, policy::Policy};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use std::time::Duration;
#[tokio::main]
async fn main() {
println!("=== Do-Over Circuit Breaker Example ===\n");
println!("Configuration: failure_threshold=3, reset_timeout=2s\n");
let breaker = CircuitBreaker::new(3, Duration::from_secs(2));
let failure_mode = Arc::new(AtomicUsize::new(1));
phase1_trigger_failures(&breaker, &failure_mode).await;
println!("\n{}\n", "─".repeat(60));
phase2_circuit_open(&breaker).await;
println!("\n{}\n", "─".repeat(60));
phase3_half_open(&breaker, &failure_mode).await;
println!("\n{}\n", "─".repeat(60));
phase4_recovery(&breaker, &failure_mode).await;
}
async fn phase1_trigger_failures(
breaker: &CircuitBreaker,
failure_mode: &Arc<AtomicUsize>,
) {
println!("📌 Phase 1: Triggering failures to open the circuit");
println!(" State: CLOSED (allowing requests)\n");
for i in 1..=4 {
let fm = Arc::clone(failure_mode);
let result: Result<String, DoOverError<String>> = breaker
.execute(|| {
let mode = Arc::clone(&fm);
async move {
if mode.load(Ordering::SeqCst) == 1 {
Err(DoOverError::Inner("Service error".to_string()))
} else {
Ok("Success".to_string())
}
}
})
.await;
match &result {
Err(DoOverError::Inner(e)) => {
println!(" Request {}: ❌ Failed - {}", i, e);
if i == 3 {
println!(" → Failure threshold reached! Circuit is now OPEN");
}
}
Err(DoOverError::CircuitOpen) => {
println!(" Request {}: 🚫 Rejected - Circuit is OPEN", i);
}
Ok(_) => println!(" Request {}: ✅ Success", i),
Err(e) => println!(" Request {}: Error - {:?}", i, e),
}
}
}
async fn phase2_circuit_open(breaker: &CircuitBreaker) {
println!("📌 Phase 2: Circuit is OPEN - Requests are rejected immediately");
println!(" (No actual service calls are made)\n");
for i in 1..=3 {
let result: Result<String, DoOverError<String>> = breaker
.execute(|| async {
println!(" ⚠️ This should not print - service was called!");
Ok("Success".to_string())
})
.await;
match result {
Err(DoOverError::CircuitOpen) => {
println!(" Request {}: 🚫 Instantly rejected (CircuitOpen)", i);
}
_ => println!(" Request {}: Unexpected result", i),
}
}
println!("\n → Requests fail fast without calling the service");
}
async fn phase3_half_open(breaker: &CircuitBreaker, failure_mode: &Arc<AtomicUsize>) {
println!("📌 Phase 3: Waiting for reset timeout (2s)...");
tokio::time::sleep(Duration::from_secs(2)).await;
println!(" → Reset timeout elapsed, circuit is now HALF-OPEN\n");
println!(" State: HALF-OPEN (testing if service recovered)");
println!(" First request will be a test request...\n");
let fm = Arc::clone(failure_mode);
let result: Result<String, DoOverError<String>> = breaker
.execute(|| {
let mode = Arc::clone(&fm);
async move {
println!(" → Test request executed (service is still failing)");
if mode.load(Ordering::SeqCst) == 1 {
Err(DoOverError::Inner("Service still down".to_string()))
} else {
Ok("Success".to_string())
}
}
})
.await;
match result {
Err(DoOverError::Inner(e)) => {
println!(" Test request: ❌ Failed - {}", e);
println!(" → Circuit returns to OPEN state");
}
_ => println!(" Test request: {:?}", result),
}
}
async fn phase4_recovery(breaker: &CircuitBreaker, failure_mode: &Arc<AtomicUsize>) {
println!("📌 Phase 4: Service Recovery");
println!(" Waiting for reset timeout again (2s)...");
tokio::time::sleep(Duration::from_secs(2)).await;
failure_mode.store(0, Ordering::SeqCst);
println!(" → Service has recovered! Switching to success mode\n");
println!(" State: HALF-OPEN (testing again)");
let fm = Arc::clone(failure_mode);
let result: Result<String, DoOverError<String>> = breaker
.execute(|| {
let mode = Arc::clone(&fm);
async move {
if mode.load(Ordering::SeqCst) == 1 {
Err(DoOverError::Inner("Service error".to_string()))
} else {
Ok("Service recovered!".to_string())
}
}
})
.await;
match result {
Ok(msg) => {
println!(" Test request: ✅ {}", msg);
println!(" → Circuit is now CLOSED (back to normal operation)");
}
Err(e) => println!(" Test request: {:?}", e),
}
println!("\n Verifying circuit is fully operational:");
for i in 1..=3 {
let result: Result<String, DoOverError<String>> = breaker
.execute(|| async { Ok(format!("Response {}", i)) })
.await;
match result {
Ok(msg) => println!(" Request {}: ✅ {}", i, msg),
Err(e) => println!(" Request {}: {:?}", i, e),
}
}
println!("\n ✨ Circuit breaker has fully recovered!");
}