Free Functions
All user-facing workflow operations are exposed as free functions under the zart:: namespace. There is no ctx to thread through your code — the framework uses task-local storage to make the current execution context available wherever you are in the call stack.
Step Execution
Section titled “Step Execution”zart::require
Section titled “zart::require”Execute a step and fail the execution on any non-Ok outcome.
pub async fn require<S>(s: S) -> Result<S::Output, TaskError>where S: ZartStep + Send, S::Error: std::error::Error + Send + Sync + 'static,This is the default path — use it when a step’s failure means the whole workflow cannot proceed.
let inventory = reserve_inventory(order_id.clone()).await?;Both BusinessErr and ZartErr result in TaskError::StepFailed, preserving the step name for on_failure inspection.
zart::step
Section titled “zart::step”Execute a step and return the three-way StepOutcome.
pub async fn step<S: ZartStep + Send>( s: S,) -> Result<StepOutcome<S::Output, S::Error>, StepError>Use this when the handler body genuinely needs to branch on a specific step’s outcome.
match zart::step(check_balance(account_id.clone())).await? { StepOutcome::Ok(b) => { /* ... */ } StepOutcome::BusinessErr(PaymentError::InsufficientFunds { balance, needed }) => { /* ... */ } StepOutcome::BusinessErr(PaymentError::CardDeclined { reason }) => { /* ... */ } StepOutcome::ZartErr(e) => return Err(e.into()),}The outer ? propagates control-flow signals (Scheduled, StepExecuted) — never matched by user code.
zart::step_or
Section titled “zart::step_or”Execute a step, returning default on any failure (business or framework).
pub async fn step_or<S: ZartStep + Send>( s: S, default: S::Output,) -> Result<S::Output, StepError>A blind fallback — the error is discarded. Use only when the specific failure reason does not matter.
let cached = zart::step_or(fetch_remote_config(), Config::default()).await?;zart::step_or_else
Section titled “zart::step_or_else”Execute a step, computing a fallback on business error only.
pub async fn step_or_else<S, F>( s: S, f: F,) -> Result<S::Output, StepError>where S: ZartStep + Send, F: FnOnce(S::Error) -> S::Output,The closure receives S::Error only — ZartStepError (retry exhausted, timeout) is not passed to f and still propagates as a framework error. This is intentional: framework-level failures are not business decisions and should not be silently swallowed.
let payment = zart::step_or_else( charge_card(account_id.clone(), data.amount), |e: PaymentError| { PaymentResult { transaction_id: "fallback".into(), amount: 0.0 } },).await?;Parallel Execution
Section titled “Parallel Execution”zart::schedule
Section titled “zart::schedule”Register a step for parallel execution without waiting for it to complete.
pub fn schedule<S: ZartStep + Send + 'static>( s: S,) -> StepHandle<S::Output>The returned handle can be passed to zart::wait to collect results.
let h1 = zart::schedule(check_service("auth-api".into()));let h2 = zart::schedule(check_service("payments".into()));let h3 = zart::schedule(check_service("users-db".into()));zart::wait
Section titled “zart::wait”Wait for all handles returned by zart::schedule to complete.
pub async fn wait<T>( handles: Vec<StepHandle<T>>,) -> Result<Vec<Result<T, StepError>>, StepError>where T: Serialize + for<'de> Deserialize<'de>,Returns Ok(results) where each element corresponds to one handle in order. An individual step failure appears as Err(StepError) inside the Vec.
let results = zart::wait(vec![h1, h2, h3]).await?;for result in results { let svc = result?; // fail-fast on any step failure println!("{}: {}", svc.name, svc.status);}Time and Events
Section titled “Time and Events”zart::sleep
Section titled “zart::sleep”Suspend execution for duration, resuming at now + duration.
pub async fn sleep( name: &str, duration: Duration,) -> Result<(), StepError>The name must be a stable, unique string within this execution body. Treat it like a migration name — do not change it after the execution has started.
zart::sleep("warehouse-settle", Duration::from_secs(60)).await?;zart::sleep_until
Section titled “zart::sleep_until”Suspend execution until an absolute UTC timestamp.
pub async fn sleep_until( name: &str, wake_time: chrono::DateTime<chrono::Utc>,) -> Result<(), StepError>let next_monday = compute_next_monday();zart::sleep_until("wait-for-monday", next_monday).await?;zart::wait_for_event
Section titled “zart::wait_for_event”Wait for an external event to be delivered to this execution.
pub async fn wait_for_event<T: DeserializeOwned>( name: &str, timeout: Option<Duration>,) -> Result<T, StepError>let decision: ApprovalDecision = zart::wait_for_event( "manager-approval", Some(Duration::from_secs(86400)),).await?;See Steps → wait_for_event for the full walkthrough.
Durable Values
Section titled “Durable Values”zart::capture
Section titled “zart::capture”Capture a synchronous, pure value durably. On first body run: evaluates f(), writes the result as a completed step row, returns the value. On replay: returns the cached DB value; f is never called.
pub async fn capture<T, F>( name: &str, f: F,) -> Result<T, StepError>where T: Serialize + for<'de> Deserialize<'de>, F: FnOnce() -> T,let started_at = zart::capture!("started-at", chrono::Utc::now());// On replay: returns the cached DateTime — Utc::now() is never called again.The macro form zart_capture! (re-exported as zart::capture!) expands to ::zart::capture(name, || expr).await?.
zart::now
Section titled “zart::now”Shorthand for capture(name, chrono::Utc::now).
pub async fn now( name: &str,) -> Result<chrono::DateTime<chrono::Utc>, StepError>let created_at = zart::now("created-at").await?;Introspection
Section titled “Introspection”zart::context
Section titled “zart::context”Returns read-only information about the current execution. Callable from anywhere — handler body or step body.
pub fn context() -> ExecutionInfolet info = zart::context();println!("Execution: {}", info.execution_id);println!("Task: {}", info.task_name);println!("Attempt: {}", info.current_attempt);println!("Is retry: {}", info.is_retry());| Field | Type | Description |
|---|---|---|
execution_id | String | Unique identifier of this execution |
task_name | String | Registered name of the handler |
data | serde_json::Value | The original input payload (read-only) |
current_attempt | usize | 0-indexed retry count |
max_retries | Option<usize> | Maximum configured retries |
is_retry() | bool | current_attempt > 0 |
Next Steps
Section titled “Next Steps”- Macros —
#[zart_durable]and#[zart_step] - Execution Management —
DurableScheduler - Error Types —
StepOutcome,TaskError,ExecutionFailure