use std::path::Path;
use crate::binding::{BindingRegistry, ImplStatus};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BindingPolicy {
AllImplemented,
WarnOnGaps,
TieredEnforcement,
}
#[derive(Debug)]
pub struct VerifyResult {
pub bound_count: usize,
pub partial_count: usize,
pub not_implemented_count: usize,
}
pub fn verify_bindings(binding_yaml_path: &str, policy: BindingPolicy) -> VerifyResult {
let path = Path::new(binding_yaml_path);
println!("cargo:rerun-if-changed={binding_yaml_path}");
if let Some(parent) = path.parent() {
if let Some(grandparent) = parent.parent() {
println!("cargo:rerun-if-changed={}", grandparent.display());
}
}
let yaml_content = std::fs::read_to_string(path).unwrap_or_else(|e| {
panic!(
"CONTRACT BUILD ERROR: Cannot read binding YAML at '{}': {e}\n\
Hint: Ensure provable-contracts is checked out as a sibling directory.",
path.display()
);
});
let registry: BindingRegistry = serde_yaml::from_str(&yaml_content).unwrap_or_else(|e| {
panic!(
"CONTRACT BUILD ERROR: Cannot parse binding YAML at '{}': {e}",
path.display()
);
});
let mut result = VerifyResult {
bound_count: 0,
partial_count: 0,
not_implemented_count: 0,
};
for binding in ®istry.bindings {
let env_key = make_env_key(&binding.contract, &binding.equation);
match binding.status {
ImplStatus::Implemented => {
println!("cargo:rustc-env={env_key}=bound");
result.bound_count += 1;
}
ImplStatus::Partial => {
result.partial_count += 1;
match policy {
BindingPolicy::AllImplemented => {
panic!(
"CONTRACT BUILD ERROR: Binding {}.{} has status 'partial'. \
Policy requires all bindings to be 'implemented'.\n\
Module: {}\n\
See: unified-contract-by-design.md §10",
binding.contract,
binding.equation,
binding.module_path.as_deref().unwrap_or("(unknown)"),
);
}
BindingPolicy::WarnOnGaps | BindingPolicy::TieredEnforcement => {
println!(
"cargo:warning=CONTRACT: partial binding {}.{} ({})",
binding.contract,
binding.equation,
binding.module_path.as_deref().unwrap_or("?"),
);
println!("cargo:rustc-env={env_key}=partial");
}
}
}
ImplStatus::NotImplemented => {
result.not_implemented_count += 1;
match policy {
BindingPolicy::AllImplemented | BindingPolicy::TieredEnforcement => {
panic!(
"CONTRACT BUILD ERROR: Binding {}.{} has status 'not_implemented'. \
All bindings must be implemented.\n\
Equation: {}\n\
Target: {}\n\
See: unified-contract-by-design.md §10",
binding.contract,
binding.equation,
binding.equation,
binding.module_path.as_deref().unwrap_or("(unassigned)"),
);
}
BindingPolicy::WarnOnGaps => {
println!(
"cargo:warning=CONTRACT: not_implemented binding {}.{} ({})",
binding.contract,
binding.equation,
binding.module_path.as_deref().unwrap_or("?"),
);
}
}
}
}
}
println!(
"cargo:warning=CONTRACT: {}/{} bindings bound ({} partial, {} not_implemented)",
result.bound_count,
registry.bindings.len(),
result.partial_count,
result.not_implemented_count,
);
result
}
fn make_env_key(contract: &str, equation: &str) -> String {
let contract_part = contract.to_uppercase().replace(['-', '.'], "_");
let equation_part = equation.to_uppercase().replace(['-', '.'], "_");
format!("CONTRACT_{contract_part}_{equation_part}")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_make_env_key_matches_macro_convention() {
assert_eq!(
make_env_key("rmsnorm-kernel-v1", "rmsnorm"),
"CONTRACT_RMSNORM_KERNEL_V1_RMSNORM"
);
assert_eq!(
make_env_key("gated-delta-net-v1", "decay"),
"CONTRACT_GATED_DELTA_NET_V1_DECAY"
);
}
#[test]
fn test_verify_result_defaults() {
let r = VerifyResult {
bound_count: 0,
partial_count: 0,
not_implemented_count: 0,
};
assert_eq!(r.bound_count, 0);
}
}