Skip to content
Snippets Groups Projects
delete.rs 7.36 KiB
use clap::Args;
use colored::Colorize;
use std::{
    collections::HashMap as HM,
    process::{exit, Command, Stdio},
};

use crate::{commit, prune};
#[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 = "Archive filters",
        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 = "Archive filters",
        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 = "Archive filters", 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 = "Archive filters",
        display_order = 4
    )]
    pub(crate) first: Option<usize>,
    /// consider last N archives after other filters were applied
    #[clap(
        long,
        value_name = "N",
        help_heading = "Archive filters",
        display_order = 5
    )]
    pub(crate) last: Option<usize>,
    /// 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", prune::get_str(&sd.prefix)),
        ("--glob-archives", prune::get_str(&sd.glob_archives)),
        ("--sort-by", prune::get_str(&sd.sort_by)),
        ("--first", prune::string_convert(sd.first)),
        ("--last", prune::string_convert(sd.last)),
    ]);
    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);
        }
    }
}