aprender-contracts-cli 0.37.0

CLI for provable-contracts — validate, scaffold, verify, status, audit
use std::path::{Path, PathBuf};

pub fn run(contract_dir: &Path, dry_run: bool) -> Result<(), Box<dyn std::error::Error>> {
    eprintln!("pv migrate — contract schema migration");
    eprintln!("=========================================\n");

    let count = scan_and_report(contract_dir, dry_run)?;
    print_summary(contract_dir, dry_run, count);
    Ok(())
}

fn scan_and_report(contract_dir: &Path, dry_run: bool) -> std::io::Result<usize> {
    let mut count = 0;
    for entry in std::fs::read_dir(contract_dir)?.flatten() {
        let path = entry.path();
        if path.extension().and_then(|e| e.to_str()) != Some("yaml") {
            continue;
        }
        if needs_migration(&path)? {
            report_migration(&path, dry_run);
            count += 1;
        }
    }
    Ok(count)
}

fn needs_migration(path: &PathBuf) -> std::io::Result<bool> {
    let content = std::fs::read_to_string(path)?;
    Ok(!content.contains("metadata:")
        || (!content.contains("proof_obligations:") && !content.contains("registry: true")))
}

fn report_migration(path: &Path, dry_run: bool) {
    if dry_run {
        eprintln!("  [MIGRATE] {}", path.display());
    } else {
        eprintln!(
            "  [MIGRATE] {} (auto-fix not yet implemented)",
            path.display()
        );
    }
}

fn print_summary(contract_dir: &Path, dry_run: bool, count: usize) {
    if count == 0 {
        eprintln!(
            "  All contracts in {} use current schema.",
            contract_dir.display()
        );
        return;
    }
    eprintln!("\n{count} contract(s) need migration.");
    if dry_run {
        eprintln!("Run without --dry-run to apply fixes.");
    }
}