Select Git revision
docker_init.sh
mount.rs 11.79 KiB
// SPDX-FileCopyrightText: 2023 Nicolas Fontrodona
//
// SPDX-License-Identifier: AGPL-3.0-or-later
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);
}
}
}
}
}
pub mod file_diff {
use crate::clean;
use std::{
fs,
path::PathBuf,
process::{exit, Command},
};
extern crate globwalk;
extern crate imghdr;
use colored::Colorize;
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))
}
}
/// Get the path of the .tmp folder
///
/// # Description
/// Get the path of the .tmp folder at the root of the project folder. if it
/// doesn't exits, then creates it
pub(crate) fn get_tmp_folder(borgfolder: &PathBuf) -> PathBuf {
let tmp_folder = clean::get_tmp_path(borgfolder);
if !tmp_folder.is_dir() {
fs::create_dir(&tmp_folder)
.expect(&format!("{}: Unable to create .tmp folder", "error".red()));
}
tmp_folder
}
/// Get the extention of a file
///
/// # Arguments
/// * my_file: The name of the file from which we want to recover the
/// extention
///
/// # Returns
/// The extention of my_file
fn get_file_extention(my_file: &PathBuf) -> &str {
my_file.extension().unwrap().to_str().unwrap()
}
/// Return true if a file is a png, jpeg, Bmp, Ico, Pdf or Svg file
///
/// # Arguments
/// * my_file: A file
///
/// # Returns
/// A boolean indicating if my_file is an image (png, jpeg Bmp, Ico, svg or
/// Pdf file)
fn is_image(my_file: &PathBuf) -> bool {
let res = imghdr::from_file(my_file).unwrap();
match res {
Some(imghdr::Type::Png) => true,
Some(imghdr::Type::Jpeg) => true,
Some(imghdr::Type::Bmp) => true,
Some(imghdr::Type::Ico) => true,
Some(_) => false,
None => match get_file_extention(my_file) {
"pdf" | "PDF" => true,
"svg" | "SVG" => true,
&_ => false,
},
}
}
/// launch image diff program
/// # Arguments
/// tuple_file: A tuple containing two files
/// tmp_folder: A tmp folder
fn launch_image_diff(tuple_file: (PathBuf, PathBuf), tmp_folder: &PathBuf) -> () {
let ext = get_file_extention(&tuple_file.0);
let name = tuple_file.0.file_stem().unwrap().to_str().unwrap();
let mut outfig = tmp_folder.clone();
outfig.push(format!("{}_diff.{}", name, ext));
let margs = [
"-write",
"mpr:img",
"+delete",
"-fill",
"white",
"-colorize",
"80%",
r"\(",
"mpr:img",
"-compose",
"minus",
"-composite",
"-threshold",
"0",
"-background",
"red",
"-alpha",
"shape",
r"\)",
r"\(",
"mpr:img",
"+swap",
"-compose",
"minus",
"-composite",
"-threshold",
"0",
"-background",
"green",
"-alpha",
"shape",
r"\)",
"-background",
"none",
"-compose",
"over",
"-flatten",
];
let cmd = format!(
"convert {} {} {} {}",
tuple_file.0.to_str().unwrap(),
tuple_file.1.to_str().unwrap(),
margs.join(" "),
&outfig.to_str().unwrap()
);
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);
}
}
}
/// launch delta program
/// # Arguments
/// tuple_file: A tuple containing two files
fn launch_delta(tuple_file: (PathBuf, PathBuf)) -> () {
println!(
"delta {} {}",
tuple_file.0.to_str().unwrap(),
tuple_file.1.to_str().unwrap()
);
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");
}
/// launch diff program
fn launch_diff_program(tuple_file: (PathBuf, PathBuf), result_folder: &PathBuf) -> () {
match is_image(&tuple_file.0) & is_image(&tuple_file.1) {
true => {
let tmp_folder = get_tmp_folder(result_folder);
launch_image_diff(tuple_file, &tmp_folder)
}
false => launch_delta(tuple_file),
}
}
/// 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_diff_program(ffiles, &result_folder);
}
}