diff --git a/crates/rustic_core/src/commands.rs b/crates/rustic_core/src/commands.rs index 2a32f7562..100d85284 100644 --- a/crates/rustic_core/src/commands.rs +++ b/crates/rustic_core/src/commands.rs @@ -2,3 +2,4 @@ pub mod cat; pub mod check; pub mod prune; pub mod repoinfo; +pub mod snapshots; diff --git a/crates/rustic_core/src/commands/snapshots.rs b/crates/rustic_core/src/commands/snapshots.rs new file mode 100644 index 000000000..3d9211be1 --- /dev/null +++ b/crates/rustic_core/src/commands/snapshots.rs @@ -0,0 +1,39 @@ +//! `smapshot` subcommand + +use crate::{ + OpenRepository, ProgressBars, RusticResult, SnapshotFile, SnapshotGroup, SnapshotGroupCriterion, +}; + +pub(crate) fn get_snapshot_group( + repo: &OpenRepository

, + ids: &[String], + group_by: SnapshotGroupCriterion, + filter: impl FnMut(&SnapshotFile) -> bool, +) -> RusticResult)>> { + let pb = &repo.pb; + let p = pb.progress_counter("getting snapshots..."); + let groups = match ids { + [] => SnapshotFile::group_from_backend(&repo.dbe, filter, group_by, &p)?, + [id] if id == "latest" => { + SnapshotFile::group_from_backend(&repo.dbe, filter, group_by, &p)? + .into_iter() + .map(|(group, mut snaps)| { + snaps.sort_unstable(); + let last_idx = snaps.len() - 1; + snaps.swap(0, last_idx); + snaps.truncate(1); + (group, snaps) + }) + .collect::>() + } + _ => { + let item = ( + SnapshotGroup::default(), + SnapshotFile::from_ids(&repo.dbe, ids, &p)?, + ); + vec![item] + } + }; + + Ok(groups) +} diff --git a/crates/rustic_core/src/repofile/snapshotfile.rs b/crates/rustic_core/src/repofile/snapshotfile.rs index f6f9fe469..5a5fc905f 100644 --- a/crates/rustic_core/src/repofile/snapshotfile.rs +++ b/crates/rustic_core/src/repofile/snapshotfile.rs @@ -341,7 +341,7 @@ impl SnapshotFile { pub fn group_from_backend( be: &B, filter: F, - crit: &SnapshotGroupCriterion, + crit: SnapshotGroupCriterion, p: &impl Progress, ) -> RusticResult)>> where @@ -349,7 +349,7 @@ impl SnapshotFile { F: FnMut(&Self) -> bool, { let mut snaps = Self::all_from_backend(be, filter, p)?; - snaps.sort_unstable_by(|sn1, sn2| sn1.cmp_group(*crit, sn2)); + snaps.sort_unstable_by(|sn1, sn2| sn1.cmp_group(crit, sn2)); let mut result = Vec::new(); for (group, snaps) in &snaps @@ -528,7 +528,7 @@ impl Display for SnapshotGroup { impl SnapshotGroup { #[must_use] - pub fn from_sn(sn: &SnapshotFile, crit: &SnapshotGroupCriterion) -> Self { + pub fn from_sn(sn: &SnapshotFile, crit: SnapshotGroupCriterion) -> Self { Self { hostname: crit.hostname.then(|| sn.hostname.clone()), label: crit.label.then(|| sn.label.clone()), diff --git a/crates/rustic_core/src/repository.rs b/crates/rustic_core/src/repository.rs index f5f640f2f..7f530d05e 100644 --- a/crates/rustic_core/src/repository.rs +++ b/crates/rustic_core/src/repository.rs @@ -37,7 +37,7 @@ use crate::{ error::RepositoryErrorKind, repofile::{configfile::ConfigFile, keyfile::find_key_in_backend}, BlobType, IndexBackend, NoProgressBars, ProgressBars, PruneOpts, PrunePlan, RusticResult, - SnapshotFile, + SnapshotFile, SnapshotGroup, SnapshotGroupCriterion, }; pub(super) mod constants { @@ -373,6 +373,15 @@ pub struct OpenRepository

{ } impl OpenRepository

{ + pub fn get_snapshot_group( + &self, + ids: &[String], + group_by: SnapshotGroupCriterion, + filter: impl FnMut(&SnapshotFile) -> bool, + ) -> RusticResult)>> { + commands::snapshots::get_snapshot_group(self, ids, group_by, filter) + } + pub fn cat_file(&self, tpe: FileType, id: &str) -> RusticResult { commands::cat::cat_file(self, tpe, id) } diff --git a/src/commands/backup.rs b/src/commands/backup.rs index d9170cdb0..26293087b 100644 --- a/src/commands/backup.rs +++ b/src/commands/backup.rs @@ -252,7 +252,7 @@ impl BackupCmd { // get suitable snapshot group from snapshot and opts.group_by. This is used to filter snapshots for the parent detection let group = SnapshotGroup::from_sn( &snap, - &opts.group_by.unwrap_or_else(|| { + opts.group_by.unwrap_or_else(|| { SnapshotGroupCriterion::from_str("host,label,paths").unwrap() }), ); diff --git a/src/commands/forget.rs b/src/commands/forget.rs index a341c9fc3..94e8640aa 100644 --- a/src/commands/forget.rs +++ b/src/commands/forget.rs @@ -113,7 +113,7 @@ impl ForgetCmd { SnapshotFile::group_from_backend( be, |sn| config.forget.filter.matches(sn), - &group_by, + group_by, &p, )? } else { diff --git a/src/commands/snapshots.rs b/src/commands/snapshots.rs index 485d5b0af..f1c3aed83 100644 --- a/src/commands/snapshots.rs +++ b/src/commands/snapshots.rs @@ -3,20 +3,18 @@ /// App-local prelude includes `app_reader()`/`app_writer()`/`app_config()` /// accessors along with logging macros. Customize as you see fit. use crate::{ - commands::{get_repository, open_repository}, + commands::get_repository, helpers::{bold_cell, bytes_size_to_string, table, table_right_from}, status_err, Application, RUSTIC_APP, }; +use abscissa_core::{Command, Runnable, Shutdown}; +use anyhow::Result; use comfy_table::Cell; use humantime::format_duration; - -use abscissa_core::{Command, Runnable, Shutdown}; - use itertools::Itertools; -use rustic_core::DeleteOption; -use rustic_core::{ProgressBars, SnapshotFile, SnapshotGroup, SnapshotGroupCriterion}; +use rustic_core::{DeleteOption, SnapshotFile, SnapshotGroupCriterion}; /// `snapshot` subcommand #[derive(clap::Parser, Command, Debug)] @@ -56,42 +54,13 @@ impl Runnable for SnapshotCmd { } impl SnapshotCmd { - fn inner_run(&self) -> anyhow::Result<()> { + fn inner_run(&self) -> Result<()> { let config = RUSTIC_APP.config(); + let repo = get_repository(&config).open()?; - let repo = open_repository(get_repository(&config)); - - let p = config.global.progress_options.progress_hidden(); - let groups = match &self.ids[..] { - [] => SnapshotFile::group_from_backend( - &repo.dbe, - |sn| config.snapshot_filter.matches(sn), - &self.group_by, - &p, - )?, - [id] if id == "latest" => SnapshotFile::group_from_backend( - &repo.dbe, - |sn| config.snapshot_filter.matches(sn), - &self.group_by, - &p, - )? - .into_iter() - .map(|(group, mut snaps)| { - snaps.sort_unstable(); - let last_idx = snaps.len() - 1; - snaps.swap(0, last_idx); - snaps.truncate(1); - (group, snaps) - }) - .collect::>(), - _ => { - let item = ( - SnapshotGroup::default(), - SnapshotFile::from_ids(&repo.dbe, &self.ids, &p)?, - ); - vec![item] - } - }; + let groups = repo.get_snapshot_group(&self.ids, self.group_by, |sn| { + config.snapshot_filter.matches(sn) + })?; if self.json { let mut stdout = std::io::stdout(); @@ -99,6 +68,7 @@ impl SnapshotCmd { return Ok(()); } + let mut total_count = 0; for (group, mut snapshots) in groups { if !group.is_empty() { println!("\nsnapshots for {group}"); @@ -160,7 +130,10 @@ impl SnapshotCmd { println!("{table}"); } println!("{count} snapshot(s)"); + total_count += count; } + println!(); + println!("total: {total_count} snapshot(s)"); Ok(()) } diff --git a/tests/backup_restore.rs b/tests/backup_restore.rs index 6ecd1d1df..9c9e82fa2 100644 --- a/tests/backup_restore.rs +++ b/tests/backup_restore.rs @@ -77,10 +77,10 @@ fn test_backup_and_check_passes() -> TestResult<()> { let mut output = String::new(); cmd.stdout().read_to_string(&mut output)?; - let patterns = &["1 snapshot(s)"]; + let patterns = &["total: 1 snapshot(s)"]; let matches = get_matches(patterns, output)?; - assert_eq!(matches, vec![(PatternID::must(0), 13)]); + assert_eq!(matches, vec![(PatternID::must(0), 20)]); cmd.wait()?.expect_success(); } @@ -111,7 +111,7 @@ fn test_backup_and_check_passes() -> TestResult<()> { let mut output = String::new(); cmd.stdout().read_to_string(&mut output)?; - let patterns = &["2 snapshot(s)"]; + let patterns = &["total: 2 snapshot(s)"]; let matches = get_matches(patterns, output)?; assert_eq!(matches, vec![(PatternID::must(0), 13)]);