use cviz::model::ExportInfo;
use cviz::parse::component::parse_component;
use cviz::parse::json::parse_json_str;
use splicer::lowlevel::{generate_wac, parse_yaml, validate_contract, Injection};
use splicer::types::ContractResult;
use std::collections::{BTreeMap, HashMap};
const JSON_LOG_SHORT: &str = r#"
{
"version": 1,
"nodes": [
{ "id": 11, "name": "log-provider", "component_index": 0, "component_num": 0, "imports": [] },
{
"id": 13, "name": "app", "component_index": 1, "component_num": 1,
"imports": [
{ "interface": "wasi:logging/log@0.1.0", "short": "log",
"source_instance": 11, "is_host_import": false }
]
}
],
"exports": [{ "interface": "wasi:logging/log@0.1.0", "source_instance": 13 }]
}
"#;
const JSON_LOG_LONG: &str = r#"
{
"version": 1,
"nodes": [
{ "id": 11, "name": "log-provider-inner", "component_index": 0, "component_num": 0, "imports": [] },
{
"id": 12, "name": "log-provider", "component_index": 1, "component_num": 1,
"imports": [
{ "interface": "wasi:logging/log@0.1.0", "short": "log",
"source_instance": 11, "is_host_import": false }
]
},
{
"id": 13, "name": "app", "component_index": 2, "component_num": 2,
"imports": [
{ "interface": "wasi:logging/log@0.1.0", "short": "log",
"source_instance": 12, "is_host_import": false }
]
}
],
"exports": [{ "interface": "wasi:logging/log@0.1.0", "source_instance": 13 }]
}
"#;
const LOG_IFACE: &str = "wasi:logging/log@0.1.0";
pub fn scenario_1a_before_on_short_chain() -> String {
let yaml = r#"
version: 1
rules:
- before:
interface: wasi:logging/log@0.1.0
provider:
name: log-provider
inject:
- name: mw-a
"#;
let cfg = parse_yaml(yaml).unwrap();
let graph = parse_json_str(JSON_LOG_SHORT).unwrap();
generate_wac(
HashMap::new(),
"placeholder",
&graph,
&cfg,
None,
"example:composition",
)
.unwrap()
.wac
}
pub fn scenario_1b_before_on_long_chain() -> String {
let yaml = r#"
version: 1
rules:
- before:
interface: wasi:logging/log@0.1.0
provider:
name: log-provider-inner
inject:
- name: mw-a
"#;
let cfg = parse_yaml(yaml).unwrap();
let graph = parse_json_str(JSON_LOG_LONG).unwrap();
generate_wac(
HashMap::new(),
"placeholder",
&graph,
&cfg,
None,
"example:composition",
)
.unwrap()
.wac
}
pub fn scenario_1c_between_on_long_chain() -> String {
let yaml = r#"
version: 1
rules:
- between:
interface: wasi:logging/log@0.1.0
inner:
name: log-provider-inner
outer:
name: log-provider
inject:
- name: mw-a
- name: mw-b
"#;
let cfg = parse_yaml(yaml).unwrap();
let graph = parse_json_str(JSON_LOG_LONG).unwrap();
generate_wac(
HashMap::new(),
"placeholder",
&graph,
&cfg,
None,
"example:composition",
)
.unwrap()
.wac
}
const CHAIN_FP: &str = "sha256-abc123-fake-fingerprint";
fn injection(name: &str) -> Injection {
Injection::from_name(name)
}
fn cache_with_fp(mw: &str, fp: &str) -> HashMap<String, BTreeMap<String, ExportInfo>> {
let mut exports = BTreeMap::new();
exports.insert(
LOG_IFACE.to_string(),
ExportInfo {
source_instance: 0,
fingerprint: Some(fp.to_string()),
ty: None,
},
);
let mut cache = HashMap::new();
cache.insert(mw.to_string(), exports);
cache
}
pub fn scenario_2a_warn_no_path() -> Vec<ContractResult> {
let mut cache = HashMap::new();
validate_contract(
&[injection("mw")],
LOG_IFACE,
&Some(CHAIN_FP.to_string()),
&mut cache,
)
}
pub fn scenario_2b_ok_compatible() -> Vec<ContractResult> {
let mut cache = cache_with_fp("mw", CHAIN_FP);
validate_contract(
&[injection("mw")],
LOG_IFACE,
&Some(CHAIN_FP.to_string()),
&mut cache,
)
}
pub fn scenario_2c_error_incompatible() -> Vec<ContractResult> {
let mut cache = cache_with_fp("mw", "sha256-zzz999-different-type");
validate_contract(
&[injection("mw")],
LOG_IFACE,
&Some(CHAIN_FP.to_string()),
&mut cache,
)
}
const CHAIN_WAT: &str = r#"(component
(import "wasi:logging/log@0.1.0" (instance $host
(export "log" (func (param "level" u32) (param "message" string)))
))
(alias export $host "log" (func $f))
(instance $out (export "log" (func $f)))
(export "wasi:logging/log@0.1.0" (instance $out))
)"#;
fn chain_log_fingerprint() -> Option<String> {
let bytes = wat::parse_str(CHAIN_WAT).expect("compile chain WAT");
let graph = parse_component(&bytes).expect("parse chain component");
graph
.component_exports
.get(LOG_IFACE)
.and_then(|e| e.fingerprint.clone())
}
fn run_type_check_full(mw_wat: &str, temp_name: &str) -> Vec<ContractResult> {
let chain_fp = chain_log_fingerprint();
let mw_bytes = wat::parse_str(mw_wat).expect("compile middleware WAT");
let tmp_path = std::env::temp_dir().join(temp_name);
std::fs::write(&tmp_path, &mw_bytes).expect("write temp wasm");
let inj = Injection::from_path("mw", tmp_path.to_str().unwrap());
let mut cache = HashMap::new();
validate_contract(&[inj], LOG_IFACE, &chain_fp, &mut cache)
}
pub fn scenario_3a_ok_compatible_wat() -> Vec<ContractResult> {
let mw_wat = include_str!("../demo/wat/log-middleware-compatible.wat");
run_type_check_full(mw_wat, "splicer-demo-mw-compatible.wasm")
}
pub fn scenario_3b_error_incompatible_wat() -> Vec<ContractResult> {
let mw_wat = include_str!("../demo/wat/log-middleware-incompatible.wat");
run_type_check_full(mw_wat, "splicer-demo-mw-incompatible.wasm")
}
fn header(title: &str) {
let bar = "═".repeat(64);
println!("\n{bar}");
println!(" {title}");
println!("{bar}");
}
fn subheader(label: &str) {
println!("\n ── {label} ──");
}
fn show_contract_result(result: &ContractResult) {
match result {
ContractResult::Ok => println!(" ✔ Ok — types are compatible, injection confirmed safe"),
ContractResult::Warn(msg) => println!(" ⚠ Warn — {msg}"),
ContractResult::Error(msg) => println!(" ✘ Error — {msg}"),
ContractResult::Tier1Compatible(ifaces) => {
println!(" ↪ Tier1Compatible — middleware is type-erased; adapter component will be generated (hooks: {ifaces:?})")
}
ContractResult::Tier2Compatible(ifaces) => {
println!(" ↪ Tier2Compatible — middleware observes lifted values; adapter component will be generated (hooks: {ifaces:?})")
}
}
}
fn main() {
header("Phase 1 — Basic Splice (no type checking)");
subheader("1a · before rule on a two-node log chain");
println!("{}", scenario_1a_before_on_short_chain());
subheader("1b · before rule on a three-node log chain (targets innermost provider)");
println!("{}", scenario_1b_before_on_long_chain());
subheader("1c · between rule on a three-node log chain (two middlewares)");
println!("{}", scenario_1c_between_on_long_chain());
header("Phase 2 — Type-Compatibility Checking");
println!();
println!(" Chain fingerprint: {CHAIN_FP}");
subheader("2a · Warn — middleware has no path; cannot validate type safety");
for r in scenario_2a_warn_no_path() {
show_contract_result(&r);
}
subheader("2b · Ok — middleware exports the interface with matching fingerprint");
for r in scenario_2b_ok_compatible() {
show_contract_result(&r);
}
subheader("2c · Error — middleware exports the interface with a DIFFERENT fingerprint");
for r in scenario_2c_error_incompatible() {
show_contract_result(&r);
}
header("Phase 3 — Full Pipeline with Real WAT");
println!();
println!(" Interface : {LOG_IFACE}");
println!(" Chain WAT : log(level: u32, message: string)");
subheader("3a · Ok — compatible WAT middleware (same log signature)");
for r in scenario_3a_ok_compatible_wat() {
show_contract_result(&r);
}
subheader("3b · Error — incompatible WAT middleware (level: string vs u32)");
for r in scenario_3b_error_incompatible_wat() {
show_contract_result(&r);
}
let bar = "═".repeat(64);
println!("\n{bar}");
}
#[test]
fn test_1a_middleware_wraps_log_provider() {
let wac = scenario_1a_before_on_short_chain();
assert!(
wac.contains("let mw-a = new my:mw-a {"),
"mw-a should be instantiated"
);
assert!(
wac.contains(&format!(r#""{LOG_IFACE}": log-provider["{LOG_IFACE}"]"#)),
"mw-a should be wired from log-provider"
);
assert!(
wac.contains(&format!(r#""{LOG_IFACE}": mw-a["{LOG_IFACE}"]"#)),
"app should receive log through mw-a"
);
}
#[test]
fn test_1b_middleware_inserted_before_innermost() {
let wac = scenario_1b_before_on_long_chain();
assert!(
wac.contains("let mw-a = new my:mw-a {"),
"mw-a should be instantiated"
);
assert!(
wac.contains(&format!(
r#""{LOG_IFACE}": log-provider-inner["{LOG_IFACE}"]"#
)),
"mw-a should be wired from log-provider-inner"
);
assert!(
wac.contains(&format!(r#""{LOG_IFACE}": mw-a["{LOG_IFACE}"]"#)),
"log-provider should receive log through mw-a"
);
}
#[test]
fn test_1c_two_middlewares_between_nodes() {
let wac = scenario_1c_between_on_long_chain();
assert!(
wac.contains("let mw-a = new my:mw-a {"),
"mw-a should be instantiated"
);
assert!(
wac.contains("let mw-b = new my:mw-b {"),
"mw-b should be instantiated"
);
let mw_a_line = format!(r#""{LOG_IFACE}": mw-a["{LOG_IFACE}"]"#);
let mw_b_line = format!(r#""{LOG_IFACE}": mw-b["{LOG_IFACE}"]"#);
assert!(wac.contains(&mw_a_line), "mw-a should be in the chain");
assert!(wac.contains(&mw_b_line), "mw-b should be in the chain");
assert!(wac.contains("let log-provider-inner = new my:log-provider-inner {"));
}
#[test]
fn test_2a_produces_warn() {
let results = scenario_2a_warn_no_path();
assert_eq!(results.len(), 1);
assert!(
matches!(results[0], ContractResult::Warn(_)),
"expected Warn, got {:?}",
results[0]
);
}
#[test]
fn test_2b_produces_ok() {
let results = scenario_2b_ok_compatible();
assert_eq!(results.len(), 1);
assert_eq!(results[0], ContractResult::Ok);
}
#[test]
fn test_2c_produces_error() {
let results = scenario_2c_error_incompatible();
assert_eq!(results.len(), 1);
assert!(
matches!(results[0], ContractResult::Error(_)),
"expected Error, got {:?}",
results[0]
);
}
#[test]
fn test_3a_compatible_wat_produces_ok() {
let results = scenario_3a_ok_compatible_wat();
assert_eq!(results.len(), 1);
assert_eq!(
results[0],
ContractResult::Ok,
"compatible WAT middleware should produce Ok"
);
}
#[test]
fn test_3b_incompatible_wat_produces_error() {
let results = scenario_3b_error_incompatible_wat();
assert_eq!(results.len(), 1);
assert!(
matches!(results[0], ContractResult::Error(_)),
"incompatible WAT middleware should produce Error, got {:?}",
results[0]
);
}