car-ffi-common 0.17.0

Shared logic for FFI bindings (NAPI, PyO3) — JSON wrappers for verify, multi-agent, scheduler
Documentation
//! JSON wrapper for `car-workflow::WorkflowEngine`.
//!
//! Lets JS / Python / WS callers run a multi-stage workflow definition
//! end-to-end. The workflow JSON shape comes from
//! [`car_workflow::Workflow`] (serde-derived); the result is
//! [`car_workflow::WorkflowResult`] serialized to JSON.
//!
//! Like `car_ffi_common::multi`, this takes an `AgentRunner` from the
//! caller — the FFI bindings supply their own (NAPI's
//! `StoredAgentRunner`, PyO3's `PyAgentRunner`) so the workflow's
//! agent stages dispatch through the registered JS/Python callback.

use car_multi::{AgentRunner, SharedInfra};
use car_workflow::{Workflow, WorkflowEngine};
use std::sync::Arc;

/// Run a Workflow JSON definition to completion. Returns
/// [`WorkflowResult`](car_workflow::WorkflowResult) as JSON.
pub async fn run_workflow(
    workflow_json: &str,
    runner: Arc<dyn AgentRunner>,
) -> Result<String, String> {
    let workflow: Workflow =
        serde_json::from_str(workflow_json).map_err(|e| format!("invalid workflow JSON: {}", e))?;
    let infra = SharedInfra::new();
    let engine = WorkflowEngine::new(runner, infra);
    let result = engine
        .run(&workflow)
        .await
        .map_err(|e| format!("workflow error: {}", e))?;
    serde_json::to_string(&result).map_err(|e| e.to_string())
}

/// Static analysis: validate a workflow definition without running it.
/// Returns the verification report as JSON.
///
/// Manually serialize the report because car_workflow::WorkflowVerifyResult
/// isn't Serde-derived (and shouldn't be — its Debug-only fields are
/// for human review, not API contract).
pub fn verify_workflow(workflow_json: &str) -> Result<String, String> {
    let workflow: Workflow =
        serde_json::from_str(workflow_json).map_err(|e| format!("invalid workflow JSON: {}", e))?;
    let report = car_workflow::verify_workflow(&workflow);
    let json = serde_json::json!({
        "valid": report.valid,
        "has_cycles": report.has_cycles,
        "reachable_stages": report.reachable_stages,
        "unreachable_stages": report.unreachable_stages,
        "issues": report.issues.iter().map(|i| format!("{:?}", i)).collect::<Vec<_>>(),
    });
    Ok(json.to_string())
}