a3s-flow 0.4.1

Durable workflow engine and Rust SDK for A3S
Documentation
#[cfg(feature = "sqlite")]
use a3s_flow::{
    FlowEngine, FlowError, FlowRuntime, RuntimeCommand, SqliteEventStore, StepInvocation,
    WorkflowInvocation, WorkflowSpec,
};
#[cfg(feature = "sqlite")]
use async_trait::async_trait;
#[cfg(feature = "sqlite")]
use serde_json::json;
#[cfg(feature = "sqlite")]
use std::sync::Arc;

#[cfg(feature = "sqlite")]
struct GreetingRuntime;

#[cfg(feature = "sqlite")]
#[async_trait]
impl FlowRuntime for GreetingRuntime {
    async fn run_workflow(
        &self,
        invocation: WorkflowInvocation,
    ) -> a3s_flow::Result<RuntimeCommand> {
        let ctx = invocation.context();
        if let Some(output) = ctx.step_output("greet") {
            return Ok(ctx.complete(output.clone()));
        }

        Ok(ctx.schedule_step(
            "greet",
            "greet_user",
            json!({ "name": ctx.input()["name"] }),
        ))
    }

    async fn run_step(&self, invocation: StepInvocation) -> a3s_flow::Result<serde_json::Value> {
        match invocation.step_name.as_str() {
            "greet_user" => Ok(json!({
                "message": format!("hello {}", invocation.input["name"].as_str().unwrap_or("unknown")),
            })),
            step => Err(FlowError::Runtime(format!("unknown step {step}"))),
        }
    }
}

#[cfg(feature = "sqlite")]
#[tokio::main(flavor = "current_thread")]
async fn main() -> a3s_flow::Result<()> {
    let db_path =
        std::env::temp_dir().join(format!("a3s-flow-sqlite-example-{}.db", std::process::id()));
    let _ = tokio::fs::remove_file(&db_path).await;
    let url = format!("sqlite://{}", db_path.display());

    let spec = WorkflowSpec::rust_embedded("examples.sqlite", "0.1.0", "examples", "main");
    {
        let store = Arc::new(SqliteEventStore::connect(&url).await?);
        let engine = FlowEngine::new(store, Arc::new(GreetingRuntime));
        engine
            .start_with_id(
                "sqlite-greeting-ada",
                spec.clone(),
                json!({ "name": "Ada" }),
            )
            .await?;
    }

    let store = Arc::new(SqliteEventStore::connect(&url).await?);
    let engine = FlowEngine::new(store, Arc::new(GreetingRuntime));
    let snapshot = engine.snapshot("sqlite-greeting-ada").await?;

    println!("run_id={}", snapshot.run_id);
    println!("status={:?}", snapshot.status);
    println!(
        "output={}",
        serde_json::to_string_pretty(&snapshot.output).unwrap()
    );

    let _ = tokio::fs::remove_file(&db_path).await;
    Ok(())
}

#[cfg(not(feature = "sqlite"))]
fn main() {
    println!("sqlite feature not enabled; run with:");
    println!("cargo run --example sqlite_durability --features sqlite");
}