From 3d73e2014dc3412d086992a9307ef007dbc33c39 Mon Sep 17 00:00:00 2001 From: Alexander Weiss Date: Sun, 20 Nov 2022 00:11:51 +0100 Subject: [PATCH] cat/ls/restore: Add filtering for latest snapshot --- src/commands/cat.rs | 24 +++++++++++++++++++----- src/commands/ls.rs | 16 +++++++++++++--- src/commands/mod.rs | 6 +++--- src/commands/restore.rs | 16 +++++++++++++--- 4 files changed, 48 insertions(+), 14 deletions(-) diff --git a/src/commands/cat.rs b/src/commands/cat.rs index ab2c60e..87cf67e 100644 --- a/src/commands/cat.rs +++ b/src/commands/cat.rs @@ -5,11 +5,12 @@ use clap::{Parser, Subcommand}; use indicatif::ProgressBar; use super::progress_counter; +use super::rustic_config::RusticConfig; use crate::backend::{DecryptReadBackend, FileType}; use crate::blob::{BlobType, Tree}; use crate::id::Id; use crate::index::{IndexBackend, IndexedBackend}; -use crate::repo::SnapshotFile; +use crate::repo::{SnapshotFile, SnapshotFilter}; #[derive(Parser)] pub(super) struct Opts { @@ -41,12 +42,19 @@ struct IdOpt { #[derive(Parser)] struct TreeOpts { + #[clap(flatten, help_heading = "SNAPSHOT FILTER OPTIONS (when using latest)")] + filter: SnapshotFilter, + /// Snapshot/path of the tree to display #[clap(value_name = "SNAPSHOT[:PATH]")] snap: String, } -pub(super) fn execute(be: &impl DecryptReadBackend, opts: Opts) -> Result<()> { +pub(super) fn execute( + be: &impl DecryptReadBackend, + opts: Opts, + config_file: RusticConfig, +) -> Result<()> { match opts.command { Command::Config => cat_file(be, FileType::Config, IdOpt::default()), Command::Index(opt) => cat_file(be, FileType::Index, opt), @@ -55,7 +63,7 @@ pub(super) fn execute(be: &impl DecryptReadBackend, opts: Opts) -> Result<()> { Command::TreeBlob(opt) => cat_blob(be, BlobType::Tree, opt), Command::DataBlob(opt) => cat_blob(be, BlobType::Data, opt), // special treatment for cating a tree within a snapshot - Command::Tree(opts) => cat_tree(be, opts), + Command::Tree(opts) => cat_tree(be, opts, config_file), } } @@ -75,9 +83,15 @@ fn cat_blob(be: &impl DecryptReadBackend, tpe: BlobType, opt: IdOpt) -> Result<( Ok(()) } -fn cat_tree(be: &impl DecryptReadBackend, opts: TreeOpts) -> Result<()> { +fn cat_tree( + be: &impl DecryptReadBackend, + mut opts: TreeOpts, + config_file: RusticConfig, +) -> Result<()> { + config_file.merge_into("snapshot-filter", &mut opts.filter)?; + let (id, path) = opts.snap.split_once(':').unwrap_or((&opts.snap, "")); - let snap = SnapshotFile::from_str(be, id, |_| true, progress_counter(""))?; + let snap = SnapshotFile::from_str(be, id, |sn| sn.matches(&opts.filter), progress_counter(""))?; let index = IndexBackend::new(be, progress_counter(""))?; let id = Tree::subtree_id(&index, snap.tree, Path::new(path))?; let data = index.blob_from_backend(&BlobType::Tree, &id)?; diff --git a/src/commands/ls.rs b/src/commands/ls.rs index b87d07a..eb33ca7 100644 --- a/src/commands/ls.rs +++ b/src/commands/ls.rs @@ -3,21 +3,31 @@ use clap::Parser; use std::path::Path; use super::progress_counter; +use super::rustic_config::RusticConfig; use crate::backend::DecryptReadBackend; use crate::blob::{NodeStreamer, Tree}; use crate::index::IndexBackend; -use crate::repo::SnapshotFile; +use crate::repo::{SnapshotFile, SnapshotFilter}; #[derive(Parser)] pub(super) struct Opts { + #[clap(flatten, help_heading = "SNAPSHOT FILTER OPTIONS (when using latest)")] + filter: SnapshotFilter, + /// Snapshot/path to list #[clap(value_name = "SNAPSHOT[:PATH]")] snap: String, } -pub(super) fn execute(be: &(impl DecryptReadBackend + Unpin), opts: Opts) -> Result<()> { +pub(super) fn execute( + be: &impl DecryptReadBackend, + mut opts: Opts, + config_file: RusticConfig, +) -> Result<()> { + config_file.merge_into("snapshot-filter", &mut opts.filter)?; + let (id, path) = opts.snap.split_once(':').unwrap_or((&opts.snap, "")); - let snap = SnapshotFile::from_str(be, id, |_| true, progress_counter(""))?; + let snap = SnapshotFile::from_str(be, id, |sn| sn.matches(&opts.filter), progress_counter(""))?; let index = IndexBackend::new(be, progress_counter(""))?; let tree = Tree::subtree_id(&index, snap.tree, Path::new(path))?; diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 09c1245..12cf897 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -309,7 +309,7 @@ pub fn execute() -> Result<()> { match cmd { Command::Backup(opts) => backup::execute(&dbe, opts, config, config_file, command)?, Command::Config(opts) => config::execute(&dbe, &be_hot, opts, config)?, - Command::Cat(opts) => cat::execute(&dbe, opts)?, + Command::Cat(opts) => cat::execute(&dbe, opts, config_file)?, Command::Check(opts) => check::execute(&dbe, &cache, &be_hot, &be, opts)?, Command::Completions(_) => {} // already handled above Command::Diff(opts) => diff::execute(&dbe, opts)?, @@ -317,11 +317,11 @@ pub fn execute() -> Result<()> { Command::Init(_) => {} // already handled above Command::Key(opts) => key::execute(&dbe, key, opts)?, Command::List(opts) => list::execute(&dbe, opts)?, - Command::Ls(opts) => ls::execute(&dbe, opts)?, + Command::Ls(opts) => ls::execute(&dbe, opts, config_file)?, Command::SelfUpdate(_) => {} // already handled above Command::Snapshots(opts) => snapshots::execute(&dbe, opts, config_file)?, Command::Prune(opts) => prune::execute(&dbe, cache, opts, config, vec![])?, - Command::Restore(opts) => restore::execute(&dbe, opts)?, + Command::Restore(opts) => restore::execute(&dbe, opts, config_file)?, Command::Repair(opts) => repair::execute(&dbe, opts, config_file, &config)?, Command::Repoinfo(opts) => repoinfo::execute(&dbe, &be_hot, opts)?, Command::Tag(opts) => tag::execute(&dbe, opts, config_file)?, diff --git a/src/commands/restore.rs b/src/commands/restore.rs index df88a8a..37f4c71 100644 --- a/src/commands/restore.rs +++ b/src/commands/restore.rs @@ -11,6 +11,7 @@ use ignore::{DirEntry, WalkBuilder}; use log::*; use rayon::ThreadPoolBuilder; +use super::rustic_config::RusticConfig; use super::{bytes, progress_bytes, progress_counter, wait, warm_up, warm_up_command}; use crate::backend::{DecryptReadBackend, FileType, LocalBackend}; use crate::blob::{Node, NodeStreamer, NodeType, Tree}; @@ -18,11 +19,14 @@ use crate::commands::helpers::progress_spinner; use crate::crypto::hash; use crate::id::Id; use crate::index::{IndexBackend, IndexedBackend}; -use crate::repo::SnapshotFile; +use crate::repo::{SnapshotFile, SnapshotFilter}; #[derive(Parser)] #[clap(global_setting(AppSettings::DeriveDisplayOrder))] pub(super) struct Opts { + #[clap(flatten, help_heading = "SNAPSHOT FILTER OPTIONS (when using latest)")] + filter: SnapshotFilter, + /// Dry-run: don't restore, only show what would be done #[clap(long, short = 'n')] dry_run: bool, @@ -57,7 +61,13 @@ pub(super) struct Opts { dest: String, } -pub(super) fn execute(be: &(impl DecryptReadBackend + Unpin), opts: Opts) -> Result<()> { +pub(super) fn execute( + be: &(impl DecryptReadBackend + Unpin), + mut opts: Opts, + config_file: RusticConfig, +) -> Result<()> { + config_file.merge_into("snapshot-filter", &mut opts.filter)?; + if let Some(command) = &opts.warm_up_command { if !command.contains("%id") { bail!("warm-up command must contain %id!") @@ -66,7 +76,7 @@ pub(super) fn execute(be: &(impl DecryptReadBackend + Unpin), opts: Opts) -> Res } let (id, path) = opts.snap.split_once(':').unwrap_or((&opts.snap, "")); - let snap = SnapshotFile::from_str(be, id, |_| true, progress_counter(""))?; + let snap = SnapshotFile::from_str(be, id, |sn| sn.matches(&opts.filter), progress_counter(""))?; let index = IndexBackend::new(be, progress_counter(""))?; let tree = Tree::subtree_id(&index, snap.tree, Path::new(path))?;