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