use std::fs;
use std::path::Path;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, Mutex};
use deezconfigs::{ui, walk};
use super::common::{
get_home_directory, get_hooks_for_command, resolve_and_pull_config_root, resolve_config_root,
run_hooks,
};
pub fn rsync(root: Option<&String>, verbose: bool, pull_before_command: bool) -> Result<(), i32> {
let root = if pull_before_command {
resolve_and_pull_config_root(root)?
} else {
resolve_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_rsync())?;
let files = Arc::new(Mutex::new(Vec::with_capacity(20)));
let nb_files_rsynced = 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_symlink()
&& match does_symlink_point_to_file(&home, &destination, &source) {
Ok(points_to_source) => points_to_source,
Err(err) => {
nb_errors.fetch_add(1, Ordering::Relaxed);
eprintln!("{err}");
return;
}
}
{
} else if destination.is_file() {
if let Err(err) = fs::copy(destination, source) {
nb_errors.fetch_add(1, Ordering::Relaxed);
eprintln!(
"{error}: Could not copy '{}' from home: {err}",
p.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_rsynced.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_rsync())?;
let nb_files_rsynced = nb_files_rsynced.into_inner();
let nb_errors = nb_errors.into_inner();
ui::print_summary(
ui::Action::RSync,
&root,
nb_files_rsynced,
nb_errors,
nb_hooks_ran,
);
if nb_errors > 0 { Err(1) } else { Ok(()) }
}
fn does_symlink_point_to_file(home: &Path, symlink: &Path, file: &Path) -> Result<bool, String> {
let symlink_target = match fs::read_link(symlink) {
Ok(target) => {
if target.is_relative() {
home.join(target)
} else {
target
}
}
Err(err) => {
return Err(format!(
"{error}: Symbolic link is broken '{}': {err}",
symlink.display(),
error = ui::Color::error("error"),
));
}
};
let symlink_target = match symlink_target.canonicalize() {
Ok(target) => target,
Err(err) => {
return Err(format!(
"{error}: Could not canonicalize symlink target '{}': {err}",
symlink_target.display(),
error = ui::Color::error("error"),
));
}
};
let file = match file.canonicalize() {
Ok(file) => file,
Err(err) => {
return Err(format!(
"{error}: Could not canonicalize config file '{}': {err}",
file.display(),
error = ui::Color::error("error"),
));
}
};
Ok(symlink_target == file)
}