Skip to content
Snippets Groups Projects
Select Git revision
  • 6197492a92e681f320e3ed4e7e3ec042b1eef06b
  • master default protected
  • dev
  • v2.0.0
  • v0.4.0
  • v0.3.0
  • v0.2.9
  • v0.2.8
  • v0.2.7
  • v0.2.6
  • v0.1.0
  • v0.2.5
  • v0.2.4
  • v0.2.3
  • v0.2.2
  • v0.2.1
  • v0.2.0
  • v0.1.2
18 results

docker_init.sh

Blame
  • 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);
        }
    }