diff --git a/crates/rustic_core/src/commands/backup.rs b/crates/rustic_core/src/commands/backup.rs index 60474866b..8c9ed567e 100644 --- a/crates/rustic_core/src/commands/backup.rs +++ b/crates/rustic_core/src/commands/backup.rs @@ -12,8 +12,8 @@ use crate::{ backend::{dry_run::DryRunBackend, stdin::StdinSource}, repofile::SnapshotFile, repository::{IndexedIds, IndexedTree}, - Id, LocalSource, LocalSourceFilterOptions, LocalSourceSaveOptions, Open, PathList, - ProgressBars, Repository, RusticResult, SnapshotGroup, SnapshotGroupCriterion, + Id, LocalSource, LocalSourceFilterOptions, LocalSourceSaveOptions, PathList, ProgressBars, + Repository, RusticResult, SnapshotGroup, SnapshotGroupCriterion, }; #[cfg_attr(feature = "clap", derive(clap::Parser))] diff --git a/crates/rustic_core/src/commands/config.rs b/crates/rustic_core/src/commands/config.rs index beb0c45ed..b15c67c37 100644 --- a/crates/rustic_core/src/commands/config.rs +++ b/crates/rustic_core/src/commands/config.rs @@ -6,7 +6,8 @@ use crate::{ backend::decrypt::{DecryptBackend, DecryptWriteBackend}, error::CommandErrorKind, repofile::ConfigFile, - Key, Open, Repository, RusticResult, + repository::Open, + Key, Repository, RusticResult, }; pub(crate) fn apply_config( diff --git a/crates/rustic_core/src/commands/copy.rs b/crates/rustic_core/src/commands/copy.rs index 3996bb337..13a86bdcd 100644 --- a/crates/rustic_core/src/commands/copy.rs +++ b/crates/rustic_core/src/commands/copy.rs @@ -8,8 +8,8 @@ use crate::{ blob::{packer::Packer, tree::TreeStreamerOnce, BlobType}, index::{indexer::Indexer, IndexedBackend, ReadIndex}, repofile::SnapshotFile, - repository::{IndexedFull, IndexedIds, IndexedTree}, - Open, ProgressBars, Repository, RusticResult, + repository::{IndexedFull, IndexedIds, IndexedTree, Open}, + ProgressBars, Repository, RusticResult, }; #[derive(Debug)] diff --git a/crates/rustic_core/src/commands/key.rs b/crates/rustic_core/src/commands/key.rs index 8744a4c88..622f0cb60 100644 --- a/crates/rustic_core/src/commands/key.rs +++ b/crates/rustic_core/src/commands/key.rs @@ -6,7 +6,8 @@ use crate::{ crypto::hasher::hash, error::CommandErrorKind, repofile::KeyFile, - Id, Key, Open, Repository, RusticResult, + repository::Open, + Id, Key, Repository, RusticResult, }; #[cfg_attr(feature = "clap", derive(clap::Parser))] diff --git a/crates/rustic_core/src/commands/merge.rs b/crates/rustic_core/src/commands/merge.rs index be00d4136..f20861a77 100644 --- a/crates/rustic_core/src/commands/merge.rs +++ b/crates/rustic_core/src/commands/merge.rs @@ -15,7 +15,7 @@ use crate::{ index::{indexer::Indexer, ReadIndex}, repofile::{SnapshotFile, SnapshotSummary}, repository::IndexedTree, - Id, Open, PathList, Progress, ProgressBars, Repository, RusticResult, + Id, PathList, Progress, ProgressBars, Repository, RusticResult, }; pub(crate) fn merge_snapshots( diff --git a/crates/rustic_core/src/commands/repair/index.rs b/crates/rustic_core/src/commands/repair/index.rs index 9fea690c3..e16969974 100644 --- a/crates/rustic_core/src/commands/repair/index.rs +++ b/crates/rustic_core/src/commands/repair/index.rs @@ -12,7 +12,8 @@ use crate::{ error::CommandErrorKind, index::indexer::Indexer, repofile::{IndexFile, IndexPack, PackHeader, PackHeaderRef}, - Open, Progress, ProgressBars, Repository, RusticResult, + repository::Open, + Progress, ProgressBars, Repository, RusticResult, }; #[cfg_attr(feature = "clap", derive(clap::Parser))] diff --git a/crates/rustic_core/src/commands/repair/snapshots.rs b/crates/rustic_core/src/commands/repair/snapshots.rs index e5849e7bc..ca3be11ec 100644 --- a/crates/rustic_core/src/commands/repair/snapshots.rs +++ b/crates/rustic_core/src/commands/repair/snapshots.rs @@ -10,7 +10,7 @@ use crate::{ index::{indexer::Indexer, IndexedBackend, ReadIndex}, repofile::SnapshotFile, repository::{IndexedFull, IndexedTree}, - Id, Open, ProgressBars, Repository, RusticResult, StringList, + Id, ProgressBars, Repository, RusticResult, StringList, }; #[cfg_attr(feature = "clap", derive(clap::Parser))] diff --git a/crates/rustic_core/src/commands/restore.rs b/crates/rustic_core/src/commands/restore.rs index b4db6f166..82a5f2a96 100644 --- a/crates/rustic_core/src/commands/restore.rs +++ b/crates/rustic_core/src/commands/restore.rs @@ -24,8 +24,8 @@ use crate::{ }, blob::BlobType, error::CommandErrorKind, - repository::{IndexedFull, IndexedTree}, - Id, LocalDestination, Open, Progress, ProgressBars, Repository, RusticResult, + repository::{IndexedFull, IndexedTree, Open}, + Id, LocalDestination, Progress, ProgressBars, Repository, RusticResult, }; pub(crate) mod constants { diff --git a/crates/rustic_core/src/lib.rs b/crates/rustic_core/src/lib.rs index 02e6f6f26..05c8af7bf 100644 --- a/crates/rustic_core/src/lib.rs +++ b/crates/rustic_core/src/lib.rs @@ -128,5 +128,5 @@ pub use crate::{ repofile::snapshotfile::{ PathList, SnapshotGroup, SnapshotGroupCriterion, SnapshotOptions, StringList, }, - repository::{IndexedFull, Open, OpenStatus, Repository, RepositoryOptions}, + repository::{IndexedFull, OpenStatus, Repository, RepositoryOptions}, }; diff --git a/crates/rustic_core/src/repofile/snapshotfile.rs b/crates/rustic_core/src/repofile/snapshotfile.rs index 7e5d1faaf..e5ff67570 100644 --- a/crates/rustic_core/src/repofile/snapshotfile.rs +++ b/crates/rustic_core/src/repofile/snapshotfile.rs @@ -756,6 +756,7 @@ impl StringList { self.0.join("\n") } + /// Turn StringList into an Iterator pub fn iter(&self) -> std::slice::Iter<'_, String> { self.0.iter() } diff --git a/crates/rustic_core/src/repository.rs b/crates/rustic_core/src/repository.rs index a74e31531..9f53e1960 100644 --- a/crates/rustic_core/src/repository.rs +++ b/crates/rustic_core/src/repository.rs @@ -65,6 +65,7 @@ use warm_up::{warm_up, warm_up_wait}; #[derive(Clone, Default, Debug, serde::Deserialize, Setters)] #[serde(default, rename_all = "kebab-case", deny_unknown_fields)] #[setters(into, strip_option)] +/// Options for using and opening a [`Repository`] pub struct RepositoryOptions { /// Repository to use #[cfg_attr( @@ -141,13 +142,19 @@ pub struct RepositoryOptions { #[serde_as(as = "Option")] pub warm_up_wait: Option, + /// Other options for this repository #[cfg_attr(feature = "clap", clap(skip))] #[cfg_attr(feature = "merge", merge(strategy = overwrite))] pub options: HashMap, } -// TODO: Unused function -#[allow(dead_code)] +impl RepositoryOptions { + /// Create a [`Repository`] using the given repository options + pub fn to_repository(&self) -> RusticResult> { + Repository::new(self) + } +} + pub(crate) fn overwrite(left: &mut T, right: T) { *left = right; } @@ -188,22 +195,28 @@ pub fn read_password_from_reader(file: &mut impl BufRead) -> RusticResult { + /// The name of the repository pub name: String, - pub be: HotColdBackend, - pub be_hot: Option, + pub(crate) be: HotColdBackend, + pub(crate) be_hot: Option, opts: RepositoryOptions, pub(crate) pb: P, status: S, } impl Repository { + /// Create a new repository from the given [`RepositoryOptions`] (without progress bars) pub fn new(opts: &RepositoryOptions) -> RusticResult { Self::new_with_progress(opts, NoProgressBars {}) } } impl

Repository { + /// Create a new repository from the given [`RepositoryOptions`] with given progress bars pub fn new_with_progress(opts: &RepositoryOptions, pb: P) -> RusticResult { let be = match &opts.repository { Some(repo) => ChooseBackend::from_url(repo)?, @@ -244,6 +257,7 @@ impl

Repository { } } impl Repository { + /// Evaluates the password given by the repository options pub fn password(&self) -> RusticResult> { match ( &self.opts.password, @@ -291,6 +305,7 @@ impl Repository { } } + /// Gives the id of the config file pub fn config_id(&self) -> RusticResult> { let config_ids = self .be @@ -304,6 +319,9 @@ impl Repository { } } + /// Open the repository. + /// + /// This gets the decryption key and reads the config file pub fn open(self) -> RusticResult> { let password = self .password()? @@ -311,6 +329,9 @@ impl Repository { self.open_with_password(&password) } + /// Open the repository with a given password. + /// + /// This gets the decryption key and reads the config file pub fn open_with_password(self, password: &str) -> RusticResult> { let config_id = self .config_id()? @@ -342,6 +363,9 @@ impl Repository { self.open_raw(key, config) } + /// Initialize a new repository with given options using the password defined in `RepositoryOptions` + /// + /// This returns an open repository which can be directly used. pub fn init( self, key_opts: &KeyOptions, @@ -353,6 +377,9 @@ impl Repository { self.init_with_password(&password, key_opts, config_opts) } + /// Initialize a new repository with given password and options. + /// + /// This returns an open repository which can be directly used. pub fn init_with_password( self, pass: &str, @@ -366,13 +393,16 @@ impl Repository { self.open_raw(key, config) } + /// Initialize a new repository with given password and a ready [`ConfigFile`]. + /// + /// This returns an open repository which can be directly used. pub fn init_with_config( self, - pass: &str, + password: &str, key_opts: &KeyOptions, config: ConfigFile, ) -> RusticResult> { - let key = commands::init::init_with_config(&self, pass, key_opts, &config)?; + let key = commands::init::init_with_config(&self, password, key_opts, &config)?; info!("repository {} successfully created.", config.id); self.open_raw(key, config) } @@ -412,19 +442,24 @@ impl Repository { }) } + /// List all file [`Id`]s of the given [`FileType`] which are present in the repository pub fn list(&self, tpe: FileType) -> RusticResult> { Ok(self.be.list(tpe)?.into_iter()) } } impl Repository { + /// Collect information about repository files pub fn infos_files(&self) -> RusticResult { commands::repoinfo::collect_file_infos(self) } + + /// Warm up the given pack files without waiting. pub fn warm_up(&self, packs: impl ExactSizeIterator) -> RusticResult<()> { warm_up(self, packs) } + /// Warm up the given pack files and wait the configured waiting time. pub fn warm_up_wait(&self, packs: impl ExactSizeIterator) -> RusticResult<()> { warm_up_wait(self, packs) } @@ -455,6 +490,7 @@ impl Open for Repository { } #[derive(Debug)] +/// Open Status: This repository is open, i.e. the password has been checked and the decryption key is available. pub struct OpenStatus { key: Key, cache: Option, @@ -480,20 +516,36 @@ impl Open for OpenStatus { } impl Repository { + /// Get the content of the decrypted repository file given by id and [`FileType`] pub fn cat_file(&self, tpe: FileType, id: &str) -> RusticResult { commands::cat::cat_file(self, tpe, id) } + /// Add a new key to the repository pub fn add_key(&self, pass: &str, opts: &KeyOptions) -> RusticResult { opts.add_key(self, pass) } + /// Update the repository config by applying the given [`ConfigOptions`] pub fn apply_config(&self, opts: &ConfigOptions) -> RusticResult { commands::config::apply_config(self, opts) } + + /// Get the repository configuration + pub fn config(&self) -> &ConfigFile { + self.status.config() + } + + pub(crate) fn dbe(&self) -> &S::DBE { + self.status.dbe() + } } impl Repository { + /// Get grouped snapshots. + /// + /// If `ids` are given, this will try to resolve the ids (or `latest` with respect to the given filter) and return a single group + /// If `ids` is empty, return and group all snapshots respecting the filter. pub fn get_snapshot_group( &self, ids: &[String], @@ -503,6 +555,10 @@ impl Repository { commands::snapshots::get_snapshot_group(self, ids, group_by, filter) } + /// Get a single snapshot + /// + /// If `id` is (part of) an `Id`, return this snapshot. + /// If `id` is "latest", return the latest snapshot respecting the giving filter. pub fn get_snapshot_from_str( &self, id: &str, @@ -513,15 +569,21 @@ impl Repository { Ok(snap) } + /// Get the given snapshots. + /// + /// `ids` may contain part of snapshots id which will be resolved. However, "latest" is not supported in + /// this function. pub fn get_snapshots>(&self, ids: &[T]) -> RusticResult> { let p = self.pb.progress_counter("getting snapshots..."); SnapshotFile::from_ids(self.dbe(), ids, &p) } + /// Get all snapshots from the repository pub fn get_all_snapshots(&self) -> RusticResult> { self.get_matching_snapshots(|_| true) } + /// Get all snapshots from the repository respecting the given `filter` pub fn get_matching_snapshots( &self, filter: impl FnMut(&SnapshotFile) -> bool, @@ -530,6 +592,7 @@ impl Repository { SnapshotFile::all_from_backend(self.dbe(), filter, &p) } + /// Get snapshots to forget depending on the given [`KeepOptions`] pub fn get_forget_snapshots( &self, keep: &KeepOptions, @@ -539,6 +602,9 @@ impl Repository { commands::forget::get_forget_snapshots(self, keep, group_by, filter) } + /// Get snapshots which are not already present and should be present. + /// + /// Note: This method should be called on the *destination repository* pub fn relevant_copy_snapshots( &self, filter: impl FnMut(&SnapshotFile) -> bool, @@ -549,6 +615,7 @@ impl Repository { // TODO: Maybe only offer a method to remove &[Snapshotfile] and check if they must be kept. // See e.g. the merge command of the CLI + /// Remove the given snapshots from the repository pub fn delete_snapshots(&self, ids: &[Id]) -> RusticResult<()> { let p = self.pb.progress_counter("removing snapshots..."); self.dbe() @@ -556,6 +623,7 @@ impl Repository { Ok(()) } + /// Save the given snapshots to the repository. pub fn save_snapshots(&self, mut snaps: Vec) -> RusticResult<()> { for snap in &mut snaps { snap.id = Id::default(); @@ -565,14 +633,19 @@ impl Repository { Ok(()) } + /// Check the repository for errors or inconsistencies pub fn check(&self, opts: CheckOptions) -> RusticResult<()> { opts.run(self) } + /// Get the plan about what should be pruned and/or repacked. pub fn prune_plan(&self, opts: &PruneOptions) -> RusticResult { opts.get_plan(self) } + /// Turn the repository into the `IndexedFull` state by reading and storing the index + /// + /// This saves the full index in memory which can be quite memory-consuming! pub fn to_indexed(self) -> RusticResult>> { let index = IndexBackend::new(self.dbe(), &self.pb.progress_counter(""))?; let status = IndexedStatus { @@ -590,6 +663,10 @@ impl Repository { }) } + /// Turn the repository into the `IndexedIds` state by reading and storing a size-optimized index + /// + /// This saves only the `Id`s for data blobs. Therefore, not all operations are possible on the repository. + /// However, operations which add data are fully functional. pub fn to_indexed_ids(self) -> RusticResult>> { let index = IndexBackend::only_full_trees(self.dbe(), &self.pb.progress_counter(""))?; let status = IndexedStatus { @@ -607,10 +684,14 @@ impl Repository { }) } + /// Get statistical information from the index + /// + /// This method reads all index files, even if an index is already available in memory. pub fn infos_index(&self) -> RusticResult { commands::repoinfo::collect_index_infos(self) } + /// Read all files of a given [`RepoFile`] pub fn stream_files( &self, ) -> RusticResult>> { @@ -620,6 +701,10 @@ impl Repository { .into_iter()) } + /// Repair the index + /// + /// This compares the index with existing pack files and reads packfile headers to ensure the index + /// correctly represents the pack files. pub fn repair_index(&self, opts: &RepairIndexOptions, dry_run: bool) -> RusticResult<()> { opts.repair(self, dry_run) } @@ -630,7 +715,9 @@ pub trait IndexedTree: Open { fn index(&self) -> &Self::I; } +/// Repository is fully indexed pub trait IndexedIds: IndexedTree {} +/// Repository is fully indexed pub trait IndexedFull: IndexedIds {} impl IndexedTree for Repository { @@ -682,6 +769,7 @@ impl Open for IndexedStatus { } impl Repository { + /// Get the index entry of the given blob pub fn get_index_entry(&self, tpe: BlobType, id: &Id) -> RusticResult { let ie = self .index() @@ -692,6 +780,9 @@ impl Repository { } impl Repository { + /// Get a [`Node`] from a "SNAP\[:PATH\]" syntax + /// + /// This parses for a snapshot (using the filter when "latest" is used) and then traverses into the path to get the node. pub fn node_from_snapshot_path( &self, snap_path: &str, @@ -705,6 +796,9 @@ impl Repository { Tree::node_from_path(self.index(), snap.tree, Path::new(path)) } + /// Get a [`Node`] from a [`SnapshotFile`] and a `path` + /// + /// This traverses into the path to get the node. pub fn node_from_snapshot_and_path( &self, snap: &SnapshotFile, @@ -713,6 +807,9 @@ impl Repository { Tree::node_from_path(self.index(), snap.tree, Path::new(path)) } + /// Reads a raw tree from a "SNAP\[:PATH\]" syntax + /// + /// This parses for a snapshot (using the filter when "latest" is used) and then traverses into the path to get the tree. pub fn cat_tree( &self, snap: &str, @@ -721,6 +818,11 @@ impl Repository { commands::cat::cat_tree(self, snap, sn_filter) } + /// List the contents of a given [`Node`] + /// + /// If `node` is a tree node, this will list the content of that tree. + /// If `node` is a file node, this will only return one element. + /// Note that the `PathBuf` returned will be relative to the given 'node`. pub fn ls( &self, node: &Node, @@ -729,6 +831,7 @@ impl Repository { NodeStreamer::new_with_glob(self.index().clone(), node, ls_opts) } + /// Restore a given [`RestorePlan`] to a local destination pub fn restore( &self, restore_infos: RestorePlan, @@ -739,6 +842,11 @@ impl Repository { opts.restore(restore_infos, self, node_streamer, dest) } + /// Merge the given trees. + /// + /// This method creates needed tree blobs within the repository. + /// Merge conflicts (identical filenames which do not match) will be resolved using the ordering given by `cmp`. + /// This method returns the blob [`Id`] of the merged tree. pub fn merge_trees( &self, trees: &[Id], @@ -748,6 +856,11 @@ impl Repository { commands::merge::merge_trees(self, trees, cmp, summary) } + /// Merge the given snapshots. + /// + /// This method will create needed tree blobs within the repository. + /// Merge conflicts (identical filenames which do not match) will be resolved using the ordering given by `cmp`. + /// This method returns the modified and already saved [`SnapshotFile`]. pub fn merge_snapshots( &self, snaps: &[SnapshotFile], @@ -759,6 +872,10 @@ impl Repository { } impl Repository { + /// Run a backup of `source` using the given options. + /// + /// You have to give a preflled [`SnapshotFile`] which is modified and saved. + /// Returns the saved snapshot. pub fn backup( &self, opts: &BackupOptions, @@ -770,10 +887,14 @@ impl Repository { } impl Repository { + /// Read a raw blob pub fn cat_blob(&self, tpe: BlobType, id: &str) -> RusticResult { commands::cat::cat_blob(self, tpe, id) } + /// Dump a [`Node`] using the given writer. + /// + /// Currently, only regular file nodes are supported. pub fn dump(&self, node: &Node, w: &mut impl Write) -> RusticResult<()> { commands::dump::dump(self, node, w) } @@ -804,6 +925,10 @@ impl Repository { commands::copy::copy(self, repo_dest, snapshots) } + /// Repair snapshots. + /// + /// This traverses all trees of all snapshots and repairs defect trees. + /// WARNING: If you remove the original snapshots, you may loose data! pub fn repair_snapshots( &self, opts: &RepairSnapshotsOptions, diff --git a/src/commands/copy.rs b/src/commands/copy.rs index 6c421d7bd..fc6b1fc86 100644 --- a/src/commands/copy.rs +++ b/src/commands/copy.rs @@ -12,7 +12,7 @@ use log::{error, info}; use merge::Merge; use serde::Deserialize; -use rustic_core::{CopySnapshot, Id, KeyOptions, Open, Repository, RepositoryOptions}; +use rustic_core::{CopySnapshot, Id, KeyOptions, Repository, RepositoryOptions}; /// `copy` subcommand #[derive(clap::Parser, Command, Debug)]