diff --git a/Cargo.lock b/Cargo.lock index 03626c8c2..bfea38313 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -822,6 +822,18 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "derive_setters" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e8ef033054e131169b8f0f9a7af8f5533a9436fadf3c500ed547f730f07090d" +dependencies = [ + "darling 0.20.1", + "proc-macro2", + "quote", + "syn 2.0.23", +] + [[package]] name = "dialoguer" version = "0.10.4" @@ -2314,6 +2326,7 @@ dependencies = [ "crossbeam-channel", "derivative", "derive_more", + "derive_setters", "dialoguer", "dircmp", "directories", @@ -2381,6 +2394,7 @@ dependencies = [ "crossbeam-channel", "derivative", "derive_more", + "derive_setters", "directories", "dirs", "displaydoc", diff --git a/Cargo.toml b/Cargo.toml index 80ede2c70..de89e9899 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,6 +51,7 @@ thiserror = { workspace = true } # macros derive_more = { workspace = true } derivative = { workspace = true } +derive_setters = { workspace = true } #logging log = { workspace = true } @@ -160,6 +161,7 @@ anyhow = "1" # macros derive_more = "0.99" derivative = "2" +derive_setters = "0.1" #logging log = "0.4" diff --git a/crates/rustic_core/Cargo.toml b/crates/rustic_core/Cargo.toml index 4aead8ae8..d10efdbc2 100644 --- a/crates/rustic_core/Cargo.toml +++ b/crates/rustic_core/Cargo.toml @@ -37,6 +37,7 @@ thiserror = { workspace = true } # macros derive_more = { workspace = true } derivative = { workspace = true } +derive_setters = { workspace = true } #logging log = { workspace = true } diff --git a/crates/rustic_core/examples/backup.rs b/crates/rustic_core/examples/backup.rs index 919b56622..0755faa29 100644 --- a/crates/rustic_core/examples/backup.rs +++ b/crates/rustic_core/examples/backup.rs @@ -1,32 +1,24 @@ //! `backup` example use rustic_core::{BackupOpts, PathList, Repository, RepositoryOptions, SnapshotFile}; use simplelog::{Config, LevelFilter, SimpleLogger}; +use std::error::Error; -fn main() { +fn main() -> Result<(), Box> { // Display info logs let _ = SimpleLogger::init(LevelFilter::Info, Config::default()); // Open repository - let repo_opts = RepositoryOptions { - repository: Some("/tmp/repo".to_string()), - password: Some("test".to_string()), - ..Default::default() - }; - - let repo = Repository::new(&repo_opts) - .unwrap() - .open() - .unwrap() - .to_indexed_ids() - .unwrap(); + let repo_opts = RepositoryOptions::default() + .repository("/tmp/repo") + .password("test"); + let repo = Repository::new(&repo_opts)?.open()?.to_indexed_ids()?; let backup_opts = BackupOpts::default(); - let source = PathList::from_string(".", true).unwrap(); // true: sanitize the given string + let source = PathList::from_string(".", true)?; // true: sanitize the given string let dry_run = false; - let snap = repo - .backup(&backup_opts, source, SnapshotFile::default(), dry_run) - .unwrap(); + let snap = repo.backup(&backup_opts, source, SnapshotFile::default(), dry_run)?; - println!("successfully created snapshot:\n{snap:#?}") + println!("successfully created snapshot:\n{snap:#?}"); + Ok(()) } diff --git a/crates/rustic_core/examples/check.rs b/crates/rustic_core/examples/check.rs index 3488012ae..6ef7faa78 100644 --- a/crates/rustic_core/examples/check.rs +++ b/crates/rustic_core/examples/check.rs @@ -1,20 +1,20 @@ //! `check` example use rustic_core::{CheckOpts, Repository, RepositoryOptions}; use simplelog::{Config, LevelFilter, SimpleLogger}; +use std::error::Error; -fn main() { +fn main() -> Result<(), Box> { // Display info logs let _ = SimpleLogger::init(LevelFilter::Info, Config::default()); // Open repository - let repo_opts = RepositoryOptions { - repository: Some("/tmp/repo".to_string()), - password: Some("test".to_string()), - ..Default::default() - }; - let repo = Repository::new(&repo_opts).unwrap().open().unwrap(); + let repo_opts = RepositoryOptions::default() + .repository("/tmp/repo") + .password("test"); + let repo = Repository::new(&repo_opts)?.open()?; // Check respository with standard options let opts = CheckOpts::default(); - repo.check(opts).unwrap() + repo.check(opts)?; + Ok(()) } diff --git a/crates/rustic_core/examples/config.rs b/crates/rustic_core/examples/config.rs index 84333f6c4..6bd9b424c 100644 --- a/crates/rustic_core/examples/config.rs +++ b/crates/rustic_core/examples/config.rs @@ -1,24 +1,23 @@ //! `config` example use rustic_core::{ConfigOpts, Repository, RepositoryOptions}; use simplelog::{Config, LevelFilter, SimpleLogger}; +use std::error::Error; -fn main() { +fn main() -> Result<(), Box> { // Display info logs let _ = SimpleLogger::init(LevelFilter::Info, Config::default()); // Open repository - let repo_opts = RepositoryOptions { - repository: Some("/tmp/repo".to_string()), - password: Some("test".to_string()), - ..Default::default() - }; - - let repo = Repository::new(&repo_opts).unwrap().open().unwrap(); + let repo_opts = RepositoryOptions::default() + .repository("/tmp/repo") + .password("test"); + let repo = Repository::new(&repo_opts)?.open()?; // Set Config, e.g. Compression level let config_opts = ConfigOpts { set_compression: Some(22), ..Default::default() }; - repo.apply_config(&config_opts).unwrap(); + repo.apply_config(&config_opts)?; + Ok(()) } diff --git a/crates/rustic_core/examples/copy.rs b/crates/rustic_core/examples/copy.rs index 095e4493f..e045878d1 100644 --- a/crates/rustic_core/examples/copy.rs +++ b/crates/rustic_core/examples/copy.rs @@ -1,30 +1,25 @@ //! `copy` example -use std::error::Error; - use rustic_core::{CopySnapshot, Repository, RepositoryOptions}; use simplelog::{Config, LevelFilter, SimpleLogger}; +use std::error::Error; fn main() -> Result<(), Box> { // Display info logs let _ = SimpleLogger::init(LevelFilter::Info, Config::default()); // Open repository - let src_repo_opts = RepositoryOptions { - repository: Some("/tmp/repo".to_string()), - password: Some("test".to_string()), - ..Default::default() - }; + let src_repo_opts = RepositoryOptions::default() + .repository("/tmp/repo") + .password("test"); let src_repo = Repository::new(&src_repo_opts)?.open()?.to_indexed()?; - let dst_repo_opts = RepositoryOptions { - repository: Some("/tmp/repo2".to_string()), - password: Some("test".to_string()), - ..Default::default() - }; + let dst_repo_opts = RepositoryOptions::default() + .repository("tmp/repo") + .password("test"); let dst_repo = Repository::new(&dst_repo_opts)?.open()?.to_indexed_ids()?; // get snapshots which are missing in dst_repo - let snapshots = src_repo.get_matching_snapshots(|_| true)?; + let snapshots = src_repo.get_all_snapshots()?; let snaps = dst_repo.relevant_copy_snapshots(|_| true, &snapshots)?; // copy only relevant snapshots @@ -34,6 +29,5 @@ fn main() -> Result<(), Box> { .iter() .filter_map(|CopySnapshot { relevant, sn }| relevant.then_some(sn)), )?; - Ok(()) } diff --git a/crates/rustic_core/examples/forget.rs b/crates/rustic_core/examples/forget.rs index 3ecd022a0..51f9967fd 100644 --- a/crates/rustic_core/examples/forget.rs +++ b/crates/rustic_core/examples/forget.rs @@ -1,28 +1,26 @@ //! `forget` example use rustic_core::{KeepOptions, Repository, RepositoryOptions, SnapshotGroupCriterion}; use simplelog::{Config, LevelFilter, SimpleLogger}; +use std::error::Error; -fn main() { +fn main() -> Result<(), Box> { // Display info logs let _ = SimpleLogger::init(LevelFilter::Info, Config::default()); // Open repository - let repo_opts = RepositoryOptions { - repository: Some("/tmp/repo".to_string()), - password: Some("test".to_string()), - ..Default::default() - }; - let repo = Repository::new(&repo_opts).unwrap().open().unwrap(); + let repo_opts = RepositoryOptions::default() + .repository("/tmp/repo") + .password("test"); + let repo = Repository::new(&repo_opts)?.open()?; // Check respository with standard options let group_by = SnapshotGroupCriterion::default(); let mut keep = KeepOptions::default(); keep.keep_daily = 5; keep.keep_weekly = 10; - let snaps = repo - .get_forget_snapshots(&keep, group_by, |_| true) - .unwrap(); + let snaps = repo.get_forget_snapshots(&keep, group_by, |_| true)?; println!("{snaps:?}"); // to remove the snapshots-to-forget, uncomment this line: - // repo.delete_snapshots(&snaps.into_forget_ids()).unwrap() + // repo.delete_snapshots(&snaps.into_forget_ids())? + Ok(()) } diff --git a/crates/rustic_core/examples/init.rs b/crates/rustic_core/examples/init.rs index 152045e4b..1ead46176 100644 --- a/crates/rustic_core/examples/init.rs +++ b/crates/rustic_core/examples/init.rs @@ -1,23 +1,20 @@ //! `init` example use rustic_core::{ConfigOpts, KeyOpts, Repository, RepositoryOptions}; use simplelog::{Config, LevelFilter, SimpleLogger}; +use std::error::Error; -fn main() { +fn main() -> Result<(), Box> { // Display info logs let _ = SimpleLogger::init(LevelFilter::Info, Config::default()); // Init repository - let repo_opts = RepositoryOptions { - repository: Some("/tmp/repo".to_string()), - password: Some("test".to_string()), - ..Default::default() - }; + let repo_opts = RepositoryOptions::default() + .repository("/tmp/repo") + .password("test"); let key_opts = KeyOpts::default(); let config_opts = ConfigOpts::default(); - let _repo = Repository::new(&repo_opts) - .unwrap() - .init(&key_opts, &config_opts) - .unwrap(); + let _repo = Repository::new(&repo_opts)?.init(&key_opts, &config_opts)?; // -> use _repo for any operation on an open repository + Ok(()) } diff --git a/crates/rustic_core/examples/key.rs b/crates/rustic_core/examples/key.rs index 55c528e7e..345f59c50 100644 --- a/crates/rustic_core/examples/key.rs +++ b/crates/rustic_core/examples/key.rs @@ -1,21 +1,20 @@ //! `key` example use rustic_core::{KeyOpts, Repository, RepositoryOptions}; use simplelog::{Config, LevelFilter, SimpleLogger}; +use std::error::Error; -fn main() { +fn main() -> Result<(), Box> { // Display info logs let _ = SimpleLogger::init(LevelFilter::Info, Config::default()); // Open repository - let repo_opts = RepositoryOptions { - repository: Some("/tmp/repo".to_string()), - password: Some("test".to_string()), - ..Default::default() - }; - - let repo = Repository::new(&repo_opts).unwrap().open().unwrap(); + let repo_opts = RepositoryOptions::default() + .repository("/tmp/repo") + .password("test"); + let repo = Repository::new(&repo_opts)?.open()?; // Add a new key with the given password let key_opts = KeyOpts::default(); - repo.add_key("new_password", &key_opts).unwrap(); + repo.add_key("new_password", &key_opts)?; + Ok(()) } diff --git a/crates/rustic_core/examples/ls.rs b/crates/rustic_core/examples/ls.rs index f45b674f5..414dd9262 100644 --- a/crates/rustic_core/examples/ls.rs +++ b/crates/rustic_core/examples/ls.rs @@ -1,33 +1,27 @@ //! `ls` example use rustic_core::{Repository, RepositoryOptions, TreeStreamerOptions}; use simplelog::{Config, LevelFilter, SimpleLogger}; +use std::error::Error; -fn main() { +fn main() -> Result<(), Box> { // Display info logs let _ = SimpleLogger::init(LevelFilter::Info, Config::default()); // Open repository - let repo_opts = RepositoryOptions { - repository: Some("/tmp/repo".to_string()), - password: Some("test".to_string()), - ..Default::default() - }; - - let repo = Repository::new(&repo_opts) - .unwrap() - .open() - .unwrap() - .to_indexed() - .unwrap(); + let repo_opts = RepositoryOptions::default() + .repository("/tmp/repo") + .password("test"); + let repo = Repository::new(&repo_opts)?.open()?.to_indexed()?; // use latest snapshot without filtering snapshots - let node = repo.node_from_snapshot_path("latest", |_| true).unwrap(); + let node = repo.node_from_snapshot_path("latest", |_| true)?; // recursively list the snapshot contents using no additional filtering let recursive = true; let streamer_opts = TreeStreamerOptions::default(); - for item in repo.ls(&node, &streamer_opts, recursive).unwrap() { - let (path, _) = item.unwrap(); + for item in repo.ls(&node, &streamer_opts, recursive)? { + let (path, _) = item?; println!("{path:?} "); } + Ok(()) } diff --git a/crates/rustic_core/examples/prune.rs b/crates/rustic_core/examples/prune.rs index 34b6ec0aa..6b62403e5 100644 --- a/crates/rustic_core/examples/prune.rs +++ b/crates/rustic_core/examples/prune.rs @@ -1,23 +1,23 @@ //! `prune` example use rustic_core::{PruneOpts, Repository, RepositoryOptions}; use simplelog::{Config, LevelFilter, SimpleLogger}; +use std::error::Error; -fn main() { +fn main() -> Result<(), Box> { // Display info logs let _ = SimpleLogger::init(LevelFilter::Info, Config::default()); // Open repository - let repo_opts = RepositoryOptions { - repository: Some("/tmp/repo".to_string()), - password: Some("test".to_string()), - ..Default::default() - }; - let repo = Repository::new(&repo_opts).unwrap().open().unwrap(); + let repo_opts = RepositoryOptions::default() + .repository("/tmp/repo") + .password("test"); + let repo = Repository::new(&repo_opts)?.open()?; let prune_opts = PruneOpts::default(); - let prune_plan = repo.prune_plan(&prune_opts).unwrap(); + let prune_plan = repo.prune_plan(&prune_opts)?; println!("{:?}", prune_plan.stats); println!("to repack: {:?}", prune_plan.repack_packs()); // to run the plan uncomment this line: - // prune_plan.do_prune(&repo, &prune_opts).unwrap(); + // prune_plan.do_prune(&repo, &prune_opts)?; + Ok(()) } diff --git a/crates/rustic_core/examples/restore.rs b/crates/rustic_core/examples/restore.rs index 794331359..38a5be92f 100644 --- a/crates/rustic_core/examples/restore.rs +++ b/crates/rustic_core/examples/restore.rs @@ -3,43 +3,35 @@ use rustic_core::{ LocalDestination, Repository, RepositoryOptions, RestoreOpts, TreeStreamerOptions, }; use simplelog::{Config, LevelFilter, SimpleLogger}; +use std::error::Error; -fn main() { +fn main() -> Result<(), Box> { // Display info logs let _ = SimpleLogger::init(LevelFilter::Info, Config::default()); // Open repository - let repo_opts = RepositoryOptions { - repository: Some("/tmp/repo".to_string()), - password: Some("test".to_string()), - ..Default::default() - }; - - let repo = Repository::new(&repo_opts) - .unwrap() - .open() - .unwrap() - .to_indexed() - .unwrap(); + let repo_opts = RepositoryOptions::default() + .repository("/tmp/repo") + .password("test"); + let repo = Repository::new(&repo_opts)?.open()?.to_indexed()?; // use latest snapshot without filtering snapshots - let node = repo.node_from_snapshot_path("latest", |_| true).unwrap(); + let node = repo.node_from_snapshot_path("latest", |_| true)?; // use list of the snapshot contents using no additional filtering let recursive = true; let streamer_opts = TreeStreamerOptions::default(); - let ls = repo.ls(&node, &streamer_opts, recursive).unwrap(); + let ls = repo.ls(&node, &streamer_opts, recursive)?; let destination = "./restore/"; // restore to this destination dir let create = true; // create destination dir, if it doesn't exist - let dest = LocalDestination::new(destination, create, !node.is_dir()).unwrap(); + let dest = LocalDestination::new(destination, create, !node.is_dir())?; let opts = RestoreOpts::default(); let dry_run = false; // create restore infos. Note: this also already creates needed dirs in the destination - let restore_infos = repo - .prepare_restore(&opts, ls.clone(), &dest, dry_run) - .unwrap(); + let restore_infos = repo.prepare_restore(&opts, ls.clone(), &dest, dry_run)?; - repo.restore(restore_infos, &opts, ls, &dest).unwrap(); + repo.restore(restore_infos, &opts, ls, &dest)?; + Ok(()) } diff --git a/crates/rustic_core/examples/tag.rs b/crates/rustic_core/examples/tag.rs index ce48f16aa..04c6e0225 100644 --- a/crates/rustic_core/examples/tag.rs +++ b/crates/rustic_core/examples/tag.rs @@ -1,26 +1,22 @@ //! `tag` example -use std::str::FromStr; - use rustic_core::{Repository, RepositoryOptions, StringList}; use simplelog::{Config, LevelFilter, SimpleLogger}; +use std::error::Error; +use std::str::FromStr; -fn main() { +fn main() -> Result<(), Box> { // Display info logs let _ = SimpleLogger::init(LevelFilter::Info, Config::default()); // Open repository - let repo_opts = RepositoryOptions { - repository: Some("/tmp/repo".to_string()), - password: Some("test".to_string()), - ..Default::default() - }; - let repo = Repository::new(&repo_opts).unwrap().open().unwrap(); - - // Get all snapshots - use a filter which doesn't filter out. - let snaps = repo.get_matching_snapshots(|_| true).unwrap(); + let repo_opts = RepositoryOptions::default() + .repository("/tmp/repo") + .password("test"); + let repo = Repository::new(&repo_opts)?.open()?; // Set tag "test" to all snapshots, filtering out unchanged (i.e. tag was aready preset) snapshots - let tags = vec![StringList::from_str("test").unwrap()]; + let snaps = repo.get_all_snapshots()?; + let tags = vec![StringList::from_str("test")?]; let snaps: Vec<_> = snaps .into_iter() .filter_map(|mut sn| sn.add_tags(tags.clone()).then_some(sn)) // can also use set_tags or remove_tags @@ -28,6 +24,7 @@ fn main() { let old_snap_ids: Vec<_> = snaps.iter().map(|sn| sn.id).collect(); // remove old snapshots and save changed ones - repo.save_snapshots(snaps).unwrap(); - repo.delete_snapshots(&old_snap_ids).unwrap(); + repo.save_snapshots(snaps)?; + repo.delete_snapshots(&old_snap_ids)?; + Ok(()) } diff --git a/crates/rustic_core/src/repository.rs b/crates/rustic_core/src/repository.rs index 1923fd653..de3963437 100644 --- a/crates/rustic_core/src/repository.rs +++ b/crates/rustic_core/src/repository.rs @@ -7,6 +7,7 @@ use std::{ }; use bytes::Bytes; +use derive_setters::Setters; use log::{debug, error, info}; use nom::{ @@ -53,8 +54,9 @@ use warm_up::{warm_up, warm_up_wait}; #[serde_as] #[cfg_attr(feature = "clap", derive(clap::Parser))] #[cfg_attr(feature = "merge", derive(merge::Merge))] -#[derive(Clone, Default, Debug, serde::Deserialize)] +#[derive(Clone, Default, Debug, serde::Deserialize, Setters)] #[serde(default, rename_all = "kebab-case", deny_unknown_fields)] +#[setters(into, strip_option)] pub struct RepositoryOptions { /// Repository to use #[cfg_attr( @@ -498,6 +500,10 @@ impl Repository { SnapshotFile::from_ids(self.dbe(), ids, &p) } + pub fn get_all_snapshots(&self) -> RusticResult> { + self.get_matching_snapshots(|_| true) + } + pub fn get_matching_snapshots( &self, filter: impl FnMut(&SnapshotFile) -> bool,