diff --git a/src/pull.rs b/src/pull.rs index b6ff296711f9acce9ab1aaf1b42fc6d474d4733b..46ff9b6de7fd0e8035ab4245de09ffefba11d9e5 100644 --- a/src/pull.rs +++ b/src/pull.rs @@ -2,254 +2,271 @@ // // SPDX-License-Identifier: AGPL-3.0-or-later -// use crate::commit; -// use crate::mount::file_diff; -// use crate::push; -// use colored::Colorize; -// use std::path::PathBuf; -// use std::process::{exit, Command, Stdio}; +use crate::commit; +use crate::mount; +use crate::push; +use colored::Colorize; +use std::fs; +use std::path::PathBuf; +use std::process::exit; +use std::process::Command; +use std::process::Stdio; -// /// Function that gets the backup borg folder path used to restore or .borg -// /// folder in case that the pull command fails -// /// -// /// # Arguments -// /// - ` borg_folder` : Path to .brg folder of the current project -// /// # Return -// /// backup borg folder path used to restore or .borg -// /// folder in case that the pull command fails. It will be located in -// /// .tmp/<PROJECT_DIR>_bkp folder. Where <PROJECT_DIR> is the folder name -// /// containging our project -// pub(crate) fn get_savefolder(borg_folder: &PathBuf) -> PathBuf { -// let mut save_dir = file_diff::get_tmp_folder(borg_folder); -// let folder_name = borg_folder -// .parent() -// .unwrap() -// .file_name() -// .unwrap() -// .to_str() -// .unwrap(); -// save_dir.push(format!("{}_bkp", folder_name)); -// save_dir -// } +/// Function used to import a tar archive in the remote folder of the current +/// filsesystem +/// +/// # Arguments +/// - `borg_folder`: Path to borg folder +/// - `remote_file`: Path where the archive file will be stored +/// - `archive`: The archive name +fn import_tar_locally(borg_folder: &PathBuf, remote_file: &PathBuf, archive: &str) { + let borg_archive = format!( + "{}::{}", + borg_folder.canonicalize().unwrap().to_str().unwrap(), + archive + ); + let args = vec!["import-tar", &borg_archive, remote_file.to_str().unwrap()]; + let mut output: std::process::Child = Command::new("borg") + .args(&args) + .stdout(Stdio::inherit()) + .stdin(Stdio::inherit()) + .spawn() + .unwrap(); + let ecode = match output.wait() { + Err(e) => { + eprintln!( + "{}: borg import-tar returned an error: {} !", + "error".red(), + e + ); + exit(1); + } + Ok(r) => r, + }; + match ecode.code().unwrap() { + 0 => (), + num => { + exit(num); + } + }; +} -// /// Function that removes the backup folder (`save_dir`) in the tmp directory if it exists -// /// # Argument -// /// - `save_dir`: The backup folder inside the .tmp directory -// fn check_and_remove_dir(save_dir: &PathBuf) { -// if !save_dir.is_dir() { -// return (); -// } -// let anwser = push::user_question(&format!( -// "The backup folder {} already exists, Erase it with you're current .borg folder for backup", -// save_dir.display() -// )); -// if !anwser { -// println!("{}: Exiting !", "info".blue()); -// exit(0); -// } -// match std::fs::remove_dir_all(save_dir) { -// Err(e) => { -// eprintln!( -// "{}: Unable to remove {}. {}", -// "error".red(), -// save_dir.display(), -// e -// ); -// exit(1); -// } -// Ok(_) => (), -// }; -// } +/// Function used to import a tar archive in the remote folder in a remote +/// file-system +/// +/// # Arguments +/// - `borg_folder`: Path to borg folder +/// - `remote_file`: Path where the archive file will be stored +/// - `archive`: The archive name +/// - `adress`: The server location +fn import_tar_remotely(borg_folder: &PathBuf, remote_file: &PathBuf, archive: &str, adress: &str) { + let tmp_folder = mount::file_diff::get_tmp_folder(borg_folder); + let tmp_file = tmp_folder.join(remote_file.file_name().unwrap()); + let remote_args = format!("{}:{}", adress, remote_file.to_str().unwrap()); + let args = vec![&remote_args, tmp_file.to_str().unwrap()]; + let mut output = Command::new("rsync") + .args(&args) + .stdout(Stdio::inherit()) + .stdin(Stdio::inherit()) + .spawn() + .unwrap(); + let ecode = match output.wait() { + Err(e) => { + eprintln!("{}: rsync returned an error: {} !", "error".red(), e); + exit(1); + } + Ok(r) => r, + }; + match ecode.code().unwrap() { + 0 => (), + num => { + exit(num); + } + }; + import_tar_locally(borg_folder, &tmp_file, archive); + fs::remove_file(tmp_file).unwrap(); +} -// /// Function that will remove almost all the content of the .borg folder -// /// it only keeps .gblkconfig if it exists -// /// # Arguments -// /// - `borg_folder` : path to the .borg folder -// fn save_and_clean_borg_folder(borg_folder: &PathBuf) { -// let save_dir = get_savefolder(borg_folder); -// println!( -// "{}: Saving current .borg repositoy into {} for backup.\n - If everything works, you can remove it with the command {}.\n - If nothing works anymore, you can restore it with {}", -// "info".blue(), -// &save_dir.display(), -// "gblk clean".green(), -// "gblk restore".green() -// ); -// println!("{}", &save_dir.display()); -// // check if the folder exits, removes it with the user permission -// check_and_remove_dir(&save_dir); -// match std::fs::create_dir(&save_dir) { -// Err(e) => { -// eprintln!( -// "{}: Unable to create {}. {}", -// "error".red(), -// &save_dir.display(), -// e -// ); -// exit(1); -// } -// Ok(_) => (), -// } -// let paths = std::fs::read_dir(borg_folder).unwrap(); -// for p in paths { -// let pa = p.unwrap().path(); -// let name = pa.file_name().unwrap().to_str().unwrap(); -// let mut destination = save_dir.to_owned(); -// destination.push(name); -// match name { -// ".gblkconfig" | "config" => { -// std::fs::copy(&pa, &destination).expect(&format!( -// "{}: Unable to copy {} to {}. Your .borg folder may be corrupted, you can restor it wiht the command {}", -// "error".red(), -// &pa.display(), -// &destination.display(), -// "gblk restore".green() -// )); -// } -// _ => { -// std::fs::rename(&pa, &destination).expect(&format!( -// "{}: Unable to move {} to {} Your .borg folder may be corrupted, you can restor it wiht the command {}", -// "error".red(), -// &pa.display(), -// &destination.display(), -// "gblk restore".green() -// )); -// } -// } -// } -// } +/// Function used to export a tar archive in the remote folder +/// # Arguments +/// - `borg_folder`: Path to borg folder +/// - `remote_file`: Path where the archive file will be stored +/// - `adress`: to location of the `url` path, 'file' for a local file +/// - `archive`: The archive name +fn import_tar(borg_folder: &PathBuf, remote_file: &PathBuf, adress: &str, archive: &str) -> () { + match adress { + "file" => import_tar_locally(borg_folder, remote_file, archive), + _ => import_tar_remotely(borg_folder, remote_file, archive, adress), + } +} -// /// Function that check if `path_str` is an existing directory. If it exists, -// /// return it else raise an error -// /// # Arguments -// /// - `path_str`: A path in string form -// /// # Return -// /// The pathbuf object of `path_str` -// fn check_ok(path_str: &str) -> PathBuf { -// let path = PathBuf::from(path_str); -// if !path.is_dir() { -// eprintln!( -// "{}: {} is not a directory !", -// "error:".red(), -// path.display() -// ); -// exit(1) -// } else { -// return path; -// } -// } +/// Function used to get all archives id present in .borg folder +/// List the content of the borg archive +pub fn borg_list() -> Vec<String> { + let mut args = Vec::new(); + args.push(String::from("list")); + args.push(String::from("--short")); + let (borg_path, _) = commit::check_path(); + args.push(String::from(borg_path.to_str().unwrap())); + let output = Command::new("borg").args(&args).output(); + let res = match output { + Err(e) => { + eprintln!( + "{}: borg list terminated with an error {}", + "error".red(), + e + ); + exit(1); + } + Ok(r) => r, + }; + match res.status.success() { + true => (), + false => { + eprintln!("{}: borg list returned an error", "error".red()); + exit(1); + } + }; + let output = String::from_utf8_lossy(&res.stdout).to_string(); + let output = output.split_ascii_whitespace().collect::<Vec<&str>>(); + output + .into_iter() + .map(|x| x.to_string()) + .collect::<Vec<String>>() +} -// /// Get the borg base directory -// /// -// /// # Description -// /// Get the directory defined with BORG_BASE_DIR env variable or the directory -// /// defined in HOME variable -// /// -// /// # Return -// /// The borg base directory -// fn get_base_dir() -> PathBuf { -// let base_dir = std::env::var("BORG_BASE_DIR"); -// match base_dir { -// Ok(dir) => check_ok(&dir), -// Err(_) => { -// let base_dir = std::env::var("HOME"); -// match base_dir { -// Ok(dir) => check_ok(&dir), -// Err(_) => { -// eprintln!("{}: HOME environment variable not set !", "error".red()); -// exit(1); -// } -// } -// } -// } -// } +/// Function that will pull one specific archive in the .borg repository +/// +/// # Arguments: +/// - `remote`: The remote folder to use +/// - `archive`: The archive to pull +fn pull_one_archive(remote: &str, archive: &str) { + let (adress, url) = push::get_adress_and_url(remote); + let (borg_folder, _) = commit::check_path(); + let borg_folder = borg_folder.canonicalize().unwrap(); + let tmp_remote = push::get_remote_file(&url, archive, "none"); + // check if the archive if present locally + let borg_archives = borg_list(); + if borg_archives.contains(&archive.to_string()) { + eprintln!( + "{}: The archive {} is already defined in your local .borg folder. Skipping", + "warning".yellow(), + archive.yellow() + ); + return (); + } + // check if the remote archive file exists + let remote_file = push::handle_existing_remote_file(&tmp_remote, &adress, "pull"); + import_tar(&borg_folder, &remote_file, &adress, &archive) +} -// /// Get the borg config directory -// /// -// /// # Description -// /// Get the directory defined with BORG_BASE_DIR env variable or the directory -// /// defined in HOME variable -// /// -// /// # Return -// /// The borg base directory -// fn get_config_dir() -> PathBuf { -// let mut config = get_base_dir(); -// config.push(".config"); -// let new_config = if !std::env::var("BORG_BASE_DIR").is_ok() { -// let mut new_config = match std::env::var("XDG_CONFIG_HOME") { -// Ok(p) => check_ok(&p), -// Err(_) => config, -// }; -// new_config.push("borg"); -// new_config -// } else { -// let config_path = match std::env::var("BORG_CONFIG_DIR") { -// Ok(p) => check_ok(&p), -// Err(_) => { -// config.push("borg"); -// config -// } -// }; -// config_path -// }; -// if !new_config.is_dir() { -// eprintln!("{}: Failed to get borg config dir", &new_config.display()); -// exit(1); -// } -// new_config -// } +/// Function that gets all the archives localised in a remote file-system +/// +/// # Arguments +/// - `url`: The url of the remote folder +/// # Return +/// A vector of archive +fn get_local_archives(url: &PathBuf) -> Vec<String> { + let mut filenames: Vec<String> = vec![String::from("*.tar*")]; + let walker = globwalk::GlobWalkerBuilder::from_patterns(url, &filenames) + .max_depth(1) + .follow_links(false) + .build() + .unwrap() + .into_iter() + .filter_map(Result::ok) + .map(|x| x.path().to_path_buf()) + .collect::<Vec<_>>(); + if walker.len() > 0 { + let mut res = walker + .into_iter() + .map(|x| x.file_stem().unwrap().to_str().unwrap().to_string()) + .collect::<Vec<String>>(); + res.sort(); + res.dedup(); + res + } else { + Vec::new() + } +} -// /// This function aims to get the borg security dir -// /// -// /// # Description -// /// Get the borg security folder defined by the BORG_SECURITY_DIR env variable -// /// or get the borg_config_dir/security path -// /// # Return -// /// The path to the borg security dir -// fn get_security_dir() -> PathBuf { -// let security_dir = std::env::var("BORG_SECURITY_DIR"); -// match security_dir { -// Ok(dir) => check_ok(&dir), -// Err(_) => { -// let mut path = get_config_dir(); -// path.push("security"); -// return path; -// } -// } -// } +/// Function that get archives from a remote file-system +/// # Arguments +/// - `url`: The remote path +/// - `adress`: The adress of the remote folder +/// # Return +/// The vector of archives +fn get_remote_archives(url: &PathBuf, adress: &str) -> Vec<String> { + let remote_files = url.join("*.tar*"); + let args = vec![adress, "ls", remote_files.to_str().unwrap()]; + let output = Command::new("ssh") + .args(&args) + .stdout(Stdio::piped()) + .output() + .unwrap(); + let str_paths = String::from_utf8(output.stdout).unwrap(); + let paths = str_paths.split_ascii_whitespace().collect::<Vec<&str>>(); + if paths.len() > 0 { + let mut archive_vec = paths + .into_iter() + .map(|x| x.to_owned()) + .collect::<Vec<String>>(); + archive_vec.sort(); + archive_vec.dedup(); + archive_vec + } else { + Vec::new() + } +} +/// Function that get the list of archives stored in the remote directory +/// +/// # Arguments +/// - `url`: The path of the remote +/// - `adress`: The adress of the remote folder +/// +/// # Return +/// The list of archive found remotely +fn get_archive_list(url: &PathBuf, adress: &str) -> Vec<String> { + if adress == "file" { + get_local_archives(url) + } else { + get_remote_archives(url, adress) + } +} -// /// Function that removes .borg cache -// /// # Arguments -// /// - `borg_folder`: The .borg folder of the current project -// fn borg_delete_cache(borg_folder: &PathBuf) { -// let args = vec!["delete", "--cache-only", borg_folder.to_str().unwrap()]; -// let mut output = Command::new("borg") -// .args(&args) -// .stdout(Stdio::inherit()) -// .stdin(Stdio::inherit()) -// .spawn() -// .unwrap(); -// match output.wait() { -// Err(e) => { -// eprintln!("{}: borg terminaned with an error: {}", "error".red(), e); -// exit(1); -// } -// Ok(_) => (), -// } -// } +/// Function that perform the pull for all remote archive +/// +/// # Argument: +/// - `remote`: The name of the remote +fn pull_all_archives(remote: &str) { + let (adress, url) = push::get_adress_and_url(remote); + let archives = get_archive_list(&url, &adress); + for archive in archives { + pull_one_archive(remote, &archive); + } +} -// /// Create a push the tar achive on the selected remote path -// /// # Arguments -// /// - `remote`: The name of a remote -// pub fn pull(remote: &str) -> () { -// let (adress, url) = push::get_adress_and_url(remote); -// let (borg_folder, _) = commit::check_path(); -// let borg_folder = borg_folder.canonicalize().unwrap(); -// push::check_dest_and_copy(&borg_folder, &url, &adress); -// // let remote_dir = push::get_remote_dir(&url); -// // push::handle_existing_remote_dir(&remote_dir, &adress, "pull"); -// // save_and_clean_borg_folder(&borg_folder); -// // push::export_tar(&borg_folder, &remote_dir, &adress, "pull"); -// // remove_manifest_timestamp(); -// // borg_delete_cache(&borg_folder); -// } +/// Create a push the tar achive on the selected remote path +/// # Arguments +/// - `remote`: The name of a remote +/// - `archive`: The name of the archive to use (optional) +/// - `all`: A bollean indicating whether we want to push all new archive into +/// our .borg repository or not +pub fn pull(remote: &str, archive: &Option<String>, all: bool) -> () { + if !all { + match archive { + None => { + eprintln!("{}: Nothing to do !", "warning".yellow()); + return (); + } + Some(arc) => pull_one_archive(remote, arc), + } + } else { + match archive { + Some(_) => eprintln!("{}: ignoring archive given in input and pulling all archives from remote repository.", "warning".yellow()), + None => () + } + pull_all_archives(remote); + } +}