diff --git a/crates/rustic_core/src/commands.rs b/crates/rustic_core/src/commands.rs index f1720ec51..f450b03a4 100644 --- a/crates/rustic_core/src/commands.rs +++ b/crates/rustic_core/src/commands.rs @@ -1,5 +1,6 @@ pub mod cat; pub mod check; +pub mod dump; pub mod forget; pub mod prune; pub mod repoinfo; diff --git a/crates/rustic_core/src/commands/dump.rs b/crates/rustic_core/src/commands/dump.rs new file mode 100644 index 000000000..374f24f57 --- /dev/null +++ b/crates/rustic_core/src/commands/dump.rs @@ -0,0 +1,23 @@ +use std::io::Write; + +use crate::{ + error::CommandErrorKind, repository::IndexedRepository, BlobType, IndexedBackend, Node, + NodeType, RusticResult, +}; + +pub(crate) fn dump

( + repo: &IndexedRepository

, + node: &Node, + w: &mut impl Write, +) -> RusticResult<()> { + if node.node_type != NodeType::File { + return Err(CommandErrorKind::DumpNotSupported(node.node_type.clone()).into()); + } + + for id in node.content.as_ref().unwrap() { + // TODO: cache blobs which are needed later + let data = repo.index.blob_from_backend(BlobType::Data, id)?; + w.write_all(&data)?; + } + Ok(()) +} diff --git a/crates/rustic_core/src/error.rs b/crates/rustic_core/src/error.rs index a37386107..13be3e151 100644 --- a/crates/rustic_core/src/error.rs +++ b/crates/rustic_core/src/error.rs @@ -21,7 +21,7 @@ use chrono::OutOfRangeError; use displaydoc::Display; use thiserror::Error; -use crate::{id::Id, repofile::indexfile::IndexPack}; +use crate::{id::Id, repofile::indexfile::IndexPack, NodeType}; /// Result type often returned from methods that can have rustic `Error`s. pub type RusticResult = Result; @@ -160,6 +160,8 @@ pub enum CommandErrorKind { RepackUncompressedRepoV1, /// datetime out of range: `{0:?}` FromOutOfRangeError(#[from] OutOfRangeError), + /// node type {0:?} not supported by dump + DumpNotSupported(NodeType), } /// [`CryptoErrorKind`] describes the errors that can happen while dealing with Cryptographic functions diff --git a/crates/rustic_core/src/repository.rs b/crates/rustic_core/src/repository.rs index fd24a9c0c..4080ee1ce 100644 --- a/crates/rustic_core/src/repository.rs +++ b/crates/rustic_core/src/repository.rs @@ -1,8 +1,8 @@ use std::{ collections::HashMap, fs::File, - io::{BufRead, BufReader}, - path::PathBuf, + io::{BufRead, BufReader, Write}, + path::{Path, PathBuf}, process::{Command, Stdio}, }; @@ -37,8 +37,8 @@ use crate::{ crypto::aespoly1305::Key, error::RepositoryErrorKind, repofile::{configfile::ConfigFile, keyfile::find_key_in_backend}, - BlobType, Id, IndexBackend, NoProgressBars, ProgressBars, PruneOpts, PrunePlan, RusticResult, - SnapshotFile, SnapshotGroup, SnapshotGroupCriterion, + BlobType, Id, IndexBackend, NoProgressBars, Node, ProgressBars, PruneOpts, PrunePlan, + RusticResult, SnapshotFile, SnapshotGroup, SnapshotGroupCriterion, Tree, }; pub(super) mod constants { @@ -448,9 +448,23 @@ pub struct IndexedRepository

{ } impl IndexedRepository

{ + pub fn node_from_snapshot_path( + &self, + snap_path: &str, + filter: impl FnMut(&SnapshotFile) -> bool + Send + Sync, + ) -> RusticResult { + let (id, path) = snap_path.split_once(':').unwrap_or((snap_path, "")); + + let p = &self.repo.pb.progress_counter("getting snapshot..."); + let snap = SnapshotFile::from_str(&self.repo.dbe, id, filter, p)?; + + Tree::node_from_path(&self.index, snap.tree, Path::new(path)) + } + pub fn cat_blob(&self, tpe: BlobType, id: &str) -> RusticResult { commands::cat::cat_blob(self, tpe, id) } + pub fn cat_tree( &self, snap: &str, @@ -458,4 +472,8 @@ impl IndexedRepository

{ ) -> RusticResult { commands::cat::cat_tree(self, snap, sn_filter) } + + pub fn dump(&self, node: &Node, w: &mut impl Write) -> RusticResult<()> { + commands::dump::dump(self, node, w) + } } diff --git a/src/commands/dump.rs b/src/commands/dump.rs index e478cd112..451dd9142 100644 --- a/src/commands/dump.rs +++ b/src/commands/dump.rs @@ -2,19 +2,10 @@ /// App-local prelude includes `app_reader()`/`app_writer()`/`app_config()` /// accessors along with logging macros. Customize as you see fit. -use crate::{ - commands::{get_repository, open_repository}, - status_err, Application, RUSTIC_APP, -}; +use crate::{commands::get_repository, status_err, Application, RUSTIC_APP}; use abscissa_core::{Command, Runnable, Shutdown}; -use anyhow::{bail, Result}; - -use std::{io::Write, path::Path}; - -use rustic_core::{ - BlobType, IndexBackend, IndexedBackend, NodeType, ProgressBars, SnapshotFile, Tree, -}; +use anyhow::Result; /// `dump` subcommand #[derive(clap::Parser, Command, Debug)] @@ -37,31 +28,12 @@ impl DumpCmd { fn inner_run(&self) -> Result<()> { let config = RUSTIC_APP.config(); - let repo = open_repository(get_repository(&config)); - - let be = &repo.dbe; - let progress_options = &config.global.progress_options; - - let (id, path) = self.snap.split_once(':').unwrap_or((&self.snap, "")); - let snap = SnapshotFile::from_str( - be, - id, - |sn| config.snapshot_filter.matches(sn), - &progress_options.progress_counter(""), - )?; - let index = IndexBackend::new(be, &progress_options.progress_counter(""))?; - let node = Tree::node_from_path(&index, snap.tree, Path::new(path))?; - - if node.node_type != NodeType::File { - bail!("dump only supports regular files!"); - } + let repo = get_repository(&config).open()?.to_indexed()?; + let node = + repo.node_from_snapshot_path(&self.snap, |sn| config.snapshot_filter.matches(sn))?; let mut stdout = std::io::stdout(); - for id in node.content.unwrap() { - // TODO: cache blobs which are needed later - let data = index.blob_from_backend(BlobType::Data, &id)?; - stdout.write_all(&data)?; - } + repo.dump(&node, &mut stdout)?; Ok(()) }