use durare::{DurableContext, DurableEngine, InMemoryProvider, Result, WorkflowOptions};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use std::time::{Duration, Instant};
static CHARGES: AtomicUsize = AtomicUsize::new(0);
#[durare::step]
async fn charge(ctx: &DurableContext, month: u32, cents: i64) -> Result<i64> {
CHARGES.fetch_add(1, Ordering::SeqCst);
println!(" >> charging {cents} cents for month {month}");
Ok(cents)
}
#[durare::workflow]
async fn subscription(ctx: DurableContext, months: u32) -> Result<i64> {
let mut total = 0;
for m in 1..=months {
total += charge(&ctx, m, 999).await?;
if m < months {
ctx.sleep(Duration::from_millis(700)).await?;
}
}
Ok(total)
}
#[tokio::main]
async fn main() -> Result<()> {
let engine = DurableEngine::new(Arc::new(InMemoryProvider::new())).await?;
let t0 = Instant::now();
let total: i64 = engine
.start_with(Subscription, 3u32, WorkflowOptions::with_id("sub-ada"))
.await?
.await?;
println!(
"[run 1] billed {total} cents over 3 months in {:?} — {} charges\n",
t0.elapsed(),
CHARGES.load(Ordering::SeqCst)
);
let t1 = Instant::now();
let total_again: i64 = engine
.start_with(Subscription, 3u32, WorkflowOptions::with_id("sub-ada"))
.await?
.await?;
println!(
"[run 2] same id -> {total_again} cents in {:?} — still {} charges (no re-nap, no re-bill)",
t1.elapsed(),
CHARGES.load(Ordering::SeqCst)
);
assert_eq!(total, total_again);
assert_eq!(
CHARGES.load(Ordering::SeqCst),
3,
"run 2 must not re-charge"
);
println!("\n[ok] durable naps + charges checkpointed: replay never re-waits or re-bills");
Ok(())
}