aprender_contracts_cli/commands/
proof_status.rs1use std::path::Path;
2
3use provable_contracts::binding::parse_binding;
4use provable_contracts::obligation_matrix::{format_obligation_table, obligation_matrix};
5use provable_contracts::proof_status::{format_text, proof_status_report};
6use provable_contracts::schema::{parse_contract, ContractKind};
7
8use crate::contract_walk::collect_contracts;
9
10pub fn run(
11 path: &Path,
12 binding_path: Option<&Path>,
13 verify_root: Option<&Path>,
14 format: &str,
15 table: bool,
16 kind_filter: Option<&str>,
17) -> Result<(), Box<dyn std::error::Error>> {
18 let binding = match binding_path {
19 Some(bp) => Some(match verify_root {
23 Some(root) => parse_binding(bp)?.verified(root),
24 None => parse_binding(bp)?,
25 }),
26 None => None,
27 };
28
29 let kind = kind_filter.map(parse_kind).transpose()?;
30
31 let mut contracts = Vec::new();
33 if path.is_dir() {
34 collect_contracts(path, &mut contracts);
35 } else {
36 let stem = path
37 .file_stem()
38 .and_then(|s| s.to_str())
39 .unwrap_or("unknown")
40 .to_string();
41 let c = parse_contract(path)?;
42 contracts.push((stem, c));
43 }
44
45 if let Some(k) = kind {
46 contracts.retain(|(_, c)| c.kind() == k);
47 }
48
49 contracts.sort_by(|a, b| a.0.cmp(&b.0));
50
51 let refs: Vec<(String, &provable_contracts::schema::Contract)> =
52 contracts.iter().map(|(s, c)| (s.clone(), c)).collect();
53
54 let include_classes = contracts.len() > 1;
55 let report = proof_status_report(&refs, binding.as_ref(), include_classes);
56
57 if format == "json" {
58 let json = serde_json::to_string_pretty(&report)?;
59 println!("{json}");
60 } else {
61 print!("{}", format_text(&report));
62 if contracts.len() > 1 {
64 print_kind_breakdown(&contracts);
65 }
66 }
67
68 if table {
69 let matrices = obligation_matrix(&refs);
70 print!("{}", format_obligation_table(&matrices));
71 }
72
73 Ok(())
74}
75
76fn print_kind_breakdown(contracts: &[(String, provable_contracts::schema::Contract)]) {
77 let mut counts = std::collections::BTreeMap::<ContractKind, usize>::new();
78 for (_, c) in contracts {
79 *counts.entry(c.kind()).or_insert(0) += 1;
80 }
81 if counts.len() < 2 {
83 return;
84 }
85 println!();
86 print!("By kind:");
87 for (kind, count) in &counts {
88 print!(" {kind}={count}");
89 }
90 println!();
91}
92
93fn parse_kind(s: &str) -> Result<ContractKind, Box<dyn std::error::Error>> {
94 match s.to_lowercase().as_str() {
95 "kernel" => Ok(ContractKind::Kernel),
96 "registry" => Ok(ContractKind::Registry),
97 "model-family" | "modelfamily" => Ok(ContractKind::ModelFamily),
98 "pattern" => Ok(ContractKind::Pattern),
99 "schema" => Ok(ContractKind::Schema),
100 other => Err(format!(
101 "invalid --kind value '{other}': expected one of \
102 kernel, registry, model-family, pattern, schema"
103 )
104 .into()),
105 }
106}