Skip to content

Commit

Permalink
feat(catalog): introduce the archives catalog
Browse files Browse the repository at this point in the history
  • Loading branch information
graelo committed Aug 18, 2022
1 parent 5df7973 commit 70d602e
Show file tree
Hide file tree
Showing 6 changed files with 153 additions and 32 deletions.
71 changes: 71 additions & 0 deletions src/catalog.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
//! Catalog of all archives.
//!

use std::path;

use anyhow::Result;
use async_std::fs;
use async_std::stream::StreamExt;
use regex::Regex;

/// Catalag of all archives.
pub struct Catalog {
/// Location of the catalog.
pub dirpath: path::PathBuf,

/// Sorted list of outdated archive files (oldest to newest).
pub outdated: Vec<path::PathBuf>,

/// Sorted list of recent archive files (oldest to newest).
pub recent: Vec<path::PathBuf>,
}

impl Catalog {
/// Return a new `Catalog` by listing the archives in `dirpath`.
///
/// Only archive files such as `archive-20220804T221153.tar.zst` are added to the catalog.
pub async fn new(dirpath: &path::Path, rotate_size: usize) -> Result<Catalog> {
let mut archives: Vec<path::PathBuf> = vec![];

let pattern = r#".*archive-\d{8}T\d{6}\.tar\.zst"#;
let matcher = Regex::new(pattern).unwrap();

let mut entries = fs::read_dir(dirpath).await?;
while let Some(entry) = entries.next().await {
let entry = entry?;
let path = entry.path();
if matcher.captures(&path.to_string_lossy()).is_some() {
archives.push(path.into());
}
}

archives.sort();

let index = std::cmp::max(0, archives.len() - rotate_size);
let recent = archives.split_off(index);

Ok(Catalog {
dirpath: dirpath.to_path_buf(),
outdated: archives,
recent,
})
}

/// Total number of archives in the catalog.
pub fn size(&self) -> usize {
self.outdated.len() + self.recent.len()
}

/// Filepath of the current archive.
///
/// This is usually the most recent archive.
pub fn current(&self) -> Option<&path::Path> {
self.recent.last().map(|p| p.as_ref())
}

/// Compact the catalog by deleting old archives files, keeping only the `rotate_size` most
/// recent ones.
pub async fn compact(&mut self, rotate_size: usize) -> Result<()> {
Ok(())
}
}
41 changes: 28 additions & 13 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,17 @@ use std::path::PathBuf;

use clap::{ArgAction, Parser, Subcommand};

/// Catalog subcommands.
#[derive(Debug, Subcommand)]
pub enum CatalogSubcommand {
/// List the archives in the catalog, indicating the outdated archives.
List {
/// Number of recent sessions.
#[clap(long = "rotate-size", default_value = "10")]
rotate_size: usize,
},
}

/// Indicate whether to save (resp. restore) the Tmux sessions to (resp. from) an archive.
#[derive(Debug, Subcommand)]
pub enum Command {
Expand All @@ -20,14 +31,11 @@ pub enum Command {
/// print the report in the terminal. Otherwise, if you run it via a Tmux keybinding, the
/// one-line report is printed with `tmux display-message`.
Save {
/// Print the report (num. sessions, windows & panes) on stdout,
/// otherwise send to Tmux.
#[clap(long = "stdout", action = ArgAction::SetTrue, default_value = "false")]
stdout: bool,

/// How many archive files to keep in `ARCHIVE_DIRPATH`.
#[clap(long = "history", default_value = "10")]
num_archives: u16,
/// Size of the rolling history.
///
/// Indicates how many archive files to keep in `ARCHIVE_DIRPATH`.
#[clap(long = "rotate-size", default_value = "10")]
rotate_size: usize,
},

/// Restore the Tmux sessions.
Expand All @@ -39,11 +47,13 @@ pub enum Command {
/// If you run this command from the terminal, consider using the `--stdout` flag in order to
/// print the report in the terminal. Otherwise, if you run it via a Tmux keybinding, the
/// one-line report is printed with `tmux display-message`.
Restore {
/// Print the report (num. sessions, windows & panes) on stdout,
/// otherwise send to Tmux.
#[clap(long = "stdout", action = ArgAction::SetTrue, default_value = "false")]
stdout: bool,
Restore {},

/// Operations on the catalog of archives.
Catalog {
/// List the archives in the catalog, indicating the outdated archives.
#[clap(subcommand)]
command: CatalogSubcommand,
},
}

Expand All @@ -59,6 +69,11 @@ pub struct Config {
#[clap(short = 'd', long = "dirpath", default_value_os_t = default_archive_dirpath())]
pub archive_dirpath: PathBuf,

/// Print the report (num. sessions, windows & panes) on stdout,
/// otherwise send to Tmux.
#[clap(long = "stdout", action = ArgAction::SetTrue, default_value = "false")]
pub stdout: bool,

/// Selection of commands.
#[clap(subcommand)]
pub command: Command,
Expand Down
4 changes: 3 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@
pub mod config;
mod error;

mod catalog;
pub use catalog::Catalog;
mod report;
pub use report::Report;

pub mod save;
mod tmux;
pub use tmux::display::display_message;
pub use tmux::tmux_display_message;

use serde::{Deserialize, Serialize};

Expand Down
66 changes: 49 additions & 17 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,39 +1,71 @@
use async_std::task;
use clap::Parser;

use tmux_revive::{config::Command, config::Config, display_message, save};
use tmux_revive::{
config::CatalogSubcommand, config::Command, config::Config, save, tmux_display_message, Catalog,
};

fn main() {
let config = Config::parse();

match config.command {
Command::Save {
stdout,
num_archives,
} => {
match task::block_on(save::save(&config.archive_dirpath, num_archives)) {
Command::Save { rotate_size } => {
match task::block_on(save::save(&config.archive_dirpath, rotate_size)) {
Ok(report) => {
let message = format!(
"{report}, persisted to {}",
config.archive_dirpath.to_string_lossy()
);
if stdout {
println!("{message}");
} else {
display_message(&message);
}
success_message(&message, config.stdout);
}
Err(e) => {
let message = format!("🛑 Could not save sessions: {}", e);
if stdout {
eprintln!("{message}");
std::process::exit(1);
} else {
display_message(&message);
}
failure_message(&message, config.stdout);
}
};
}

Command::Restore { .. } => unimplemented!(),

Command::Catalog { command } => match command {
CatalogSubcommand::List { rotate_size } => {
match task::block_on(Catalog::new(&config.archive_dirpath, rotate_size)) {
Ok(catalog) => {
println!(
"Catalog: {} archives in `{}`\n",
&catalog.size(),
&catalog.dirpath.to_string_lossy()
);
for archive_path in catalog.outdated.iter() {
println!("{} (outdated)", archive_path.to_string_lossy());
}
for archive_path in catalog.recent.iter() {
println!("{}", archive_path.to_string_lossy());
}
}
Err(e) => {
let message = format!("🛑 Could not list archives: {}", e);
failure_message(&message, config.stdout);
}
}
}
},
}
}

fn success_message(message: &str, stdout: bool) {
if stdout {
println!("{message}");
} else {
tmux_display_message(message);
}
}

fn failure_message(message: &str, stdout: bool) {
if stdout {
eprintln!("{message}");
std::process::exit(1);
} else {
tmux_display_message(message);
}
}
2 changes: 1 addition & 1 deletion src/save.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use crate::{Report, Summary, PANES_DIR_NAME, SUMMARY_FILENAME};
/// `archive-20220731T222948.tar.zst`.
///
/// The n-most recent archives are kept.
pub async fn save(archive_dirpath: &Path, num_archives: u16) -> Result<Report> {
pub async fn save(archive_dirpath: &Path, rotate_size: usize) -> Result<Report> {
fs::create_dir_all(&archive_dirpath).await?;

let archive_filepath = {
Expand Down
1 change: 1 addition & 0 deletions src/tmux/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod display;
pub use display::display_message as tmux_display_message;
pub mod pane;
pub mod pane_id;
pub mod session;
Expand Down

0 comments on commit 70d602e

Please sign in to comment.