cargo-reclaim 0.2.1

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

use super::inventory::inventory_cargo_home;
use super::model::{
    CARGO_HOME_REPORT_SCHEMA_VERSION, CargoHomeClass, CargoHomeError, CargoHomeInput,
    CargoHomeRecommendation, CargoHomeReport, CargoHomeTotals,
};
use super::resolve::{CargoHomeResolveRequest, resolve_cargo_home};

#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct CargoHomeReportRequest {
    pub cargo_home: Option<PathBuf>,
}

pub fn build_cargo_home_report(
    request: CargoHomeReportRequest,
) -> Result<CargoHomeReport, CargoHomeError> {
    let input = resolve_cargo_home(CargoHomeResolveRequest {
        explicit_path: request.cargo_home,
    })?;
    report_for_input(input)
}

fn report_for_input(input: CargoHomeInput) -> Result<CargoHomeReport, CargoHomeError> {
    let (entries, problems) = inventory_cargo_home(input.clone())?;
    let totals = totals_for_entries(&entries, problems.len());
    let recommendations = recommendations(&totals);
    Ok(CargoHomeReport {
        schema_version: CARGO_HOME_REPORT_SCHEMA_VERSION,
        input,
        entries,
        totals,
        recommendations,
        problems,
    })
}

fn totals_for_entries(
    entries: &[super::model::CargoHomeEntry],
    problem_count: usize,
) -> CargoHomeTotals {
    let mut total_bytes = 0u64;
    let mut cache_bytes = 0u64;
    let mut preserved_bytes = 0u64;
    let mut skipped_count = 0usize;
    let mut known_cache_entry_count = 0usize;
    for entry in entries {
        total_bytes = total_bytes.saturating_add(entry.size_bytes);
        if entry.preserved {
            preserved_bytes = preserved_bytes.saturating_add(entry.size_bytes);
        }
        if entry.skipped {
            skipped_count += 1;
        }
        if is_cache_class(entry.class) {
            cache_bytes = cache_bytes.saturating_add(entry.size_bytes);
            known_cache_entry_count += 1;
        }
    }
    CargoHomeTotals {
        entry_count: entries.len(),
        total_bytes,
        cache_bytes,
        preserved_bytes,
        skipped_count,
        problem_count,
        known_cache_entry_count,
    }
}

fn recommendations(totals: &CargoHomeTotals) -> Vec<CargoHomeRecommendation> {
    if totals.known_cache_entry_count == 0 {
        return Vec::new();
    }
    vec![CargoHomeRecommendation {
        message: "Review Cargo cache.auto-clean-frequency to let Cargo manage global cache cleanup; cargo-reclaim did not modify Cargo config or files.".to_string(),
    }]
}

fn is_cache_class(class: CargoHomeClass) -> bool {
    matches!(
        class,
        CargoHomeClass::RegistryIndex
            | CargoHomeClass::RegistryCache
            | CargoHomeClass::RegistrySource
            | CargoHomeClass::GitDatabase
            | CargoHomeClass::GitCheckouts
    )
}