use behavior_contracts::{
fingerprint_component_graph, run_behavior, BehaviorError, ComponentExec, SpecVersions, Value,
};
use serde_json::{json, Value as J};
use std::sync::LazyLock;
pub const EXPECTED_SPEC_VERSIONS: [(&str, i64); 3] =
[("behavior", 2), ("expression", 2), ("plan", 1)];
pub const IR_FINGERPRINT: &str = "fnv1a64:8fefaaddaea8f34c";
pub const COMPONENT_NAMES: [&str; 1] = ["getUserGroupsWithPerms"];
pub static IR: LazyLock<J> = LazyLock::new(|| {
let ir = json!({
"irVersion": 1,
"exprVersion": 2,
"components": [
{
"name": "getUserGroupsWithPerms",
"inputPorts": {
"userId": {
"type": "string",
"required": true
}
},
"body": [
{
"id": "a",
"component": "GetItem",
"ports": {
"PK": {
"concat": [
"USER#",
{
"ref": [
"userId"
]
}
]
}
}
},
{
"id": "m1",
"map": {
"over": {
"ref": [
"a",
"items"
]
},
"as": "$i",
"component": "GetGroup",
"parent": "a",
"into": "group",
"ports": {
"PK": {
"concat": [
"GROUP#",
{
"ref": [
"$i",
"gid"
]
}
]
}
}
}
},
{
"id": "m2",
"map": {
"over": {
"ref": [
"m1"
]
},
"as": "$g",
"component": "GetPerms",
"parent": "m1",
"into": "perms",
"ports": {
"PK": {
"concat": [
"PERM#",
{
"ref": [
"$g",
"group",
"permRef"
]
}
]
}
}
}
}
],
"output": {
"ref": [
"m2"
]
},
"plan": {
"groups": [
[
0
],
[
1
],
[
2
]
],
"concurrency": 16
}
}
]
});
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)")
}
}
}
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)");
}
}
pub struct Bound<H: ComponentExec> {
handlers: H,
}
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))
}
}