behavior-contracts 0.2.1

Language-neutral IR runtime core (expression evaluation, template rendering, execution plan, canonical serialization) shared across DSL implementations. Passes the dsl-contracts conformance vectors byte-for-byte.
Documentation
// GENERATED by behavior-contracts common generator (bc#13) — DO NOT EDIT.
// Input: portable component-graph IR only (scp-ir-architecture.md §5/§7.4).
// This module is the codegen endpoint (§7.3 endpoint 3): the IR is embedded as
// NATIVE json! constructor code (compile-time checked; no JSON-text parsing at
// runtime) and handed to the EXISTING runtime core (run_behavior) — no execution
// logic is generated. Handlers are ALWAYS injected at the boundary
// (IR + {effects,config,hooks} — concept.md §4.4); they are never generated.
//
// Const deviation (documented): serde_json::Value is not const-constructible, so
// the IR is built ONCE on first use behind std::sync::LazyLock (requires Rust
// >= 1.80). The first-use fail-closed checks (#208 prepared-artifact discipline)
// run inside the initializer: a spec-version skew or fingerprint mismatch panics
// LOUDLY before any behavior can run.
// irFingerprint: fnv1a64:5ee5238dffaaf12a

use behavior_contracts::{
    fingerprint_component_graph, run_behavior, BehaviorError, ComponentExec, SpecVersions, Value,
};
use serde_json::{json, Value as J};
use std::sync::LazyLock;

/// Spec versions baked at generation time (fail-closed comparison on first use).
pub const EXPECTED_SPEC_VERSIONS: [(&str, i64); 3] =
    [("behavior", 2), ("expression", 2), ("plan", 1)];

/// FNV-1a 64 fingerprint of the source portable IR (canonical_json discipline, #208).
pub const IR_FINGERPRINT: &str = "fnv1a64:5ee5238dffaaf12a";

/// Component names exposed by bind(), in IR declaration order.
pub const COMPONENT_NAMES: [&str; 1] = ["boom"];

/// The portable component-graph IR, built once on first use from native json!
/// constructor code (no JSON-text parsing at runtime; see the const-deviation
/// note in the module header).
pub static IR: LazyLock<J> = LazyLock::new(|| {
    let ir = json!({
        "irVersion": 1,
        "exprVersion": 2,
        "components": [
            {
                "name": "boom",
                "inputPorts": {},
                "body": [
                    {
                        "id": "a",
                        "component": "GetItem",
                        "ports": {
                            "PK": "X"
                        }
                    }
                ],
                "output": {
                    "ref": [
                        "a"
                    ]
                }
            }
        ]
    });
    check_generated_module(&ir);
    ir
});

fn runtime_spec_version(key: &str) -> i64 {
    match key {
        "behavior" => SpecVersions::BEHAVIOR,
        "expression" => SpecVersions::EXPRESSION,
        "plan" => SpecVersions::PLAN,
        other => {
            panic!("behavior-contracts generated module: unknown spec key '{other}' (fail-closed)")
        }
    }
}

// First-use fail-closed checks (#208 prepared-artifact discipline).
fn check_generated_module(ir: &J) {
    for (key, want) in EXPECTED_SPEC_VERSIONS {
        let got = runtime_spec_version(key);
        if got != want {
            panic!("behavior-contracts generated module: spec-version skew for '{key}' (generated={want}, runtime={got}) — regenerate against this runtime (fail-closed)");
        }
    }
    for name in COMPONENT_NAMES {
        let found = ir["components"]
            .as_array()
            .is_some_and(|cs| cs.iter().any(|c| c["name"] == name));
        if !found {
            panic!("behavior-contracts generated module: component '{name}' missing from the embedded IR — the generated code was modified or corrupted (fail-closed)");
        }
    }
    let got_fp = fingerprint_component_graph(ir).unwrap_or_else(|e| {
        panic!(
            "behavior-contracts generated module: fingerprint recompute failed: {e} (fail-closed)"
        )
    });
    if got_fp != IR_FINGERPRINT {
        panic!("behavior-contracts generated module: IR fingerprint mismatch (baked={IR_FINGERPRINT}, embedded literal={got_fp}) — the generated code was modified or corrupted (fail-closed)");
    }
}

/// Injected-handler accessor over the embedded IR (see [`bind`]). Rust ownership
/// makes a map of per-component closures sharing one `&mut handlers` impractical,
/// so the per-component executor surface is `call(name, input)` with the
/// component name checked against [`COMPONENT_NAMES`] by the runtime core.
pub struct Bound<H: ComponentExec> {
    handlers: H,
}

/// bind — inject handlers (boundary injection; catalog name → implementation)
/// and get an executor over the embedded IR. Each `call` delegates to the
/// runtime core's run_behavior (dynamic-path equivalent — §7.3). Forces the
/// first-use fail-closed checks.
pub fn bind<H: ComponentExec>(handlers: H) -> Bound<H> {
    LazyLock::force(&IR);
    Bound { handlers }
}

impl<H: ComponentExec> Bound<H> {
    pub fn call(&mut self, name: &str, input: &[(String, Value)]) -> Result<Value, BehaviorError> {
        run_behavior(&IR, &mut self.handlers, input, Some(name))
    }
}