use durare::{
DurableContext, DurableEngine, InMemoryProvider, PostgresProvider, Result, StateProvider,
WorkflowOptions,
};
use serde::{Deserialize, Serialize};
use std::sync::Arc;
#[derive(Serialize, Deserialize, Clone)]
struct Order {
id: String,
amount_cents: i64,
email: String,
}
#[derive(Serialize, Deserialize, Debug)]
struct Receipt {
charge_id: String,
shipment_id: String,
}
#[durare::step]
async fn charge_card(ctx: &DurableContext, order_id: String, amount_cents: i64) -> Result<String> {
println!(" >> CHARGING card for {amount_cents} cents (real side effect!)");
Ok(format!("ch_{order_id}"))
}
#[durare::step]
async fn create_shipment(ctx: &DurableContext, order_id: String) -> Result<String> {
println!(" >> creating shipment");
Ok(format!("sh_{order_id}"))
}
#[durare::step]
async fn send_email(ctx: &DurableContext, to: String) -> Result<()> {
println!(" >> emailing receipt to {to}");
Ok(())
}
#[durare::workflow]
async fn process_order(ctx: DurableContext, order: Order) -> Result<Receipt> {
let charge_id = charge_card(&ctx, order.id.clone(), order.amount_cents).await?;
println!(" charge_id = {charge_id}");
fail::fail_point!("after_charge", |_| {
println!(" !! failpoint `after_charge` fired — crashing after charge");
std::process::exit(1);
});
let shipment_id = create_shipment(&ctx, order.id.clone()).await?;
send_email(&ctx, order.email.clone()).await?;
Ok(Receipt {
charge_id,
shipment_id,
})
}
#[tokio::main]
async fn main() -> Result<()> {
let _failpoints = fail::FailScenario::setup();
let provider: Arc<dyn StateProvider> = match std::env::var("DATABASE_URL") {
Ok(url) => {
println!("[backend] Postgres");
Arc::new(PostgresProvider::connect(&url).await?)
}
Err(_) => {
println!("[backend] in-memory (set DATABASE_URL for crash-across-restart)");
Arc::new(InMemoryProvider::new())
}
};
let engine = DurableEngine::new(provider).await?;
let resumed = engine.recover().await?;
if resumed > 0 {
println!("[recover] resumed {resumed} incomplete workflow(s)");
}
let order = Order {
id: "1001".to_string(),
amount_cents: 4999,
email: "sam@example.com".to_string(),
};
println!("[start] process_order wf-order-1001");
let handle = engine
.start_with(
ProcessOrder,
order,
WorkflowOptions::with_id("wf-order-1001"),
)
.await?;
match handle.await {
Ok(receipt) => println!("[done] {receipt:?}"),
Err(e) => println!("[error] {e}"),
}
Ok(())
}