use std::fs;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, Mutex};
use deezconfigs::{ui, walk};
use super::common::{
determine_config_root, get_config_root_from_git, get_home_directory, get_hooks_for_command,
is_git_remote_uri, run_hooks,
};
pub fn clean(root: Option<&String>, verbose: bool) -> Result<(), i32> {
let root = if is_git_remote_uri(root) {
get_config_root_from_git(root.expect("not empty, contains a `git:` prefix"), verbose)?
} else {
determine_config_root(root, true)?
};
let home = get_home_directory()?;
let hooks = get_hooks_for_command(&root, &home, verbose)?;
let mut nb_hooks_ran = 0;
nb_hooks_ran += run_hooks(|| hooks.pre_clean())?;
let files = Arc::new(Mutex::new(Vec::with_capacity(20)));
let nb_files_removed = AtomicUsize::new(0);
let nb_errors = AtomicUsize::new(0);
walk::find_files_recursively(&root, |p| {
debug_assert!(!p.is_dir());
let destination = home.join(p);
if destination.is_dir() {
if let Err(err) = fs::remove_dir(&destination) {
nb_errors.fetch_add(1, Ordering::Relaxed);
eprintln!(
"{error}: Could not remove exising directory '{}': {err}",
destination.display(),
error = ui::Color::error("error"),
);
return;
}
}
if destination.is_file() {
if let Err(err) = fs::remove_file(&destination) {
nb_errors.fetch_add(1, Ordering::Relaxed);
eprintln!(
"{error}: Could not remove file '{}': {err}",
destination.display(),
error = ui::Color::error("error"),
);
return;
}
#[allow(clippy::items_after_statements)]
const DEPTH_LIMIT: usize = 20;
for (i, dir) in destination.ancestors().skip(1).enumerate() {
if dir == home {
break;
}
if fs::remove_dir(dir).is_err() {
break;
}
if i == DEPTH_LIMIT {
break;
}
}
}
if verbose {
let file = p.to_string_lossy().to_string();
if let Ok(mut files) = files.lock() {
files.push(file);
drop(files);
} else {
println!("{}", p.display());
}
}
nb_files_removed.fetch_add(1, Ordering::Relaxed);
});
let mut files = Arc::try_unwrap(files)
.expect("processing is over, we're back to a single thread.")
.into_inner()
.unwrap();
files.sort();
ui::print_files(&files);
nb_hooks_ran += run_hooks(|| hooks.post_clean())?;
let nb_files_removed = nb_files_removed.into_inner();
let nb_errors = nb_errors.into_inner();
ui::print_summary(
ui::Action::Clean,
&root,
nb_files_removed,
nb_errors,
nb_hooks_ran,
);
if nb_errors > 0 { Err(1) } else { Ok(()) }
}