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_home_directory, get_hooks_for_command, run_hooks};
pub fn link(root: Option<&String>, verbose: bool) -> Result<(), i32> {
let root = 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_link())?;
let files = Arc::new(Mutex::new(Vec::with_capacity(20)));
let nb_files_linked = AtomicUsize::new(0);
let nb_errors = AtomicUsize::new(0);
walk::find_files_recursively(&root, |p| {
debug_assert!(!p.is_dir());
let source = root.join(p);
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 let Err(err) = fs::create_dir_all(
destination
.parent()
.expect("at the bare minimum, `parent` is `$HOME`"),
) {
nb_errors.fetch_add(1, Ordering::Relaxed);
eprintln!(
"{error}: Could not link '{}' to Home: {err}",
p.display(),
error = ui::Color::error("error"),
);
return;
}
if destination.is_file() || destination.is_symlink() {
if let Err(err) = fs::remove_file(&destination) {
nb_errors.fetch_add(1, Ordering::Relaxed);
eprintln!(
"{error}: Could not remove exising file '{}': {err}",
destination.display(),
error = ui::Color::error("error"),
);
return;
}
}
#[cfg(unix)]
let res = std::os::unix::fs::symlink(&source, &destination);
#[cfg(windows)]
let res = std::os::windows::fs::symlink_file(&source, &destination);
if let Err(err) = res {
nb_errors.fetch_add(1, Ordering::Relaxed);
eprintln!(
"{error}: Could not create link to '{}': {err}",
source.display(),
error = ui::Color::error("error"),
);
return;
}
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_linked.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_link())?;
let nb_files_linked = nb_files_linked.into_inner();
let nb_errors = nb_errors.into_inner();
ui::print_summary(
ui::Action::Link,
&root,
nb_files_linked,
nb_errors,
nb_hooks_ran,
);
if nb_errors > 0 { Err(1) } else { Ok(()) }
}