Select Git revision
mount.rs 7.68 KiB
use crate::commit;
use std::fs;
use std::path::PathBuf;
use std::process::{exit, Command};
/// Get the path of the .mount folder
///
/// # Description
/// Get the path of the .mount folder at the root of the project folder. if it
/// doesn't exits, then creates it
fn get_mount_folder(borgfolder: &PathBuf) -> PathBuf {
let mut mount_folder = borgfolder.parent().unwrap().to_path_buf();
mount_folder.push(".mount");
if !mount_folder.is_dir() {
fs::create_dir(&mount_folder).expect("Unable to create .mount directory");
}
mount_folder
}
pub fn mount_archive(
commit: &Option<String>,
path: &Option<String>,
mut versions: bool,
diff: bool,
last: Option<u8>,
) -> () {
if diff & versions {
eprintln!("Diff cannot be performed with version view. Setting versions to false");
versions = false;
}
let (borg_folder, result_folder) = commit::check_path();
let mount_folder = get_mount_folder(&borg_folder);
let globp = match commit {
Some(g) => format!("-a '{}'", g),
None => String::from(""),
};
let mp = match path {
Some(p) => String::from(p),
None => String::from(""),
};
let lastp = match last {
Some(p) => {
if p > 0 {
format!("--last {}", p)
} else {
String::from("")
}
}
None => String::from(""),
};
let vopt = match versions {
true => String::from("-o versions"),
false => String::from(""),
};
let cmd = format!(
"borg mount {} {} {} {} {} {}",
globp,
vopt,
lastp,
borg_folder.to_str().unwrap(),
mount_folder.to_str().unwrap(),
mp
);
let output = Command::new("sh").arg("-c").arg(cmd).output().unwrap();
match output.status.code().unwrap() {
0 => (),
num => {
eprintln!("{}", String::from_utf8(output.stderr).unwrap());
exit(num);
}
}
if diff {
file_diff::display_diff(&mount_folder, &result_folder, path);
}
}
/// Unount an borg archive mounted in .mount folder
/// # Argument
/// * silent a boolean indicating if the function fails silently if the archive is not mounted.
pub fn umount_archive(silent: bool) -> () {
let (borg_folder, _) = commit::check_path();
let mount_folder = get_mount_folder(&borg_folder);
let cmd = format!("borg umount {}", mount_folder.to_str().unwrap());
let output = Command::new("sh").arg("-c").arg(cmd).output().unwrap();
match output.status.code().unwrap() {
0 => (),
num => {
let mut error_msg = String::from_utf8(output.stderr).unwrap();
let msg = format!(
"fusermount: entry for {} not found in /etc/mtab",
mount_folder.canonicalize().unwrap().to_str().unwrap()
);
if error_msg.ends_with('\n') {
error_msg.pop();
}
match (error_msg == msg) & silent {
true => (),
false => {
eprintln!("{}", error_msg);
exit(num);
}
}
}
}
}
mod file_diff {
use std::{
fs,
path::PathBuf,
process::{exit, Command},
};
extern crate globwalk;
use filetime::FileTime;
/// Function used to recover files inside the folder `folder`
/// # Arguments
/// * folder: The folder containing the path pattern
/// * path_pattern: A pattern used to recover the files into that folder
/// # Returns
/// The list of file located inside `folder` matching `path_pattern`
fn recover_files(folder: &PathBuf, path_pattern: &str) -> Vec<PathBuf> {
let mfiles = globwalk::GlobWalkerBuilder::from_patterns(folder, &[path_pattern])
.max_depth(100)
.follow_links(false)
.build()
.unwrap()
.into_iter()
.filter_map(Result::ok)
.map(|x| x.path().to_path_buf())
.collect::<Vec<_>>();
let rfiles = mfiles
.into_iter()
.filter(|x| x.is_file())
.collect::<Vec<PathBuf>>();
rfiles
}
/// Arrange files by their last modification dates
/// # Arguments
/// * file_tuple: A tuple of two files
/// # Return
/// The file tuple ordered by creation date
fn arrange_by_modification_date(file_tuple: (PathBuf, PathBuf)) -> (PathBuf, PathBuf) {
let (f1, f2) = file_tuple;
let meta1 = fs::metadata(&f1).unwrap();
let time1 = FileTime::from_last_modification_time(&meta1);
let meta2 = fs::metadata(&f2).unwrap();
let time2 = FileTime::from_last_modification_time(&meta2);
if time1 < time2 {
(f1, f2)
} else {
(f2, f1)
}
}
/// Check the len of the list of files given in input with vfiles
/// # Arguments
/// * vfiles: A tuple containing files of interest
/// # Results
/// A tuple containing two paths only if vfiles contains two files else
/// return an error
fn check_file_len(vfiles: &Vec<PathBuf>) -> (PathBuf, PathBuf) {
match vfiles.len() {
2 => (vfiles[0].to_path_buf(), vfiles[1].to_path_buf()),
x => {
eprintln!(
"Unable to show diffs because {x} files were match by the --path pattern !"
);
exit(32);
}
}
}
/// Add to the list of files given in input those located in the current
/// result folder and arrange them by modification date
/// # Arguments
/// * vfile: A list of files
/// * result_folder: The result folder of the current project
/// * pattern: The path pattern given to the mount function
fn update_files(
vfile: &Vec<PathBuf>,
result_folder: &PathBuf,
pattern: &String,
) -> (PathBuf, PathBuf) {
let nres = if pattern.starts_with("results/") {
result_folder
.canonicalize()
.unwrap()
.parent()
.unwrap()
.to_path_buf()
} else {
result_folder.to_path_buf()
};
if vfile.len() == 1 {
let tmp = recover_files(&nres, pattern);
let mut tmp2 = vfile.clone();
tmp2.extend(tmp);
check_file_len(&tmp2)
} else {
arrange_by_modification_date(check_file_len(vfile))
}
}
/// launch delta program
/// # Arguments
/// tuple_file: A tuple containing two files
fn launch_delta(tuple_file: (PathBuf, PathBuf)) -> () {
Command::new("delta")
.arg(tuple_file.0.to_str().unwrap())
.arg(tuple_file.1.to_str().unwrap())
.spawn()
.expect("command failed")
.wait()
.expect("wait failed");
}
/// Function used to displays the differences between two files matching the pattern `path_pattern`
/// # Arguments
/// * mount_folder: The folder containing the path pattern
/// * path_pattern: A pattern used to recover the files into that folder
pub fn display_diff(
mount_folder: &PathBuf,
result_folder: &PathBuf,
path_pattern: &Option<String>,
) -> () {
let my_pattern = match path_pattern {
None => {
eprintln!("Cannot compare files is no path 'argument --path/-p are given !");
exit(31);
}
Some(p) => p,
};
let npattern = format!("*/{}", my_pattern);
let rfiles = recover_files(mount_folder, &npattern);
let ffiles = update_files(&rfiles, result_folder, my_pattern);
launch_delta(ffiles);
}
}