use std::collections::BTreeMap;
use std::path::{Path, PathBuf};
use anyhow::{Context, Result};
use crate::config::Registry;
use crate::engine;
use crate::output;
pub fn run(path_str: &str) -> Result<()> {
let path = Path::new(path_str)
.canonicalize()
.with_context(|| format!("Path not found: {path_str}"))?;
output::print_header("dev-prune restore");
output::print_info(&format!(
"Restoring dependencies in {}",
output::clean_path(&path)
));
let global_depth = crate::config::Registry::load()
.map(|r| r.settings.scan_depth)
.unwrap_or(crate::constants::DEFAULT_SCAN_DEPTH);
let results = engine::restore_project_to_depth(&path, global_depth)?;
let mut failed = 0usize;
for (adapter_name, result) in &results {
match result {
Ok(()) => {
output::print_success(&format!("{adapter_name}: dependencies restored"));
}
Err(e) => {
output::print_error(&format!("{adapter_name}: {e}"));
failed += 1;
}
}
}
if failed > 0 {
anyhow::bail!(
"{failed} of {} {} failed to restore — see the errors above",
results.len(),
output::plural(results.len(), "adapter", "adapters")
);
}
output::print_success(&format!(
"Restored {} {}.",
results.len(),
output::plural(results.len(), "adapter", "adapters")
));
Ok(())
}
pub fn run_last_run() -> Result<()> {
let registry = Registry::load()?;
let Some(last) = registry.last_prune.as_ref() else {
anyhow::bail!(
"No prune pass has been recorded yet, so there is nothing to put back.\n \
`devp restore <path>` restores a project you name."
);
};
let total: u64 = last.dirs.iter().map(|d| d.size_freed).sum();
output::print_header("dev-prune restore --last-run");
output::print_info(&format!(
"Putting back {} {} deleted on {} ({}).",
last.dirs.len(),
output::plural(last.dirs.len(), "directory", "directories"),
last.at.format("%Y-%m-%d %H:%M UTC"),
output::format_bytes(total)
));
let mut by_repo: BTreeMap<PathBuf, Vec<(String, String)>> = BTreeMap::new();
for dir in &last.dirs {
by_repo
.entry(dir.repo_path.clone())
.or_default()
.push((dir.bloat_dir.clone(), dir.adapter.clone()));
}
let global_depth = registry.settings.scan_depth;
let mut attempted = 0usize;
let mut failed = 0usize;
for (repo_path, deleted) in &by_repo {
println!();
output::print_info(&output::clean_path(repo_path));
if !repo_path.exists() {
for (label, adapter) in deleted {
attempted += 1;
failed += 1;
output::print_error(&format!(
" {adapter} ({label}): the repository no longer exists at this path"
));
}
continue;
}
for (label, result) in engine::restore_deleted(repo_path, deleted, global_depth) {
attempted += 1;
match result {
Ok(()) => output::print_success(&format!(" {label}: restored")),
Err(e) => {
failed += 1;
output::print_error(&format!(" {label}: {e}"));
}
}
}
}
println!();
if failed > 0 {
anyhow::bail!(
"{failed} of {attempted} {} failed to restore — see the errors above",
output::plural(attempted, "directory", "directories")
);
}
output::print_success(&format!(
"Restored {attempted} {} across {} {}.",
output::plural(attempted, "directory", "directories"),
by_repo.len(),
output::plural(by_repo.len(), "repository", "repositories")
));
Ok(())
}