use crate::paths;
use std::env;
use std::error::Error;
use std::fs;
use std::path::{Path, PathBuf};
#[derive(Debug, Clone)]
pub struct CleanOptions {
pub project_dir: PathBuf,
pub temp: bool,
pub cache: bool,
pub tools: bool,
pub dry_run: bool,
}
#[derive(Debug, Default)]
pub struct CleanReport {
pub removed: Vec<PathBuf>,
pub missing: Vec<PathBuf>,
pub failed: Vec<(PathBuf, String)>,
}
pub fn run_clean(options: &CleanOptions) -> Result<CleanReport, Box<dyn Error>> {
let mut report = CleanReport::default();
let project_dir = if options.tools {
Some(fs::canonicalize(&options.project_dir)?)
} else {
None
};
let mut targets = Vec::new();
if options.temp {
targets.extend(find_temp_dirs(paths::BUILD_TEMP_PREFIX)?);
targets.extend(find_temp_dirs(paths::RUN_TEMP_PREFIX)?);
}
if options.cache {
targets.push(paths::cache_root());
}
if options.tools {
if let Some(project_dir) = project_dir.as_ref() {
targets.push(project_dir.join(paths::tools_dir_name()));
}
}
targets.sort();
targets.dedup();
for target in targets {
if !target.exists() {
report.missing.push(target);
continue;
}
if options.dry_run {
report.removed.push(target);
continue;
}
if let Err(err) = remove_any(&target) {
report.failed.push((target, err.to_string()));
} else {
report.removed.push(target);
}
}
Ok(report)
}
fn find_temp_dirs(prefix: &str) -> Result<Vec<PathBuf>, Box<dyn Error>> {
let mut dirs = Vec::new();
for entry in fs::read_dir(env::temp_dir())? {
let entry = entry?;
let path = entry.path();
if !path.is_dir() {
continue;
}
let Some(name) = path.file_name().and_then(|name| name.to_str()) else {
continue;
};
if name.starts_with(prefix) {
dirs.push(path);
}
}
Ok(dirs)
}
fn remove_any(path: &Path) -> Result<(), Box<dyn Error>> {
if path.is_dir() {
fs::remove_dir_all(path)?;
} else {
fs::remove_file(path)?;
}
Ok(())
}