a3s-flow 1.1.0

Durable workflow engine and Rust SDK for A3S
Documentation
use std::sync::Arc;

use a3s_flow::{
    FlowEngine, FlowRuntime, RuntimeCommand, StepInvocation, WorkflowInvocation, WorkflowPatchId,
    WorkflowSpec,
};
use async_trait::async_trait;
use serde_json::{json, Value};

const PATCH_ID: &str = "checkout.calculation-v2";

struct CheckoutRuntime;

#[async_trait]
impl FlowRuntime for CheckoutRuntime {
    async fn run_workflow(
        &self,
        invocation: WorkflowInvocation,
    ) -> a3s_flow::Result<RuntimeCommand> {
        let context = invocation.context();
        let implementation = if context.has_patch_marker(PATCH_ID) {
            "v2"
        } else {
            "v1"
        };
        Ok(context.complete(json!({ "implementation": implementation })))
    }

    async fn run_step(&self, _invocation: StepInvocation) -> a3s_flow::Result<Value> {
        unreachable!("this workflow has no side-effecting steps")
    }
}

#[tokio::main(flavor = "current_thread")]
async fn main() -> a3s_flow::Result<()> {
    let engine = FlowEngine::in_memory(Arc::new(CheckoutRuntime));
    let base = WorkflowSpec::rust_embedded(
        "checkout.calculate",
        "2",
        "examples::replay_safe_patch",
        "main",
    );
    let patched = base
        .clone()
        .with_patch_marker(WorkflowPatchId::new(PATCH_ID)?);

    let legacy_run = engine.start(base, json!({})).await?;
    let patched_run = engine.start(patched, json!({})).await?;

    println!(
        "legacy={}",
        engine.snapshot(&legacy_run).await?.output.unwrap()
    );
    println!(
        "patched={}",
        engine.snapshot(&patched_run).await?.output.unwrap()
    );
    Ok(())
}