From a06546e159a275398f270f00db060c667aeb0597 Mon Sep 17 00:00:00 2001 From: Alexander Weiss Date: Thu, 1 Sep 2022 13:25:53 +0200 Subject: [PATCH] snapshots: summarize snapshots with identical trees --- src/commands/snapshots.rs | 44 +++++++++++++++++++++++++-------------- src/repo/snapshotfile.rs | 4 ++-- 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/src/commands/snapshots.rs b/src/commands/snapshots.rs index d2aac36..4f6d4c9 100644 --- a/src/commands/snapshots.rs +++ b/src/commands/snapshots.rs @@ -3,6 +3,7 @@ use std::time::Duration; use anyhow::Result; use clap::Parser; use humantime::format_duration; +use itertools::Itertools; use prettytable::{format, row, Table}; use super::bytes; @@ -24,6 +25,10 @@ pub(super) struct Opts { #[clap(long)] long: bool, + /// Show all snapshots instead of summarizing identical follow-up snapshots + #[clap(long)] + all: bool, + /// Snapshots to show #[clap(value_name = "ID")] ids: Vec, @@ -63,24 +68,31 @@ pub(super) async fn execute(be: &impl DecryptReadBackend, opts: Opts) -> Result< display_snap(snap); } } else { + let snap_to_table = |(sn, count): (SnapshotFile, usize)| { + let tags = sn.tags.formatln(); + let paths = sn.paths.formatln(); + let time = sn.time.format("%Y-%m-%d %H:%M:%S"); + let (files, dirs, size) = match &sn.summary { + Some(s) => ( + s.total_files_processed.to_string(), + s.total_dirs_processed.to_string(), + bytes(s.total_bytes_processed), + ), + None => ("?".to_string(), "?".to_string(), "?".to_string()), + }; + let id = match count { + 0 => format!("{}", sn.id), + count => format!("{} (+{})", sn.id, count), + }; + row![id, time, sn.hostname, tags, paths, r->files, r->dirs, r->size] + }; + let mut table: Table = snapshots .into_iter() - .map(|sn| { - let tags = sn.tags.formatln(); - let paths = sn.paths.formatln(); - let time = sn.time.format("%Y-%m-%d %H:%M:%S"); - let (files, dirs, size) = sn - .summary - .map(|s| { - ( - s.total_files_processed.to_string(), - s.total_dirs_processed.to_string(), - bytes(s.total_bytes_processed), - ) - }) - .unwrap_or_else(|| ("?".to_string(), "?".to_string(), "?".to_string())); - row![sn.id, time, sn.hostname, tags, paths, r->files, r->dirs, r->size] - }) + .group_by(|sn| if opts.all { sn.id } else { sn.tree }) + .into_iter() + .map(|(_, mut g)| (g.next().unwrap(), g.count())) + .map(snap_to_table) .collect(); table.set_titles( row![b->"ID", b->"Time", b->"Host", b->"Tags", b->"Paths", br->"Files",br->"Dirs", br->"Size"], diff --git a/src/repo/snapshotfile.rs b/src/repo/snapshotfile.rs index 51a6023..8b0ad88 100644 --- a/src/repo/snapshotfile.rs +++ b/src/repo/snapshotfile.rs @@ -15,7 +15,7 @@ use crate::backend::{DecryptReadBackend, FileType, RepoFile}; /// This is an extended version of the summaryOutput structure of restic in /// restic/internal/ui/backup$/json.go -#[derive(Debug, Serialize, Deserialize, Derivative)] +#[derive(Debug, Clone, Serialize, Deserialize, Derivative)] #[derivative(Default)] pub struct SnapshotSummary { pub files_new: u64, @@ -61,7 +61,7 @@ impl DeleteOption { } } -#[derive(Debug, Serialize, Deserialize, Derivative)] +#[derive(Debug, Clone, Serialize, Deserialize, Derivative)] #[derivative(Default)] pub struct SnapshotFile { #[derivative(Default(value = "Local::now()"))]