cargo-reclaim 0.2.2

Safe Cargo cleanup for target directories, stale artifacts, and Cargo home caches
Documentation
use std::io::Write;

use cargo_reclaim::{
    CargoHomeApplyEntryStatus, CargoHomeApplyReport, CargoHomePlan, CargoHomeReport,
};

use super::super::CliError;
use super::labels::{
    action_label, class_label, display_path, display_text, path_kind_label, policy_label,
    source_label,
};

pub(super) fn write_terminal_report(
    output: &mut impl Write,
    report: &CargoHomeReport,
) -> Result<(), CliError> {
    writeln!(output, "cargo-reclaim cargo-home report")?;
    writeln!(
        output,
        "dry-run/read-only; no files were deleted or modified"
    )?;
    writeln!(
        output,
        "human-readable text; use --json for a stable structured document"
    )?;
    writeln!(output, "root: {}", display_path(&report.input.root))?;
    writeln!(output, "source: {}", source_label(report.input.source))?;
    writeln!(output, "entries: {}", report.totals.entry_count)?;
    writeln!(
        output,
        "known cache entries: {}",
        report.totals.known_cache_entry_count
    )?;
    writeln!(output, "known cache bytes: {}", report.totals.cache_bytes)?;
    writeln!(output, "total bytes: {}", report.totals.total_bytes)?;
    writeln!(output, "preserved bytes: {}", report.totals.preserved_bytes)?;
    writeln!(
        output,
        "skipped/problem entries: {}",
        report.totals.skipped_count
    )?;
    writeln!(output, "problems: {}", report.totals.problem_count)?;

    if !report.recommendations.is_empty() {
        writeln!(output)?;
        for recommendation in &report.recommendations {
            writeln!(
                output,
                "recommendation: {}",
                display_text(&recommendation.message)
            )?;
        }
    }

    if report.entries.is_empty() {
        writeln!(output)?;
        writeln!(output, "no Cargo home reportable entries found")?;
        return Ok(());
    }

    writeln!(output)?;
    for entry in &report.entries {
        writeln!(
            output,
            "{}\t{}\t{}\t{}\t{}",
            class_label(entry.class),
            path_kind_label(entry.path_kind),
            entry.size_bytes,
            display_path(&entry.relative_path),
            display_text(&entry.reason)
        )?;
    }
    Ok(())
}

pub(super) fn write_terminal_plan(
    output: &mut impl Write,
    plan: &CargoHomePlan,
) -> Result<(), CliError> {
    writeln!(output, "cargo-reclaim cargo-home plan")?;
    writeln!(
        output,
        "dry-run planning only; no files were deleted or modified"
    )?;
    writeln!(
        output,
        "human-readable text; use --json for a stable structured document"
    )?;
    writeln!(output, "root: {}", display_path(&plan.input.root))?;
    writeln!(output, "source: {}", source_label(plan.input.source))?;
    writeln!(output, "policy: {}", policy_label(plan.policy))?;
    writeln!(output, "entries: {}", plan.totals.entry_count)?;
    writeln!(
        output,
        "delete candidates: {}",
        plan.totals.delete_candidate_count
    )?;
    writeln!(
        output,
        "delete candidate bytes: {}",
        plan.totals.delete_candidate_bytes
    )?;
    writeln!(output, "preserved entries: {}", plan.totals.preserved_count)?;
    writeln!(output, "preserved bytes: {}", plan.totals.preserved_bytes)?;
    writeln!(
        output,
        "skipped/problem entries: {}",
        plan.totals.skipped_count
    )?;
    writeln!(output, "problems: {}", plan.totals.problem_count)?;

    if !plan.recommendations.is_empty() {
        writeln!(output)?;
        for recommendation in &plan.recommendations {
            writeln!(
                output,
                "recommendation: {}",
                display_text(&recommendation.message)
            )?;
        }
    }

    if plan.entries.is_empty() {
        writeln!(output)?;
        writeln!(output, "no Cargo home plan entries found")?;
        return Ok(());
    }

    writeln!(output)?;
    for entry in &plan.entries {
        writeln!(
            output,
            "{}\t{}\t{}\t{}\t{}\t{}",
            action_label(entry.action),
            class_label(entry.class),
            path_kind_label(entry.path_kind),
            entry.size_bytes,
            display_path(&entry.relative_path),
            display_text(&entry.reason)
        )?;
    }
    Ok(())
}

pub(super) fn write_terminal_apply_report(
    output: &mut impl Write,
    report: &CargoHomeApplyReport,
) -> Result<(), CliError> {
    if report.validation_only {
        writeln!(output, "cargo-reclaim cargo-home apply validation")?;
        writeln!(output, "validation only; no files were deleted or modified")?;
    } else {
        writeln!(output, "cargo-reclaim cargo-home apply execution")?;
        writeln!(
            output,
            "execution mode; files were removed only after persisted-plan revalidation"
        )?;
    }
    writeln!(
        output,
        "human-readable text; use --json for a stable structured document"
    )?;
    writeln!(output, "plan id: {}", report.plan_id.as_str())?;
    writeln!(output, "entries: {}", report.totals.entry_count)?;
    writeln!(
        output,
        "delete candidates: {}",
        report.totals.delete_candidate_count
    )?;
    writeln!(output, "would delete: {}", report.totals.would_delete_count)?;
    writeln!(
        output,
        "would delete bytes: {}",
        report.totals.would_delete_bytes
    )?;
    writeln!(output, "deleted: {}", report.totals.applied_count)?;
    writeln!(output, "deleted bytes: {}", report.totals.applied_bytes)?;
    writeln!(output, "delete failures: {}", report.totals.failed_count)?;
    writeln!(output, "skipped: {}", report.totals.skipped_count)?;
    writeln!(output, "stale skips: {}", report.totals.stale_skip_count)?;

    for entry in &report.entries {
        writeln!(
            output,
            "{}\t{}\t{}\t{}\t{}",
            apply_status_label(entry.status),
            display_text(&entry.planned_action),
            entry.size_bytes,
            display_text(&entry.path),
            display_text(&entry.reason)
        )?;
    }

    Ok(())
}

fn apply_status_label(status: CargoHomeApplyEntryStatus) -> &'static str {
    match status {
        CargoHomeApplyEntryStatus::WouldDelete => "would_delete",
        CargoHomeApplyEntryStatus::Deleted => "deleted",
        CargoHomeApplyEntryStatus::NotPlannedForDeletion => "not_planned_for_deletion",
        CargoHomeApplyEntryStatus::SkipStalePlan => "skip_stale_plan",
        CargoHomeApplyEntryStatus::DeleteFailed => "delete_failed",
    }
}