ironsaga
A Rust crate for building command pipelines with automatic compensation (rollback) support — for both sync and async workflows.
You define plain functions, #[ironcmd] turns them into commands, and IronSagaSync / IronSagaAsync orchestrates their execution and rollback.
Why ironsaga?
When you have a sequence of operations that must either all succeed or all undo themselves, manually wiring rollback logic is tedious and error-prone. ironsaga gives you:
- ✅ Declarative command definition via
#[ironcmd] - ✅ Automatic LIFO rollback on failure
- ✅ Recursive compensation chains
- ✅ Shared typed context across commands
check out examples folder for sync/async full examples.
Quick Start
[]
= "0.2"
Defining Commands
use ironcmd;
let mut cmd = new;
cmd.execute.unwrap;
assert_eq!;
The macro generates a Greet struct with fname, lname fields, implementing SyncCommand.
Macro Attributes
| Attribute | Effect |
|---|---|
result |
if your function returns a result (failable) then its better to annotate it , this helps the macro to extend the execute trait implemetation so that it can run rollback |
rename = "CustomStructName" |
Override the default PascalCase generated struct name |
recursive_rollback |
On rollback failure, recursively tries rollback_cmd.rollback() |
Shared Context
Pass an Rc<RefCell<YourContext>> for sync/ Arc<Mutex> for sync, to share state and collect results across commands:
Rollback & Compensation
Rollbacks are also commands — inject them with set_rollback:
let mut create = new;
create.set_rollback;
If the pipeline fails at step N, all previous commands roll back in reverse order. If a rollback itself fails and recursive_rollback is set, it tries rollback_cmd.rollback() recursively until the chain is exhausted.
Sync Pipeline
let mut saga = default;
saga.add_command;
saga.add_command;
saga.add_command; // 💥 fails → charge and create roll back
assert!;
assert_eq!;
assert_eq!;
Async Pipeline
IronSagaAsync accepts only async commands, because of design limitation to make it Send:
let mut bus = default;
let mut user_insertion = new;
user_insertion.set_rollback;
bus.add_command; // async
bus.add_command; // async
bus.add_command;
assert!;
assert!; // rollback ran ✅
Full Example
use ;
use ;
// ── Shared Context ────────────────────────────────────────────────────────────
// ── Step 1: Create Order ──────────────────────────────────────────────────────
// ── Step 2: Charge Payment ────────────────────────────────────────────────────
// ── Step 3: Schedule Shipment — always FAILS ──────────────────────────────────
// ── Runner ────────────────────────────────────────────────────────────────────