use std::path::Path;
use super::certify::analyze_config;
pub fn run(
contract_dir: &Path,
config_json: Option<&Path>,
model_file: Option<&Path>,
) -> Result<(), Box<dyn std::error::Error>> {
println!("pv verify-structure — Architecture Structure Verification");
println!("=========================================================");
println!();
let arch_path = find_contract(contract_dir, "apr-architecture-schema-v1");
let config_path = find_contract(contract_dir, "model-config-algebra-v1");
let shape_path = find_contract(contract_dir, "tensor-shape-flow-v1");
let mut found = 0;
let mut missing = Vec::new();
for (name, path) in [
("apr-architecture-schema-v1", &arch_path),
("model-config-algebra-v1", &config_path),
("tensor-shape-flow-v1", &shape_path),
] {
if let Some(p) = path {
let contract = provable_contracts::schema::parse_contract(p)?;
let eq_count = contract.equations.len();
let has_assumes = contract
.equations
.values()
.filter(|e| e.assumes.is_some())
.count();
let has_guarantees = contract
.equations
.values()
.filter(|e| e.guarantees.is_some())
.count();
println!(
" ✓ {name}: {eq_count} equations, {has_assumes} assumes, {has_guarantees} guarantees"
);
found += 1;
} else {
println!(" ✗ {name}: not found");
missing.push(name);
}
}
println!();
if let Some(cfg) = config_json {
if cfg.exists() {
analyze_config(cfg)?;
} else {
println!("Config file not found: {}", cfg.display());
}
} else {
println!("No --config provided. Use --config path/to/config.json for structural analysis.");
}
if let Some(mf) = model_file {
println!("Model file: {}", mf.display());
println!(" ⚠ Model file parsing not yet implemented (P0-4 phase 2)");
println!(" Planned: enumerate actual tensors, compare shapes to arch-schema");
}
println!();
if missing.is_empty() && found == 3 {
println!("Result: PASS — all 3 architecture contracts found with composition data");
} else {
println!(
"Result: PARTIAL — {found}/3 contracts found, {} missing",
missing.len()
);
}
Ok(())
}
fn check(label: &str, ok: bool) {
let icon = if ok { "✓" } else { "✗" };
println!(" {icon} {label}");
}
fn find_contract(dir: &Path, stem: &str) -> Option<std::path::PathBuf> {
let direct = dir.join(format!("{stem}.yaml"));
if direct.exists() {
return Some(direct);
}
let entries = std::fs::read_dir(dir).ok()?;
for entry in entries.flatten() {
let path = entry.path();
if path.is_dir() {
if let Some(found) = find_contract(&path, stem) {
return Some(found);
}
}
}
None
}