harn-cli 0.10.57

CLI for the Harn programming language — run, test, REPL, format, and lint
Documentation
use super::*;

pub(super) fn print_human_plan(plan: &RepairPlan) {
    if plan.repairs.is_empty() && plan.skipped_files.is_empty() {
        println!("{}: no repairable diagnostics found", plan.path);
        print_frozen_callables(&plan.frozen_callables);
        return;
    }
    if !plan.repairs.is_empty() {
        println!(
            "{}: {} repairable diagnostic(s)",
            plan.path,
            plan.repairs.len()
        );
        println!(
            "idx  code          safety               edits  clean  impact                    repair"
        );
        for repair in &plan.repairs {
            let clean = if repair.applies_cleanly { "yes" } else { "no" };
            println!(
                "{:<4} {:<13} {:<20} {:<5} {:<5} {:<25} {}",
                repair.diagnostic_index,
                repair.diagnostic_code,
                repair.repair.safety,
                repair.edits.len(),
                clean,
                repair.impact.classification,
                repair.repair.id
            );
            for note in &repair.impact.notes {
                println!("      note: {note}");
            }
        }
    }
    print_skipped_files(&plan.skipped_files);
    print_declared_invalid_files(&plan.declared_invalid_files);
    print_frozen_callables(&plan.frozen_callables);
}

/// Name what the migration could not re-sign.
///
/// Without this a frozen owner aborts the file's whole repair and the run
/// prints `no repairable diagnostics found` — which reads as "nothing to do"
/// rather than "blocked here" (#6153).
pub(super) fn print_frozen_callables(frozen: &[FrozenCallableWire]) {
    if frozen.is_empty() {
        return;
    }
    println!(
        "froze {} callable signature(s); capability migration cannot proceed for them:",
        frozen.len()
    );
    for callable in frozen {
        println!("  `{}`: {}", callable.name, callable.reason);
    }
}

fn print_skipped_files(skipped_files: &[SkippedFileWire]) {
    if skipped_files.is_empty() {
        return;
    }
    println!("skipped {} file(s):", skipped_files.len());
    for skipped in skipped_files {
        println!("skipped {}: {}", skipped.path, skipped.reason);
        for diagnostic in &skipped.diagnostics {
            let code = diagnostic.code.as_deref().unwrap_or("no-code");
            println!("  {}[{}]: {}", diagnostic.source, code, diagnostic.message);
            if let Some(help) = &diagnostic.help {
                println!("    help: {help}");
            }
        }
    }
}

/// Name the fixtures that were left alone because the repo declared them
/// expected-invalid.
///
/// Reported rather than dropped: a file the codemod did not visit should never
/// be invisible, or "the suite was clean" and "the suite was never read" look
/// the same from the outside (harn#6264).
fn print_declared_invalid_files(declared: &[SkippedFileWire]) {
    if declared.is_empty() {
        return;
    }
    println!(
        "left {} declared-invalid fixture(s) untouched (sibling `.error` file):",
        declared.len()
    );
    for file in declared {
        println!("  {}", file.path);
    }
}

pub(super) fn print_apply_result(result: &ApplyResult) {
    let verb = if result.dry_run {
        "would apply"
    } else {
        "applied"
    };
    println!(
        "{verb} {} repair(s), skipped {}; post-apply diagnostics: {}",
        result.applied.len(),
        result.skipped.len(),
        result.post_apply_diagnostics_count
    );
    for skipped in &result.skipped {
        println!(
            "skipped {} {} in {}: {}",
            skipped.diagnostic_code, skipped.repair_id, skipped.path, skipped.reason
        );
    }
    print_skipped_files(&result.skipped_files);
    print_declared_invalid_files(&result.declared_invalid_files);
    print_frozen_callables(&result.frozen_callables);
}

pub(super) fn skipped_files_error(count: usize) -> String {
    format!("harn fix skipped {count} file(s) due to read, lex, or parse errors")
}