From 8cd3beba4ae650cf8086169c0db137b923393a6c Mon Sep 17 00:00:00 2001 From: graelo Date: Tue, 25 Oct 2022 22:57:58 +0200 Subject: [PATCH] refactor(config)!: move strategies downwards - choice of a strategy is not global anymore but is now specific to commands save, list, restore - default strategy is now most-recent, with n = 10 In practice, this means the following commands work out of the box ```sh tmux-backup catalog list tmux-backup save tmux-backup restore ``` but they can take strategy options: ```sh tmux-backup catalog -n 12 list tmux-backup save -s classic tmux-backup save -n 13 --compact tmux-backup restore -s most-recent -n 18 ``` --- src/bin/tmux-backup.rs | 74 ++++++++++++++++++---------- src/config.rs | 100 ++++++++++++++++++++++++-------------- src/management/catalog.rs | 7 +-- 3 files changed, 114 insertions(+), 67 deletions(-) diff --git a/src/bin/tmux-backup.rs b/src/bin/tmux-backup.rs index ad4232c..bda4c5a 100755 --- a/src/bin/tmux-backup.rs +++ b/src/bin/tmux-backup.rs @@ -1,60 +1,77 @@ //! Main runner +use std::path::Path; + use async_std::task; use clap::{CommandFactory, Parser}; use clap_complete::generate; use tmux_backup::{ actions::{restore, save}, - config::{CatalogSubcommand, Command, Config}, + config::{CatalogSubcommand, Command, Config, StrategyConfig}, management::{archive::v1, catalog::Catalog}, tmux, }; -async fn run(config: Config) { - let catalog = match Catalog::new(&config.backup_dirpath, config.strategy()).await { +async fn init_catalog>( + backup_dirpath: P, + strategy_config: StrategyConfig, +) -> Catalog { + match Catalog::new(&backup_dirpath.as_ref(), strategy_config.strategy()).await { Ok(catalog) => catalog, Err(e) => { failure_message( format!( "🛑 Catalog error at `{}`: {}", - config.backup_dirpath.to_string_lossy(), + backup_dirpath.as_ref().to_string_lossy(), e ), Output::Both, ); - return; + std::process::exit(1); } - }; + } +} +async fn run(config: Config) { match config.command { - Command::Catalog { command } => match command { - CatalogSubcommand::List { - details_flag, - only_backup_status, - filepaths_flag, - } => { - catalog - .list(details_flag, only_backup_status, filepaths_flag) - .await - } - CatalogSubcommand::Compact => match catalog.compact().await { - Ok(n) => { - let message = format!("✅ deleted {n} outdated backups"); - success_message(message, Output::Stdout) + Command::Catalog { strategy, command } => { + let catalog = init_catalog(&config.backup_dirpath, strategy).await; + + match command { + CatalogSubcommand::List { + details_flag, + only_backup_status, + filepaths_flag, + } => { + catalog + .list(details_flag, only_backup_status, filepaths_flag) + .await } - Err(e) => failure_message( - format!("🛑 Could not compact backups: {}", e), - Output::Stdout, - ), - }, - }, + CatalogSubcommand::Compact => match catalog.compact().await { + Ok(n) => { + let message = format!("✅ deleted {n} outdated backups"); + success_message(message, Output::Stdout) + } + Err(e) => failure_message( + format!("🛑 Could not compact backups: {}", e), + Output::Stdout, + ), + }, + } + } Command::Describe { backup_filepath } => { v1::print_description(backup_filepath).await.unwrap() } - Command::Save { to_tmux, compact } => { + Command::Save { + strategy, + to_tmux, + compact, + } => { + let catalog = init_catalog(&config.backup_dirpath, strategy).await; + match save(&catalog.dirpath).await { Ok((backup_filepath, archive_overview)) => { if compact { @@ -81,9 +98,12 @@ async fn run(config: Config) { } Command::Restore { + strategy, to_tmux, backup_filepath, } => { + let catalog = init_catalog(&config.backup_dirpath, strategy).await; + // Either the provided filepath, or catalog.latest(), or failure message let backup_to_restore = { if let Some(ref backup_filepath) = backup_filepath { diff --git a/src/config.rs b/src/config.rs index 6f839f2..9bc31f8 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,13 +1,56 @@ -//! Simple configuration with only `save` and `restore` commands. +//! Configuration. -use std::env; use std::path::PathBuf; +use std::{env, fmt}; -use clap::{ArgAction, ArgGroup, Parser, Subcommand, ValueHint}; +use clap::{ArgAction, Parser, Subcommand, ValueEnum, ValueHint}; use clap_complete::Shell; use crate::management::{backup::BackupStatus, compaction::Strategy}; +/// Strategy values +#[derive(Debug, Clone, ValueEnum)] +pub enum StrategyValues { + /// Apply a most-recent strategy, keeping only n backups. + MostRecent, + + /// Apply a classic backup strategy. + /// + /// Keep + /// the lastest per hour for the past 24 hours, + /// the lastest per day for the past 7 days, + /// the lastest per week of the past 4 weeks, + /// the lastest per month of this year. + Classic, +} + +impl fmt::Display for StrategyValues { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let s = match self { + Self::MostRecent => "most-recent", + Self::Classic => "classic", + }; + write!(f, "{}", s) + } +} + +/// Strategy configuration. +#[derive(Debug, clap::Args)] +pub struct StrategyConfig { + #[clap(short = 's', long = "strategy", default_value_t = StrategyValues::MostRecent)] + strategy: StrategyValues, + + /// Number of recent backups to keep, for instance 10. + #[clap( + short = 'n', + long, + value_name = "NUMBER", + value_parser = clap::value_parser!(u16).range(1..), + default_value_t = 10, + )] + num_backups: u16, +} + /// Catalog subcommands. #[derive(Debug, Subcommand)] pub enum CatalogSubcommand { @@ -55,6 +98,10 @@ pub enum Command { /// one-line report to the Tmux status bar. If you run this command from the terminal, ignore /// this flag in order to print the one-line report in the terminal. Save { + /// Choose a strategy for managing backups. + #[command(flatten)] + strategy: StrategyConfig, + /// Print a one-line report in the Tmux status bar, otherwise print to stdout. #[clap(long, action = ArgAction::SetTrue)] to_tmux: bool, @@ -74,6 +121,10 @@ pub enum Command { /// one-line report to the Tmux status bar. If you run this command from the terminal, ignore /// this flag in order to print the one-line report in the terminal. Restore { + /// Choose a strategy for managing backups. + #[command(flatten)] + strategy: StrategyConfig, + /// Print a one-line report in the Tmux status bar, otherwise print to stdout. #[clap(long, action = ArgAction::SetTrue)] to_tmux: bool, @@ -85,6 +136,10 @@ pub enum Command { /// Catalog commands. Catalog { + /// Choose a strategy for managing backups. + #[command(flatten)] + strategy: StrategyConfig, + /// Catalog commands. #[clap(subcommand)] command: CatalogSubcommand, @@ -109,10 +164,6 @@ pub enum Command { #[derive(Debug, Parser)] #[clap(author, about, version)] #[clap(propagate_version = true)] -#[clap(group( - ArgGroup::new("strategy") - .required(true) - ))] pub struct Config { /// Location of backups. /// @@ -122,31 +173,6 @@ pub struct Config { default_value_os_t = default_backup_dirpath())] pub backup_dirpath: PathBuf, - /// Number of recent backups to keep, for instance 10. - #[clap(group="strategy", - short = 'k', - long="strategy-most-recent", - value_name = "NUMBER", - value_parser = clap::value_parser!(u16).range(1..), - env = "TMUX_BACKUP_STRATEGY_MOST_RECENT" - )] - strategy_most_recent: Option, - - /// Apply a classic backup strategy. - /// - /// Keep - /// the lastest per hour for the past 24 hours, - /// the lastest per day for the past 7 days, - /// the lastest per week of the past 4 weeks, - /// the lastest per month of this year. - #[clap( - group = "strategy", - long = "strategy-classic", - value_parser, - env = "TMUX_BACKUP_STRATEGY_CLASSIC" - )] - strategy_classic: bool, - /// Selection of commands. #[clap(subcommand)] pub command: Command, @@ -156,16 +182,16 @@ pub struct Config { // Helpers // -impl Config { +impl StrategyConfig { /// Compaction Strategy corresponding to the CLI arguments. pub fn strategy(&self) -> Strategy { - if let Some(k) = self.strategy_most_recent { - Strategy::most_recent(k as usize) - } else { - Strategy::Classic + match self.strategy { + StrategyValues::MostRecent => Strategy::most_recent(self.num_backups as usize), + StrategyValues::Classic => Strategy::Classic, } } } + /// Determine the folder where to save backups. /// /// If `$XDG_STATE_HOME` is defined, the function returns `$XDG_STATE_HOME/tmux-backup`, otherwise, diff --git a/src/management/catalog.rs b/src/management/catalog.rs index 01ff92a..467a9b2 100644 --- a/src/management/catalog.rs +++ b/src/management/catalog.rs @@ -43,12 +43,13 @@ impl Catalog { /// - The catalog only manages backup files such as `backup-20220804T221153.tar.zst`, other /// files are simply ignored (and in principle, should not be present). pub async fn new>(dirpath: P, strategy: Strategy) -> Result { - fs::create_dir_all(dirpath.as_ref()).await?; + let dirpath = dirpath.as_ref(); + fs::create_dir_all(dirpath).await?; - let backup_files = Self::parse_backup_filenames(dirpath.as_ref()).await?; + let backup_files = Self::parse_backup_filenames(dirpath).await?; let catalog = Catalog { - dirpath: dirpath.as_ref().to_path_buf(), + dirpath: dirpath.to_path_buf(), strategy, backups: backup_files, };