From 346df2576ee40e5275c0140207ed5589ec41a35b Mon Sep 17 00:00:00 2001
From: Fontrodona Nicolas <nicolas.fontrodona@ens-lyon.fr>
Date: Fri, 23 Sep 2022 15:12:20 +0200
Subject: [PATCH] src/delete.rs: Add deletion subcommand wrapper

---
 src/delete.rs | 241 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 241 insertions(+)
 create mode 100644 src/delete.rs

diff --git a/src/delete.rs b/src/delete.rs
new file mode 100644
index 0000000..f146242
--- /dev/null
+++ b/src/delete.rs
@@ -0,0 +1,241 @@
+use clap::Args;
+use colored::Colorize;
+use std::{
+    collections::HashMap as HM,
+    process::{exit, Command, Stdio},
+};
+
+use crate::commit;
+#[derive(Debug, Args)]
+pub(crate) struct Delete {
+    /// Do not change the repository
+    #[clap(
+        short = 'n',
+        long = "dry-run",
+        takes_value = false,
+        help_heading = "General options"
+    )]
+    pub(crate) dry_run: bool,
+    /// Output verbose list of archive
+    #[clap(long = "--list", takes_value = false, help_heading = "General options")]
+    pub(crate) list: bool,
+    /// Print statistics for the deleted archive
+    #[clap(
+        short = 's',
+        long = "stats",
+        takes_value = false,
+        help_heading = "General options"
+    )]
+    pub(crate) stats: bool,
+    /// Delete only the local cache for the given repository
+    #[clap(
+        long = "cache-only",
+        takes_value = false,
+        help_heading = "General options"
+    )]
+    pub(crate) cache_only: bool,
+    /// Force deletion of corrupted archives, use `--force --force` in case `--force` does not work.
+    #[clap(
+        long,
+        parse(from_occurrences),
+        takes_value = false,
+        help_heading = "General options"
+    )]
+    pub(crate) force: usize,
+    /// Keep the local security info when deleting a repository
+    #[clap(
+        long = "keep-security-info",
+        takes_value = false,
+        help_heading = "General options"
+    )]
+    pub(crate) keep_security_info: bool,
+    /// Work slower but using less space
+    #[clap(
+        long = "save-space",
+        takes_value = false,
+        help_heading = "General options"
+    )]
+    pub(crate) save_space: bool,
+    /// Only consider archive names starting with this prefix
+    #[clap(
+        short = 'P',
+        long = "prefix",
+        group = "pref",
+        value_name = "PREFIX",
+        help_heading = "Common Options",
+        display_order = 1
+    )]
+    pub(crate) prefix: Option<String>,
+    /// only consider archive names matching the glob. sh: rules apply, see "borg help patterns".
+    /// `--prefix` and `--glob-archives` are mutually exclusive.
+    #[clap(
+        short = 'a',
+        long = "glob-archives",
+        group = "pref",
+        value_name = "GLOB",
+        help_heading = "Common Options",
+        display_order = 2
+    )]
+    pub(crate) glob_archives: Option<String>,
+    /// Comma-separated list of sorting keys; valid keys are: timestamp, name, id; default is: timestamp
+    #[clap(long = "sort-by", help_heading = "Common Options", display_order = 3)]
+    pub(crate) sort_by: Option<String>,
+    /// consider first N archives after other filters were applied
+    #[clap(
+        long,
+        value_name = "N",
+        help_heading = "Common Options",
+        display_order = 4
+    )]
+    pub(crate) first: Option<isize>,
+    /// consider last N archives after other filters were applied
+    #[clap(
+        long,
+        value_name = "N",
+        help_heading = "Common Options",
+        display_order = 5
+    )]
+    pub(crate) last: Option<isize>,
+    /// Name of archives to delete
+    #[clap(
+        help_heading = "Positional Arguments",
+        multiple_occurrences = true,
+        multiple_values = true,
+        required = false
+    )]
+    pub(crate) archive: Vec<String>,
+}
+
+/// Function that add flags arguments in the delete commands
+/// # Arguments
+/// * `sd`: Struct containing all the data that can be used to build the delete command
+/// # Returns
+/// A vector containing boolean options
+fn handle_boolean_options(sd: &Delete) -> Vec<String> {
+    let mut args: Vec<String> = Vec::new();
+    let boolp: HM<&str, bool> = HM::from([
+        ("--list", sd.list),
+        ("--stats", sd.stats),
+        ("--dry-run", sd.dry_run),
+        ("--cache-only", sd.cache_only),
+        ("--keep-security-info", sd.keep_security_info),
+        ("--save-space", sd.save_space),
+    ]);
+    for (name, value) in boolp {
+        if value {
+            args.push(name.to_owned());
+        }
+    }
+    args
+}
+
+/// Handle the number of time --force is given to borg delete
+/// # Arguments
+/// * margs A vector the arguments to pass to borg delete
+/// * sd: A struct containing all arguments passed to gblk delete
+/// Return
+/// Nothing, but will update `margs` parameter
+fn handle_force_arguments(margs: &mut Vec<String>, sd: &Delete) {
+    match sd.force {
+        1 | 2 => {
+            margs.push("--force".to_string());
+            if sd.force == 2 {
+                margs.push("--force".to_owned());
+            }
+        }
+        0 => (),
+        _ => {
+            eprintln!("error: --force can only be used once or twice !");
+            exit(99);
+        }
+    }
+}
+
+/// Function to handle optional arguments with an associated value
+/// # Arguments
+/// * margs A vector the arguments to pass to borg delete
+/// * sd: A struct containing all arguments passed to gblk delete
+/// Return
+/// A boolean indicating if Archive filters are used. Also Update `margs` parameter
+fn handle_arguments_value(margs: &mut Vec<String>, sd: &Delete) -> bool {
+    let va: HM<&str, String> = HM::from([
+        ("--prefix", sd.prefix.clone().unwrap_or(String::from(""))),
+        (
+            "--glob-archives",
+            sd.glob_archives.clone().unwrap_or(String::from("")),
+        ),
+        ("--sort-by", sd.sort_by.clone().unwrap_or(String::from(""))),
+        (
+            "--first",
+            sd.first.unwrap_or(-1).to_string().replace("-1", ""),
+        ),
+        (
+            "--last",
+            sd.last.unwrap_or(-1).to_string().replace("-1", ""),
+        ),
+    ]);
+    let mut count = 0;
+    for (name, value) in va {
+        if value != String::from("") {
+            count += 1;
+            margs.push(name.to_owned());
+            margs.push(value);
+        }
+    }
+    count == 0
+}
+
+/// Function handling positional arguments
+/// # Arguments
+/// * margs A vector the arguments to pass to borg delete
+/// * sd: A struct containing all arguments passed to gblk delete
+/// Return
+/// A boolean indicating if Archive filters are used. Also Update `margs` parameter
+fn handle_positional_arguments(margs: &mut Vec<String>, sd: &Delete) -> bool {
+    for p in sd.archive.to_owned().into_iter() {
+        margs.push(p);
+    }
+    sd.archive.len() == 0
+}
+
+/// Fonction that will launch borg delete
+/// # Arguments
+/// * `sd`: Struct containing all the data that can be used to build the delete command
+pub(crate) fn deletion_launcher(sd: Delete) {
+    let (borg_path, _) = commit::check_path();
+    let mut margs = handle_boolean_options(&sd);
+    handle_force_arguments(&mut margs, &sd);
+    let no_filter = handle_arguments_value(&mut margs, &sd);
+    margs.push(borg_path.to_str().unwrap().to_owned());
+    let no_pos = handle_positional_arguments(&mut margs, &sd);
+    if no_filter && no_pos {
+        let sargs = margs.join(" ");
+        let cmd = format!("borg delete {}", sargs);
+        eprintln!(
+            "{} this command `{}` will remove your `{}` repository wich may disrup the behavior of gblk !\n{}\n{}",
+            "Warning:".yellow(),
+            cmd.blue(),
+            borg_path.to_str().unwrap().blue(),
+            "Using it is not recommended".bold().yellow(),
+            "Terminating".italic().red()
+        );
+        exit(801);
+    }
+    let mut output = Command::new("borg")
+        .arg("delete")
+        .args(margs)
+        .stdout(Stdio::piped())
+        .spawn()
+        .unwrap_or_else(|e| {
+            eprintln!("An error occured during borg delete ! {}", e);
+            exit(800);
+        });
+    let ecode = output.wait().expect("error");
+    match ecode.code().unwrap() {
+        0 => (),
+        num => {
+            eprintln!("{}", ecode.to_string());
+            exit(num);
+        }
+    }
+}
-- 
GitLab