diff --git a/crates/rustic_core/src/commands.rs b/crates/rustic_core/src/commands.rs index 3e8ff0f..a91af1f 100644 --- a/crates/rustic_core/src/commands.rs +++ b/crates/rustic_core/src/commands.rs @@ -1 +1,2 @@ +pub mod cat; pub mod check; diff --git a/crates/rustic_core/src/commands/cat.rs b/crates/rustic_core/src/commands/cat.rs new file mode 100644 index 0000000..e85217f --- /dev/null +++ b/crates/rustic_core/src/commands/cat.rs @@ -0,0 +1,43 @@ +use std::path::Path; + +use bytes::Bytes; + +use crate::{ + error::CommandErrorKind, BlobType, DecryptReadBackend, FileType, Id, IndexBackend, + IndexedBackend, OpenRepository, ProgressBars, ReadBackend, RusticResult, SnapshotFile, Tree, +}; + +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: &OpenRepository, + tpe: BlobType, + id: &str, + pb: &impl ProgressBars, +) -> RusticResult { + let id = Id::from_hex(id)?; + let data = IndexBackend::new(&repo.dbe, &pb.progress_hidden())?.blob_from_backend(tpe, &id)?; + + Ok(data) +} + +pub fn cat_tree( + repo: &OpenRepository, + 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.dbe, id, sn_filter, &pb.progress_counter(""))?; + let index = IndexBackend::new(&repo.dbe, &pb.progress_counter(""))?; + let node = Tree::node_from_path(&index, snap.tree, Path::new(path))?; + let id = node + .subtree + .ok_or_else(|| CommandErrorKind::PathIsNoDir(path.to_string()))?; + let data = index.blob_from_backend(BlobType::Tree, &id)?; + Ok(data) +} diff --git a/crates/rustic_core/src/error.rs b/crates/rustic_core/src/error.rs index f34d1ee..60b5c10 100644 --- a/crates/rustic_core/src/error.rs +++ b/crates/rustic_core/src/error.rs @@ -42,6 +42,10 @@ impl RusticError { #[non_exhaustive] #[derive(Error, Debug)] pub enum RusticErrorKind { + /// [`CommandErrorKind`] describes the errors that can happen while executing a high-level command + #[error(transparent)] + Command(#[from] CommandErrorKind), + /// [`CryptoErrorKind`] describes the errors that can happen while dealing with Cryptographic functions #[error(transparent)] Crypto(#[from] CryptoErrorKind), @@ -135,6 +139,13 @@ pub enum RusticErrorKind { StdIo(#[from] std::io::Error), } +/// [`CommandErrorKind`] describes the errors that can happen while executing a high-level command +#[derive(Error, Debug, Display)] +pub enum CommandErrorKind { + /// path is no dir: `{0:?}` + PathIsNoDir(String), +} + /// [`CryptoErrorKind`] describes the errors that can happen while dealing with Cryptographic functions #[derive(Error, Debug, Display, Copy, Clone)] pub enum CryptoErrorKind { @@ -741,6 +752,7 @@ impl RusticErrorMarker for ProviderErrorKind {} impl RusticErrorMarker for RestErrorKind {} impl RusticErrorMarker for StdInErrorKind {} impl RusticErrorMarker for ArchiverErrorKind {} +impl RusticErrorMarker for CommandErrorKind {} impl RusticErrorMarker for std::io::Error {} impl From for RusticError diff --git a/crates/rustic_core/src/lib.rs b/crates/rustic_core/src/lib.rs index b7d5389..6ae6e09 100644 --- a/crates/rustic_core/src/lib.rs +++ b/crates/rustic_core/src/lib.rs @@ -120,7 +120,10 @@ pub use crate::{ BlobLocation, BlobType, BlobTypeMap, Initialize, Sum, }, chunker::random_poly, - commands::check::CheckOpts, + commands::{ + cat::{cat_blob, cat_file, cat_tree}, + check::CheckOpts, + }, crypto::{aespoly1305::Key, hasher::hash}, error::{RusticError, RusticResult}, file::{AddFileResult, FileInfos, RestoreStats}, diff --git a/src/commands/cat.rs b/src/commands/cat.rs index 33b3cf4..17a342f 100644 --- a/src/commands/cat.rs +++ b/src/commands/cat.rs @@ -9,14 +9,9 @@ use crate::{ use abscissa_core::{Command, Runnable, Shutdown}; -use std::path::Path; +use anyhow::Result; -use anyhow::{anyhow, Result}; - -use rustic_core::{ - BlobType, DecryptReadBackend, FileType, Id, IndexBackend, IndexedBackend, ProgressBars, - SnapshotFile, Tree, -}; +use rustic_core::{cat_blob, cat_file, cat_tree, BlobType, FileType}; /// `cat` subcommand #[derive(clap::Parser, Command, Debug)] @@ -66,59 +61,27 @@ 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 be = &repo.dbe; - - match &self.cmd { - CatSubCmd::Config => cat_file(be, FileType::Config, &IdOpt::default()), - CatSubCmd::Index(opt) => cat_file(be, FileType::Index, opt), - CatSubCmd::Snapshot(opt) => cat_file(be, FileType::Snapshot, opt), - // special treatment for catingg blobs: read the index and use it to locate the blob - CatSubCmd::TreeBlob(opt) => cat_blob(be, BlobType::Tree, opt), - CatSubCmd::DataBlob(opt) => cat_blob(be, BlobType::Data, opt), + let data = match &self.cmd { + CatSubCmd::Config => cat_file(&repo, FileType::Config, "")?, + CatSubCmd::Index(opt) => cat_file(&repo, FileType::Index, &opt.id)?, + CatSubCmd::Snapshot(opt) => cat_file(&repo, FileType::Snapshot, &opt.id)?, + // special treatment for cating blobs: read the index and use it to locate the blob + CatSubCmd::TreeBlob(opt) => cat_blob(&repo, BlobType::Tree, &opt.id, &po)?, + CatSubCmd::DataBlob(opt) => cat_blob(&repo, BlobType::Data, &opt.id, &po)?, // special treatment for cating a tree within a snapshot - CatSubCmd::Tree(opts) => cat_tree(be, opts), - }?; + CatSubCmd::Tree(opt) => cat_tree( + &repo, + &opt.snap, + |sn| config.snapshot_filter.matches(sn), + &po, + )?, + }; + println!("{}", String::from_utf8(data.to_vec())?); Ok(()) } } - -fn cat_file(be: &impl DecryptReadBackend, tpe: FileType, opt: &IdOpt) -> Result<()> { - let id = be.find_id(tpe, &opt.id)?; - let data = be.read_encrypted_full(tpe, &id)?; - println!("{}", String::from_utf8(data.to_vec())?); - - Ok(()) -} - -fn cat_blob(be: &impl DecryptReadBackend, tpe: BlobType, opt: &IdOpt) -> Result<()> { - let config = RUSTIC_APP.config(); - let id = Id::from_hex(&opt.id)?; - let data = IndexBackend::new(be, &config.global.progress_options.progress_hidden())? - .blob_from_backend(tpe, &id)?; - print!("{}", String::from_utf8(data.to_vec())?); - - Ok(()) -} - -fn cat_tree(be: &impl DecryptReadBackend, opts: &TreeOpts) -> Result<()> { - let config = RUSTIC_APP.config(); - - let (id, path) = opts.snap.split_once(':').unwrap_or((&opts.snap, "")); - let snap = SnapshotFile::from_str( - be, - id, - |sn| config.snapshot_filter.matches(sn), - &config.global.progress_options.progress_counter(""), - )?; - let index = IndexBackend::new(be, &config.global.progress_options.progress_counter(""))?; - let node = Tree::node_from_path(&index, snap.tree, Path::new(path))?; - let id = node.subtree.ok_or_else(|| anyhow!("{path} is no dir"))?; - let data = index.blob_from_backend(BlobType::Tree, &id)?; - println!("{}", String::from_utf8(data.to_vec())?); - - Ok(()) -}