#![forbid(unsafe_code)]
use renkin::DEFAULT_BUILDING_BLOCKS;
use sha2::{Digest, Sha256};
use std::path::Path;
use std::process::Command;
const VERSION: &str = env!("CARGO_PKG_VERSION");
fn check(label: &str, status: &str) {
println!("{label:<24} {status}");
}
fn check_asset_hash(label: &str, local_path: &str, manifest_path: &str, asset_filename: &str) {
if !Path::new(local_path).exists() {
check(label, &format!("not found {local_path}"));
return;
}
let manifest_text = match std::fs::read_to_string(manifest_path) {
Ok(t) => t,
Err(e) => {
check(
label,
&format!("manifest unreadable {manifest_path} ({e})"),
);
return;
}
};
let manifest: serde_json::Value = match serde_json::from_str(&manifest_text) {
Ok(v) => v,
Err(e) => {
check(
label,
&format!("manifest invalid JSON {manifest_path} ({e})"),
);
return;
}
};
let Some(expected) = manifest
.get("assets")
.and_then(|a| a.get(asset_filename))
.and_then(|a| a.get("sha256"))
.and_then(|s| s.as_str())
else {
check(
label,
&format!("manifest missing assets.{asset_filename:?}.sha256 {manifest_path}"),
);
return;
};
let expected = expected.strip_prefix("sha256:").unwrap_or(expected);
let bytes = match std::fs::read(local_path) {
Ok(b) => b,
Err(e) => {
check(label, &format!("unreadable {local_path} ({e})"));
return;
}
};
let actual = renkin::sha256_hex(Sha256::digest(&bytes));
if actual == expected {
check(label, "OK (sha256 verified)");
} else {
check(
label,
&format!("MISMATCH expected {expected}, got {actual} {local_path}"),
);
}
}
fn probe_binary(name: &str) -> &'static str {
match Command::new(name).arg("--version").output() {
Ok(o) if o.status.success() => "OK",
_ => "not found",
}
}
fn probe_python() -> String {
match Command::new("python3")
.args(["-c", "import renkin; print(renkin.__version__)"])
.output()
{
Ok(o) if o.status.success() => {
let v = String::from_utf8_lossy(&o.stdout).trim().to_string();
format!("OK (v{v})")
}
_ => "not found".to_string(),
}
}
fn main() {
println!("RENKIN {VERSION}\n");
let templates_path = "data/templates_extracted_5000.smi";
if Path::new(templates_path).exists() {
let count = std::fs::read_to_string(templates_path)
.map(|s| s.lines().filter(|l| !l.trim().is_empty()).count())
.unwrap_or(0);
check(
"Templates",
&format!("OK ({count} rules) {templates_path}"),
);
} else {
check("Templates", &format!("not found {templates_path}"));
}
check_asset_hash(
"Reranker model",
"data/phase3e_reranker_training/model.txt",
"data/phase3e_reranker_training/release_asset_manifest.json",
"model.txt",
);
check_asset_hash(
"Reranker freq table",
"data/phase3e_reranker_training/frequency_table.json",
"data/phase3e_reranker_training/release_asset_manifest.json",
"frequency_table.json",
);
check_asset_hash(
"Coverage templates",
"data/phase_a5_template_scaling/templates/templates_2000.smi",
"data/phase_a5_template_scaling/templates/coverage_templates_release_asset_manifest.json",
"templates_2000.smi",
);
let bb_count = DEFAULT_BUILDING_BLOCKS.len();
check("Building blocks", &format!("OK ({bb_count})"));
check("renkin-forward", probe_binary("renkin-forward"));
check("renkin-mcp", probe_binary("renkin-mcp"));
let wasm_status = if Path::new("pkg/renkin_bg.wasm").exists() {
"OK"
} else {
"not built (run: wasm-pack build --target web --no-default-features)"
};
check("WASM package", wasm_status);
check("Python bindings", &probe_python());
}