From ceb89c6fda9c2195436efb1a31a141604f1d95f8 Mon Sep 17 00:00:00 2001 From: Alexander Weiss Date: Sun, 18 Jun 2023 22:29:05 +0200 Subject: [PATCH 1/3] rustic_core: Add NoProgress and NoProgressBars (e.g. for examples) --- crates/rustic_core/src/lib.rs | 2 +- crates/rustic_core/src/progress.rs | 54 +++++++++++++++++++++++++++++- 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/crates/rustic_core/src/lib.rs b/crates/rustic_core/src/lib.rs index b7d5389e4..d4f082c70 100644 --- a/crates/rustic_core/src/lib.rs +++ b/crates/rustic_core/src/lib.rs @@ -130,7 +130,7 @@ pub use crate::{ indexer::Indexer, IndexBackend, IndexEntry, IndexedBackend, ReadIndex, }, - progress::{Progress, ProgressBars}, + progress::{NoProgress, NoProgressBars, Progress, ProgressBars}, repofile::{ configfile::ConfigFile, indexfile::{IndexBlob, IndexFile, IndexPack}, diff --git a/crates/rustic_core/src/progress.rs b/crates/rustic_core/src/progress.rs index fdd9af51a..2ba552420 100644 --- a/crates/rustic_core/src/progress.rs +++ b/crates/rustic_core/src/progress.rs @@ -1,17 +1,69 @@ use std::borrow::Cow; +use log::info; + +/// Trait to report progress information for any rustic action which supports that. +/// Implement this trait when you want to display this progress to your users. pub trait Progress: Send + Sync + Clone { + /// Check if progress is hidden fn is_hidden(&self) -> bool; + /// Set total length for this progress fn set_length(&self, len: u64); + /// Set title for this progress fn set_title(&self, title: &'static str); + /// Advance progress by given increment fn inc(&self, inc: u64); + /// Finish the progress fn finish(&self); } +/// Trait to start progress information report progress information for any rustic action which supports that. +/// Implement this trait when you want to display this progress to your users. pub trait ProgressBars { type P: Progress; + /// Start a new progress, which is hidden + fn progress_hidden(&self) -> Self::P; + /// Start a new progress spinner. Note that this progress doesn't get a length and is not advanced, only finished. fn progress_spinner(&self, prefix: impl Into>) -> Self::P; + /// Start a new progress which counts something fn progress_counter(&self, prefix: impl Into>) -> Self::P; - fn progress_hidden(&self) -> Self::P; + /// Start a new progress which counts bytes fn progress_bytes(&self, prefix: impl Into>) -> Self::P; } + +#[derive(Clone, Copy, Debug)] +pub struct NoProgress; +impl Progress for NoProgress { + fn is_hidden(&self) -> bool { + true + } + fn set_length(&self, _len: u64) {} + fn set_title(&self, title: &'static str) { + info!("{title}"); + } + fn inc(&self, _inc: u64) {} + fn finish(&self) { + info!("finished."); + } +} + +#[derive(Clone, Copy, Debug)] +pub struct NoProgressBars; +impl ProgressBars for NoProgressBars { + type P = NoProgress; + fn progress_spinner(&self, prefix: impl Into>) -> Self::P { + info!("{}", prefix.into()); + NoProgress + } + fn progress_counter(&self, prefix: impl Into>) -> Self::P { + info!("{}", prefix.into()); + NoProgress + } + fn progress_hidden(&self) -> Self::P { + NoProgress + } + fn progress_bytes(&self, prefix: impl Into>) -> Self::P { + info!("{}", prefix.into()); + NoProgress + } +} From 0317ed59051988834e391a788160b62a9e1bbe0e Mon Sep 17 00:00:00 2001 From: Alexander Weiss Date: Mon, 19 Jun 2023 06:44:11 +0200 Subject: [PATCH 2/3] Add check example --- Cargo.lock | 1 + crates/rustic_core/Cargo.toml | 1 + crates/rustic_core/examples/check.rs | 19 +++++++++++++++++++ crates/rustic_core/src/commands/check.rs | 6 +++--- crates/rustic_core/src/repository.rs | 16 ++++++++-------- 5 files changed, 32 insertions(+), 11 deletions(-) create mode 100644 crates/rustic_core/examples/check.rs diff --git a/Cargo.lock b/Cargo.lock index ef7035999..2dc4c4521 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2440,6 +2440,7 @@ dependencies = [ "serde_json", "serde_with", "sha2", + "simplelog", "thiserror", "url", "walkdir", diff --git a/crates/rustic_core/Cargo.toml b/crates/rustic_core/Cargo.toml index f66a2c78e..ca64b6fc1 100644 --- a/crates/rustic_core/Cargo.toml +++ b/crates/rustic_core/Cargo.toml @@ -123,6 +123,7 @@ expect-test = "1.4.1" quickcheck = { workspace = true } quickcheck_macros = { workspace = true } pretty_assertions = { workspace = true } +simplelog = { workspace = true } [profile.dev] opt-level = 0 diff --git a/crates/rustic_core/examples/check.rs b/crates/rustic_core/examples/check.rs new file mode 100644 index 000000000..c924474e7 --- /dev/null +++ b/crates/rustic_core/examples/check.rs @@ -0,0 +1,19 @@ +//! `check` example +use rustic_core::{CheckOpts, NoProgressBars, Repository, RepositoryOptions}; +use simplelog::{Config, LevelFilter, SimpleLogger}; + +fn main() { + // Display info logs + let _ = SimpleLogger::init(LevelFilter::Info, Config::default()); + + // Open repository + let mut repo_opts = RepositoryOptions::default(); + repo_opts.repository = Some("/tmp/repo".to_string()); + repo_opts.password = Some("test".to_string()); + let repo = Repository::new(&repo_opts).unwrap().open().unwrap(); + + // Check respository with standard options + let opts = CheckOpts::default(); + let progress = NoProgressBars {}; + repo.check(opts, &progress).unwrap() +} diff --git a/crates/rustic_core/src/commands/check.rs b/crates/rustic_core/src/commands/check.rs index 1f9d6b73d..0832bbd8e 100644 --- a/crates/rustic_core/src/commands/check.rs +++ b/crates/rustic_core/src/commands/check.rs @@ -16,15 +16,15 @@ use crate::{ /// `check` subcommand #[cfg_attr(feature = "clap", derive(clap::Parser))] -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, Default)] pub struct CheckOpts { /// Don't verify the data saved in the cache #[cfg_attr(feature = "clap", clap(long, conflicts_with = "no_cache"))] - trust_cache: bool, + pub trust_cache: bool, /// Read all data blobs #[cfg_attr(feature = "clap", clap(long))] - read_data: bool, + pub read_data: bool, } impl CheckOpts { diff --git a/crates/rustic_core/src/repository.rs b/crates/rustic_core/src/repository.rs index 11d44ffde..d5f3380da 100644 --- a/crates/rustic_core/src/repository.rs +++ b/crates/rustic_core/src/repository.rs @@ -79,19 +79,19 @@ pub struct RepositoryOptions { feature = "clap", clap(short, long, global = true, alias = "repo", env = "RUSTIC_REPOSITORY") )] - repository: Option, + pub repository: Option, /// Repository to use as hot storage #[cfg_attr( feature = "clap", clap(long, global = true, alias = "repository_hot", env = "RUSTIC_REPO_HOT") )] - repo_hot: Option, + pub repo_hot: Option, /// Password of the repository - WARNING: Using --password can reveal the password in the process list! #[cfg_attr(feature = "clap", clap(long, global = true, env = "RUSTIC_PASSWORD"))] // TODO: use `secrecy` library - password: Option, + pub password: Option, /// File to read the password from #[cfg_attr( @@ -104,7 +104,7 @@ pub struct RepositoryOptions { conflicts_with = "password" ) )] - password_file: Option, + pub password_file: Option, /// Command to read the password from #[cfg_attr(feature = "clap", clap( @@ -113,12 +113,12 @@ pub struct RepositoryOptions { env = "RUSTIC_PASSWORD_COMMAND", conflicts_with_all = &["password", "password_file"], ))] - password_command: Option, + pub password_command: Option, /// Don't use a cache. #[cfg_attr(feature = "clap", clap(long, global = true, env = "RUSTIC_NO_CACHE"))] #[cfg_attr(feature = "merge", merge(strategy = merge::bool::overwrite_false))] - no_cache: bool, + pub no_cache: bool, /// Use this dir as cache dir instead of the standard cache dir #[cfg_attr( @@ -130,7 +130,7 @@ pub struct RepositoryOptions { env = "RUSTIC_CACHE_DIR" ) )] - cache_dir: Option, + pub cache_dir: Option, /// Warm up needed data pack files by only requesting them without processing #[cfg_attr(feature = "clap", clap(long, global = true))] @@ -151,7 +151,7 @@ pub struct RepositoryOptions { #[cfg_attr(feature = "clap", clap(skip))] #[cfg_attr(feature = "merge", merge(strategy = overwrite))] - options: HashMap, + pub options: HashMap, } // TODO: Unused function From 2342c5bb483cf47060e5bec6f90eb0b1baeaac95 Mon Sep 17 00:00:00 2001 From: Alexander Weiss Date: Tue, 20 Jun 2023 21:36:14 +0200 Subject: [PATCH 3/3] put ProgressBars into Repository struct --- crates/rustic_core/examples/check.rs | 5 ++- crates/rustic_core/src/commands/cat.rs | 11 +++---- crates/rustic_core/src/commands/check.rs | 3 +- crates/rustic_core/src/repository.rs | 39 +++++++++++++++--------- src/commands.rs | 9 +++--- src/commands/cat.rs | 14 +++------ src/commands/check.rs | 3 +- src/commands/prune.rs | 4 +-- src/helpers.rs | 12 ++++---- 9 files changed, 52 insertions(+), 48 deletions(-) diff --git a/crates/rustic_core/examples/check.rs b/crates/rustic_core/examples/check.rs index c924474e7..44a603620 100644 --- a/crates/rustic_core/examples/check.rs +++ b/crates/rustic_core/examples/check.rs @@ -1,5 +1,5 @@ //! `check` example -use rustic_core::{CheckOpts, NoProgressBars, Repository, RepositoryOptions}; +use rustic_core::{CheckOpts, Repository, RepositoryOptions}; use simplelog::{Config, LevelFilter, SimpleLogger}; fn main() { @@ -14,6 +14,5 @@ fn main() { // Check respository with standard options let opts = CheckOpts::default(); - let progress = NoProgressBars {}; - repo.check(opts, &progress).unwrap() + repo.check(opts).unwrap() } diff --git a/crates/rustic_core/src/commands/cat.rs b/crates/rustic_core/src/commands/cat.rs index f558379b1..644afc175 100644 --- a/crates/rustic_core/src/commands/cat.rs +++ b/crates/rustic_core/src/commands/cat.rs @@ -8,31 +8,30 @@ use crate::{ Tree, }; -pub fn cat_file(repo: &OpenRepository, tpe: FileType, id: &str) -> RusticResult { +pub fn cat_file

(repo: &OpenRepository

, tpe: FileType, id: &str) -> RusticResult { let id = repo.dbe.find_id(tpe, id)?; let data = repo.dbe.read_encrypted_full(tpe, &id)?; Ok(data) } -pub fn cat_blob(repo: &IndexedRepository, tpe: BlobType, id: &str) -> RusticResult { +pub fn cat_blob

(repo: &IndexedRepository

, tpe: BlobType, id: &str) -> RusticResult { let id = Id::from_hex(id)?; let data = repo.index.blob_from_backend(tpe, &id)?; Ok(data) } -pub fn cat_tree( - repo: &IndexedRepository, +pub fn cat_tree( + repo: &IndexedRepository

, snap: &str, sn_filter: impl FnMut(&SnapshotFile) -> bool + Send + Sync, - pb: &impl ProgressBars, ) -> RusticResult { let (id, path) = snap.split_once(':').unwrap_or((snap, "")); let snap = SnapshotFile::from_str( &repo.repo.dbe, id, sn_filter, - &pb.progress_counter("getting snapshot..."), + &repo.repo.pb.progress_counter("getting snapshot..."), )?; let node = Tree::node_from_path(&repo.index, snap.tree, Path::new(path))?; let id = node diff --git a/crates/rustic_core/src/commands/check.rs b/crates/rustic_core/src/commands/check.rs index 0832bbd8e..d354218e7 100644 --- a/crates/rustic_core/src/commands/check.rs +++ b/crates/rustic_core/src/commands/check.rs @@ -28,11 +28,12 @@ pub struct CheckOpts { } impl CheckOpts { - pub fn run(self, repo: &OpenRepository, pb: &impl ProgressBars) -> RusticResult<()> { + pub fn run(self, repo: &OpenRepository

) -> RusticResult<()> { let be = &repo.dbe; let cache = &repo.cache; let hot_be = &repo.be_hot; let raw_be = &repo.be; + let pb = &repo.pb; if !self.trust_cache { if let Some(cache) = &cache { for file_type in [FileType::Snapshot, FileType::Index] { diff --git a/crates/rustic_core/src/repository.rs b/crates/rustic_core/src/repository.rs index d5f3380da..973b18fbf 100644 --- a/crates/rustic_core/src/repository.rs +++ b/crates/rustic_core/src/repository.rs @@ -34,7 +34,7 @@ use crate::{ error::RepositoryErrorKind, index::IndexEntry, repofile::{configfile::ConfigFile, indexfile::IndexPack, keyfile::find_key_in_backend}, - BlobType, IndexBackend, ProgressBars, RusticResult, SnapshotFile, + BlobType, IndexBackend, NoProgressBars, ProgressBars, RusticResult, SnapshotFile, }; pub(super) mod constants { @@ -196,15 +196,22 @@ pub(crate) fn read_password_from_reader(file: &mut impl BufRead) -> RusticResult } #[derive(Debug)] -pub struct Repository { +pub struct Repository

{ name: String, pub be: HotColdBackend, pub be_hot: Option, opts: RepositoryOptions, + pb: P, } -impl Repository { +impl Repository { pub fn new(opts: &RepositoryOptions) -> RusticResult { + Self::new_with_progress(opts, NoProgressBars {}) + } +} + +impl

Repository

{ + pub fn new_with_progress(opts: &RepositoryOptions, pb: P) -> RusticResult { let be = match &opts.repository { Some(repo) => ChooseBackend::from_url(repo)?, None => return Err(RepositoryErrorKind::NoRepositoryGiven.into()), @@ -238,6 +245,7 @@ impl Repository { be, be_hot, opts: opts.clone(), + pb, }) } @@ -276,7 +284,7 @@ impl Repository { } } - pub fn open(self) -> RusticResult { + pub fn open(self) -> RusticResult> { let config_ids = match self.be.list(FileType::Config) { Ok(val) => val, Err(_e) => return Err(RepositoryErrorKind::ListingRepositoryConfigFileFailed.into()), @@ -329,6 +337,7 @@ impl Repository { be_hot: self.be_hot, config, opts: self.opts, + pb: self.pb, }) } } @@ -357,7 +366,7 @@ pub(crate) fn get_key(be: &impl ReadBackend, password: Option) -> Rustic } #[derive(Debug)] -pub struct OpenRepository { +pub struct OpenRepository

{ pub name: String, pub be: HotColdBackend, pub be_hot: Option, @@ -366,31 +375,32 @@ pub struct OpenRepository { pub dbe: DecryptBackend>, Key>, pub config: ConfigFile, pub opts: RepositoryOptions, + pub(crate) pb: P, } -impl OpenRepository { +impl OpenRepository

{ pub fn cat_file(&self, tpe: FileType, id: &str) -> RusticResult { commands::cat::cat_file(self, tpe, id) } - pub fn check(&self, opts: CheckOpts, pb: &impl ProgressBars) -> RusticResult<()> { - opts.run(self, pb) + pub fn check(&self, opts: CheckOpts) -> RusticResult<()> { + opts.run(self) } - pub fn to_indexed(self, pb: &impl ProgressBars) -> RusticResult { - let index = IndexBackend::new(&self.dbe, &pb.progress_counter(""))?; + pub fn to_indexed(self) -> RusticResult> { + let index = IndexBackend::new(&self.dbe, &self.pb.progress_counter(""))?; Ok(IndexedRepository { repo: self, index }) } } #[derive(Debug)] -pub struct IndexedRepository { - pub(crate) repo: OpenRepository, +pub struct IndexedRepository

{ + pub(crate) repo: OpenRepository

, pub(crate) index: IndexBackend>, Key>>, } -impl IndexedRepository { +impl IndexedRepository

{ pub fn cat_blob(&self, tpe: BlobType, id: &str) -> RusticResult { commands::cat::cat_blob(self, tpe, id) } @@ -398,8 +408,7 @@ impl IndexedRepository { &self, snap: &str, sn_filter: impl FnMut(&SnapshotFile) -> bool + Send + Sync, - pb: &impl ProgressBars, ) -> RusticResult { - commands::cat::cat_tree(self, snap, sn_filter, pb) + commands::cat::cat_tree(self, snap, sn_filter) } } diff --git a/src/commands.rs b/src/commands.rs index 56a1690b7..731284c52 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -34,7 +34,7 @@ use crate::{ repair::RepairCmd, repoinfo::RepoInfoCmd, restore::RestoreCmd, self_update::SelfUpdateCmd, show_config::ShowConfigCmd, snapshots::SnapshotCmd, tag::TagCmd, }, - config::RusticConfig, + config::{progress_options::ProgressOptions, RusticConfig}, {Application, RUSTIC_APP}, }; @@ -167,7 +167,7 @@ impl Configurable for EntryPoint { } } -fn open_repository(repo: Repository) -> OpenRepository { +fn open_repository

(repo: Repository

) -> OpenRepository

{ match repo.open() { Ok(it) => it, Err(err) => { @@ -177,8 +177,9 @@ fn open_repository(repo: Repository) -> OpenRepository { } } -fn get_repository(config: &Arc) -> Repository { - match Repository::new(&config.repository) { +fn get_repository(config: &Arc) -> Repository { + let po = config.global.progress_options; + match Repository::new_with_progress(&config.repository, po) { Ok(it) => it, Err(err) => { status_err!("{}", err); diff --git a/src/commands/cat.rs b/src/commands/cat.rs index 3628b1137..d590df30f 100644 --- a/src/commands/cat.rs +++ b/src/commands/cat.rs @@ -61,8 +61,6 @@ impl Runnable for CatCmd { impl CatCmd { fn inner_run(&self) -> Result<()> { let config = RUSTIC_APP.config(); - let po = config.global.progress_options; - let repo = open_repository(get_repository(&config)); let data = match &self.cmd { @@ -70,14 +68,12 @@ impl CatCmd { CatSubCmd::Index(opt) => repo.cat_file(FileType::Index, &opt.id)?, CatSubCmd::Snapshot(opt) => repo.cat_file(FileType::Snapshot, &opt.id)?, // special treatment for cating blobs: read the index and use it to locate the blob - CatSubCmd::TreeBlob(opt) => repo.to_indexed(&po)?.cat_blob(BlobType::Tree, &opt.id)?, - CatSubCmd::DataBlob(opt) => repo.to_indexed(&po)?.cat_blob(BlobType::Data, &opt.id)?, + CatSubCmd::TreeBlob(opt) => repo.to_indexed()?.cat_blob(BlobType::Tree, &opt.id)?, + CatSubCmd::DataBlob(opt) => repo.to_indexed()?.cat_blob(BlobType::Data, &opt.id)?, // special treatment for cating a tree within a snapshot - CatSubCmd::Tree(opt) => repo.to_indexed(&po)?.cat_tree( - &opt.snap, - |sn| config.snapshot_filter.matches(sn), - &po, - )?, + CatSubCmd::Tree(opt) => repo + .to_indexed()? + .cat_tree(&opt.snap, |sn| config.snapshot_filter.matches(sn))?, }; println!("{}", String::from_utf8(data.to_vec())?); diff --git a/src/commands/check.rs b/src/commands/check.rs index 4bf8faadf..5cdd90c9e 100644 --- a/src/commands/check.rs +++ b/src/commands/check.rs @@ -21,10 +21,9 @@ pub(crate) struct CheckCmd { impl Runnable for CheckCmd { fn run(&self) { let config = RUSTIC_APP.config(); - let progress_options = config.global.progress_options; let repo = open_repository(get_repository(&config)); - if let Err(err) = repo.check(self.opts, &progress_options) { + if let Err(err) = repo.check(self.opts) { status_err!("{}", err); RUSTIC_APP.shutdown(Shutdown::Crash); }; diff --git a/src/commands/prune.rs b/src/commands/prune.rs index 607c2bb7b..f8feae42c 100644 --- a/src/commands/prune.rs +++ b/src/commands/prune.rs @@ -849,9 +849,9 @@ impl Pruner { } #[allow(clippy::significant_drop_tightening)] - fn do_prune( + fn do_prune

( self, - repo: OpenRepository, + repo: OpenRepository

, opts: &PruneCmd, progress_options: &ProgressOptions, ) -> Result<()> { diff --git a/src/helpers.rs b/src/helpers.rs index e50347389..8101a2ce4 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -25,8 +25,8 @@ pub(super) mod constants { pub(super) const MAX_READER_THREADS_NUM: usize = 20; } -pub(crate) fn warm_up_wait( - repo: &OpenRepository, +pub(crate) fn warm_up_wait

( + repo: &OpenRepository

, packs: impl ExactSizeIterator, wait: bool, progress_options: &ProgressOptions, @@ -97,10 +97,10 @@ pub(crate) fn warm_up( Ok(()) } -pub(crate) fn copy( +pub(crate) fn copy

( snapshots: &[SnapshotFile], index: &impl IndexedBackend, - repo_dest: &OpenRepository, + repo_dest: &OpenRepository

, ) -> Result<()> { let config = RUSTIC_APP.config(); let be_dest = &repo_dest.dbe; @@ -201,9 +201,9 @@ pub(crate) fn copy( Ok(()) } -pub(crate) fn relevant_snapshots( +pub(crate) fn relevant_snapshots( snaps: &[SnapshotFile], - dest_repo: &OpenRepository, + dest_repo: &OpenRepository

, filter: F, p: &impl Progress, ) -> Result>