Skip to content

Commit

Permalink
refactor(config)!: move strategies downwards
Browse files Browse the repository at this point in the history
- 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
```
  • Loading branch information
graelo committed Oct 25, 2022
1 parent 1966d13 commit 8cd3beb
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 67 deletions.
74 changes: 47 additions & 27 deletions src/bin/tmux-backup.rs
Original file line number Diff line number Diff line change
@@ -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<P: AsRef<Path>>(
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 {
Expand All @@ -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 {
Expand Down
100 changes: 63 additions & 37 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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.
///
Expand All @@ -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<u16>,

/// 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,
Expand All @@ -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,
Expand Down
7 changes: 4 additions & 3 deletions src/management/catalog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<P: AsRef<Path>>(dirpath: P, strategy: Strategy) -> Result<Catalog> {
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,
};
Expand Down

0 comments on commit 8cd3beb

Please sign in to comment.