Skip to content

Commit

Permalink
feat(commands): Add lock command
Browse files Browse the repository at this point in the history
  • Loading branch information
aawsome committed Sep 27, 2024
1 parent 85c3509 commit db6ebc2
Show file tree
Hide file tree
Showing 8 changed files with 233 additions and 13 deletions.
62 changes: 57 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ rustdoc-args = ["--document-private-items", "--generate-link-to-definition"]

[dependencies]
abscissa_core = { version = "0.7.0", default-features = false, features = ["application"] }
rustic_backend = { version = "0.3.0", features = ["cli"] }
rustic_core = { version = "0.4.0", features = ["cli"] }
rustic_backend = { git = "https://github.com/rustic-rs/rustic_core.git", branch = "lock", features = ["cli"] }
rustic_core = { git = "https://github.com/rustic-rs/rustic_core.git", branch = "lock", features = ["cli"] }

# allocators
jemallocator-global = { version = "0.3.2", optional = true }
Expand Down
11 changes: 8 additions & 3 deletions src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ pub(crate) mod forget;
pub(crate) mod init;
pub(crate) mod key;
pub(crate) mod list;
pub(crate) mod lock;
pub(crate) mod ls;
pub(crate) mod merge;
pub(crate) mod prune;
Expand All @@ -39,9 +40,10 @@ use crate::{
commands::{
backup::BackupCmd, cat::CatCmd, check::CheckCmd, completions::CompletionsCmd,
config::ConfigCmd, copy::CopyCmd, diff::DiffCmd, dump::DumpCmd, forget::ForgetCmd,
init::InitCmd, key::KeyCmd, list::ListCmd, ls::LsCmd, merge::MergeCmd, prune::PruneCmd,
repair::RepairCmd, repoinfo::RepoInfoCmd, restore::RestoreCmd, self_update::SelfUpdateCmd,
show_config::ShowConfigCmd, snapshots::SnapshotCmd, tag::TagCmd,
init::InitCmd, key::KeyCmd, list::ListCmd, lock::LockCmd, ls::LsCmd, merge::MergeCmd,
prune::PruneCmd, repair::RepairCmd, repoinfo::RepoInfoCmd, restore::RestoreCmd,
self_update::SelfUpdateCmd, show_config::ShowConfigCmd, snapshots::SnapshotCmd,
tag::TagCmd,
},
config::{progress_options::ProgressOptions, AllRepositoryOptions, RusticConfig},
{Application, RUSTIC_APP},
Expand Down Expand Up @@ -113,6 +115,9 @@ enum RusticCmd {
/// List repository files
List(ListCmd),

/// Lock snapshots
Lock(LockCmd),

/// List file contents of a snapshot
Ls(LsCmd),

Expand Down
8 changes: 7 additions & 1 deletion src/commands/forget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,13 @@ impl ForgetCmd {
.get_snapshots(&self.ids)?
.into_iter()
.map(|sn| {
if sn.must_keep(now) {
if sn.is_locked(now) {
ForgetSnapshot {
snapshot: sn,
keep: true,
reasons: vec!["locked".to_string()],
}
} else if sn.must_keep(now) {
ForgetSnapshot {
snapshot: sn,
keep: true,
Expand Down
153 changes: 153 additions & 0 deletions src/commands/lock.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
//! `lock` subcommand

use std::str::FromStr;

use crate::{commands::open_repository, status_err, Application, RUSTIC_APP};
use abscissa_core::{Command, Runnable, Shutdown};

use anyhow::Result;
use chrono::{DateTime, Duration, Local};

use rustic_core::{repofile::KeyId, LockOptions};

/// `lock` subcommand
#[derive(clap::Parser, Command, Debug)]
pub(crate) struct LockCmd {
/// Subcommand to run
#[clap(subcommand)]
cmd: LockSubCmd,
}

impl Runnable for LockCmd {
fn run(&self) {
let config = RUSTIC_APP.config();
if config.global.dry_run {
println!("lock is not supported in dry-run mode");
} else {
self.cmd.run();
}
}
}

/// `lock` subcommand
#[derive(clap::Subcommand, Debug, Runnable)]
enum LockSubCmd {
/// Lock the complete repository
Repository(RepoSubCmd),
/// Lock all key files
Keys(KeysSubCmd),
/// Lock snapshots and relevant pack files
Snapshots(SnapSubCmd),
}

#[derive(clap::Parser, Command, Debug, Clone)]
pub(crate) struct RepoSubCmd {
#[clap(long)]
/// Duration for how long to extend the locks (e.g. "10d"). "forever" is also allowed
duration: LockDuration,
}

impl Runnable for RepoSubCmd {
fn run(&self) {
if let Err(err) = self.inner_run() {
status_err!("{}", err);
RUSTIC_APP.shutdown(Shutdown::Crash);
};
}
}

impl RepoSubCmd {
fn inner_run(&self) -> Result<()> {
let config = RUSTIC_APP.config();
let repo = open_repository(&config.repository)?;
repo.lock_repo(self.duration.0)?;
Ok(())
}
}

#[derive(clap::Parser, Command, Debug, Clone)]
pub(crate) struct KeysSubCmd {
#[clap(long)]
/// Duration for how long to extend the locks (e.g. "10d"). "forever" is also allowed
duration: LockDuration,
}

impl Runnable for KeysSubCmd {
fn run(&self) {
if let Err(err) = self.inner_run() {
status_err!("{}", err);
RUSTIC_APP.shutdown(Shutdown::Crash);
};
}
}

impl KeysSubCmd {
fn inner_run(&self) -> Result<()> {
let config = RUSTIC_APP.config();
let repo = open_repository(&config.repository)?;
repo.lock_repo_files::<KeyId>(self.duration.0)?;
Ok(())
}
}

#[derive(clap::Parser, Command, Debug, Clone)]
pub(crate) struct SnapSubCmd {
/// Extend locks even if the files are already locked long enough
#[clap(long)]
always_extend_lock: bool,

#[clap(long)]
/// Duration for how long to extend the locks (e.g. "10d"). "forever" is also allowed
duration: LockDuration,

/// Snapshots to lock. If none is given, use filter options to filter from all snapshots
#[clap(value_name = "ID")]
ids: Vec<String>,
}

#[derive(Debug, Clone)]
struct LockDuration(Option<DateTime<Local>>);

impl FromStr for LockDuration {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self> {
match s {
"forever" => Ok(Self(None)),
d => {
let duration = humantime::Duration::from_str(d)?;
let duration = Duration::from_std(*duration)?;
Ok(Self(Some(Local::now() + duration)))
}
}
}
}

impl Runnable for SnapSubCmd {
fn run(&self) {
if let Err(err) = self.inner_run() {
status_err!("{}", err);
RUSTIC_APP.shutdown(Shutdown::Crash);
};
}
}

impl SnapSubCmd {
fn inner_run(&self) -> Result<()> {
let config = RUSTIC_APP.config();
let repo = open_repository(&config.repository)?;

let snapshots = if self.ids.is_empty() {
repo.get_matching_snapshots(|sn| config.snapshot_filter.matches(sn))?
} else {
repo.get_snapshots(&self.ids)?
};

let lock_opts = LockOptions::default()
.always_extend_lock(self.always_extend_lock)
.until(self.duration.0);

repo.lock_snaphots(&lock_opts, &snapshots)?;

Ok(())
}
}
4 changes: 4 additions & 0 deletions src/commands/snapshots.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,10 @@ pub fn fill_table(snap: &SnapshotFile, mut add_entry: impl FnMut(&str, String))
DeleteOption::NotSet => "not set".to_string(),
DeleteOption::Never => "never".to_string(),
DeleteOption::After(t) => format!("after {}", t.format("%Y-%m-%d %H:%M:%S")),
DeleteOption::LockedUntil(t) => {
format!("locked until {}", t.format("%Y-%m-%d %H:%M:%S"))
}
DeleteOption::LockedForever => "locked forever".to_string(),
};
add_entry("Delete", delete);
add_entry("Paths", snap.paths.formatln());
Expand Down
2 changes: 1 addition & 1 deletion src/commands/tag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ impl TagCmd {
println!("would have modified the following snapshots:\n {old_snap_ids:?}");
}
(false, false) => {
repo.save_snapshots(snapshots)?;
_ = repo.save_snapshots(snapshots)?;
repo.delete_snapshots(&old_snap_ids)?;
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/commands/tui/snapshots.rs
Original file line number Diff line number Diff line change
Expand Up @@ -695,7 +695,7 @@ impl<'a, P: ProgressBars, S: IndexedFull> Snapshots<'a, P, S> {
.zip(self.snaps_status.iter())
.filter_map(|(snap, status)| status.to_forget.then_some(snap.id));
let delete_ids: Vec<_> = old_snap_ids.chain(snap_ids_to_forget).collect();
self.repo.save_snapshots(save_snaps)?;
_ = self.repo.save_snapshots(save_snaps)?;
self.repo.delete_snapshots(&delete_ids)?;
// re-read snapshots
self.reread()
Expand Down

0 comments on commit db6ebc2

Please sign in to comment.