tmux_backup/config.rs
1//! Configuration.
2
3use std::env;
4use std::path::PathBuf;
5
6use clap::{ArgAction, Parser, Subcommand, ValueEnum, ValueHint};
7use clap_complete::Shell;
8
9use crate::management::{backup::BackupStatus, compaction::Strategy};
10
11/// Save or restore Tmux sessions.
12#[derive(Debug, Parser)]
13#[clap(author, about, version)]
14#[clap(propagate_version = true)]
15pub struct Config {
16 /// Location of backups.
17 ///
18 /// If unspecified, it falls back on: `$XDG_STATE_HOME/tmux-backup`, then on
19 /// `$HOME/.local/state/tmux-backup`.
20 #[arg(short = 'd', long = "dirpath", value_hint = ValueHint::DirPath,
21 default_value_os_t = default_backup_dirpath())]
22 pub backup_dirpath: PathBuf,
23
24 /// Selection of commands.
25 #[command(subcommand)]
26 pub command: Command,
27}
28
29/// Indicate whether to save (resp. restore) the Tmux sessions to (resp. from) a backup.
30#[derive(Debug, Subcommand)]
31pub enum Command {
32 /// Save the Tmux sessions to a new backup file.
33 ///
34 /// Sessions, windows, and panes geometry + content are saved in an archive format inside the
35 /// backup folder. In that folder, the backup name is expected to be similar to
36 /// `backup-20220531T123456.tar.zst`.
37 ///
38 /// If you run this command via a Tmux keybinding, use the `--to-tmux` flag in order to send a
39 /// one-line report to the Tmux status bar. If you run this command from the terminal, ignore
40 /// this flag in order to print the one-line report in the terminal.
41 Save {
42 /// Choose a strategy for managing backups.
43 #[command(flatten)]
44 strategy: StrategyConfig,
45
46 /// Print a one-line report in the Tmux status bar, otherwise print to stdout.
47 #[arg(long, action = ArgAction::SetTrue)]
48 to_tmux: bool,
49
50 /// Delete purgeable backups after saving.
51 #[arg(long, action = ArgAction::SetTrue)]
52 compact: bool,
53
54 /// Number of lines to ignore during capture if the active command is a shell.
55 ///
56 /// At the time of saving, for each pane where the active command is one of (`zsh`, `bash`,
57 /// `fish`), the shell prompt is waiting for input. If tmux-backup naively captures the
58 /// entire history, on restoring that backup, a new shell prompt will also appear. This
59 /// obviously pollutes history with repeated shell prompts.
60 ///
61 /// If you know the number of lines your shell prompt occupies on screen, set this option
62 /// to that number (simply `1` in my case). These last lines will not be captured. On
63 /// restore, this gives the illusion of history continuity without repetition.
64 #[arg(
65 short = 'i',
66 long = "ignore-last-lines",
67 value_name = "NUMBER",
68 default_value_t = 0
69 )]
70 num_lines_to_drop: u8,
71 },
72
73 /// Restore the Tmux sessions from a backup file.
74 ///
75 /// Sessions, windows and panes geometry + content are read from the backup marked as "current"
76 /// (often the most recent backup) inside the backup folder. In that folder, the backup name is
77 /// expected to be similar to `backup-20220531T123456.tar.zst`.
78 ///
79 /// If you run this command via a Tmux keybinding, use the `--to-tmux` flag in order to send a
80 /// one-line report to the Tmux status bar. If you run this command from the terminal, ignore
81 /// this flag in order to print the one-line report in the terminal.
82 Restore {
83 /// Choose a strategy for managing backups.
84 #[command(flatten)]
85 strategy: StrategyConfig,
86
87 /// Print a one-line report in the Tmux status bar, otherwise print to stdout.
88 #[arg(long, action = ArgAction::SetTrue)]
89 to_tmux: bool,
90
91 /// Filepath of the backup to restore, by default, pick latest.
92 #[arg(value_parser)]
93 backup_filepath: Option<PathBuf>,
94 },
95
96 /// Catalog commands.
97 Catalog {
98 /// Choose a strategy for managing backups.
99 #[command(flatten)]
100 strategy: StrategyConfig,
101
102 /// Catalog commands.
103 #[command(subcommand)]
104 command: CatalogSubcommand,
105 },
106
107 /// Describe the content of a backup file.
108 Describe {
109 /// Path to the backup file.
110 #[arg(value_parser, value_hint = ValueHint::FilePath)]
111 backup_filepath: PathBuf,
112 },
113
114 /// Print a shell completion script to stdout.
115 GenerateCompletion {
116 /// Shell for which you want completion.
117 #[arg(value_enum, value_parser = clap::value_parser!(Shell))]
118 shell: Shell,
119 },
120
121 /// Outputs the default tmux plugin config to stdout.
122 ///
123 /// Similar to shell completions, this is done once when installing tmux-backup. Type
124 /// `tmux-backup init > ~/.tmux/plugins/tmux-backup.tmux`. and source it
125 /// from your `~/.tmux.conf`. See the README for details.
126 Init,
127}
128
129/// Catalog subcommands.
130#[derive(Debug, Subcommand)]
131pub enum CatalogSubcommand {
132 /// Print a list of backups to stdout.
133 ///
134 /// By default, this prints a table of backups, age and status with colors. The flag `--details`
135 /// prints additional columns.
136 ///
137 /// If the flag `--filepaths` is set, only absolute filepaths are printed. This can be used in
138 /// scripting scenarios.
139 ///
140 /// Options `--only purgeable` or `--only retainable` will list only the corresponding backups.
141 /// They will activate the flag `--filepaths` automatically.
142 List {
143 /// Add details columns to the table.
144 ///
145 /// Print number of sessions, windows and panes in the backup and the backup's format
146 /// version. This is slightly slower because it requires each backup file to be partially
147 /// read.
148 #[arg(long = "details", action = ArgAction::SetTrue)]
149 details_flag: bool,
150
151 /// List only backups having this status.
152 #[arg(long = "only", value_enum, value_parser)]
153 only_backup_status: Option<BackupStatus>,
154
155 /// Print filepaths instead of the table format.
156 #[arg(long = "filepaths", action = ArgAction::SetTrue)]
157 filepaths_flag: bool,
158 },
159
160 /// Apply the catalog's compaction strategy: this deletes all purgable backups.
161 Compact,
162}
163
164/// Strategy values
165#[derive(Debug, Clone, ValueEnum)]
166enum StrategyValues {
167 /// Apply a most-recent strategy, keeping only n backups.
168 MostRecent,
169
170 /// Apply a classic backup strategy.
171 ///
172 /// Keep
173 /// the lastest per hour for the past 24 hours,
174 /// the lastest per day for the past 7 days,
175 /// the lastest per week of the past 4 weeks,
176 /// the lastest per month of this year.
177 Classic,
178}
179
180/// Strategy configuration.
181#[derive(Debug, clap::Args)]
182pub struct StrategyConfig {
183 #[arg(short = 's', long = "strategy", value_enum, default_value_t = StrategyValues::MostRecent)]
184 strategy: StrategyValues,
185
186 /// Number of recent backups to keep, for instance 10.
187 #[arg(
188 short = 'n',
189 long,
190 value_name = "NUMBER",
191 value_parser = clap::value_parser!(u16).range(1..),
192 default_value_t = 10,
193 )]
194 num_backups: u16,
195}
196
197//
198// Helpers
199//
200
201impl StrategyConfig {
202 /// Compaction Strategy corresponding to the CLI arguments.
203 pub fn strategy(&self) -> Strategy {
204 match self.strategy {
205 StrategyValues::MostRecent => Strategy::most_recent(self.num_backups as usize),
206 StrategyValues::Classic => Strategy::Classic,
207 }
208 }
209}
210
211/// Determine the folder where to save backups.
212///
213/// If `$XDG_STATE_HOME` is defined, the function returns `$XDG_STATE_HOME/tmux-backup`, otherwise,
214/// it returns `$HOME/.local/state/tmux-backup`.
215///
216/// # Panics
217///
218/// This function panics if even `$HOME` cannot be obtained from the environment.
219fn default_backup_dirpath() -> PathBuf {
220 let state_home = match env::var("XDG_STATE_HOME") {
221 Ok(v) => PathBuf::from(v),
222 Err(_) => match env::var("HOME") {
223 Ok(v) => PathBuf::from(v).join(".local").join("state"),
224 Err(_) => panic!("Cannot find `$HOME` in the environment"),
225 },
226 };
227
228 state_home.join("tmux-backup")
229}