diff --git a/src/error.rs b/src/error.rs index aeadbd1..39c5633 100644 --- a/src/error.rs +++ b/src/error.rs @@ -10,7 +10,7 @@ pub enum ErrorKind { Io(#[from] std::io::Error), #[error("error parsing revision lockfile")] RevisionLock, - #[error("error reading revision lockfile at {path}")] + #[error("error reading revision at {path}")] ReadRevision { path: PathBuf, #[source] @@ -52,6 +52,8 @@ pub enum ErrorKind { #[source] source: std::io::Error, }, + #[error("error unpacking revision")] + UnpackError(#[source] std::io::Error), } pub type Result = core::result::Result; diff --git a/src/storage/fs.rs b/src/storage/fs.rs index 74a4fc5..0264f3f 100644 --- a/src/storage/fs.rs +++ b/src/storage/fs.rs @@ -2,6 +2,7 @@ use std::io::{self, BufReader, BufWriter, Read, Write}; use std::path::Path; use std::{fs, path::PathBuf}; +use flate2::read::GzDecoder; use indicatif::ProgressBar; use crate::Data; @@ -39,7 +40,7 @@ fn copy_with_progress(src: &Path, dst: &Path, progress: &ProgressBar) -> io::Res } impl StorageImpl for FSStorage { - fn get_revision(&self, app_state: Data) -> Result { + fn get_revision_metadata(&self, app_state: Data) -> Result { let path = base_path(app_state).join(LOCKFILE_NAME); let revision = match fs::read_to_string(&path) { Ok(rev) => rev.parse::().map_err(|_| Error::revision_lock())?, @@ -55,15 +56,12 @@ impl StorageImpl for FSStorage { } fn store_revision(&self, app_state: Data) -> Result<()> { - let revision = self.get_revision(app_state.clone())?; + let revision = self.get_revision_metadata(app_state.clone())?; let new_revision = revision + 1; let reporter = Reporter::new(app_state.clone()); let result = create_archive(app_state.clone(), &reporter)?; - let archive_path = match &app_state.config.sync.fs_dir { - Some(dir) => PathBuf::from(shellexpand::tilde(&dir).as_ref()) - .join(&format!("revision_{new_revision}.tar.gz")), - _ => base_path(app_state.clone()).join(&format!("revision_{new_revision}.tar.gz")), - }; + let archive_path = + base_path(app_state.clone()).join(&format!("revision_{revision}.tar.gz")); match result.temp_file.persist(&archive_path) { Ok(_) => {} @@ -96,6 +94,44 @@ impl StorageImpl for FSStorage { tracing::debug!(revision, path = ?remove_path, "cleared revisions"); Ok(()) } + + fn retrieve_revision( + &self, + app_state: Data, + revision: usize, + ) -> Result>>> { + let archive_path = base_path(app_state).join(&format!("revision_{revision}.tar.gz")); + + let file = + fs::File::open(&archive_path).map_err(|e| Error::read_revision(e, &archive_path))?; + let buffered = BufReader::new(file); + let decoder = GzDecoder::new(buffered); + let archive = tar::Archive::new(decoder); + + Ok(archive) + } + + fn unpack_revision( + &self, + app_state: Data, + destination: PathBuf, + revision: usize, + ) -> Result<()> { + let mut archive = self.retrieve_revision(app_state.clone(), revision)?; + let entries = archive.entries()?; + let reporter = Reporter::new(app_state); + let total_files = entries.progress_with_style + reporter.start_archive_progress(total); + + for entry in entries { + let mut entry = entry?; + entry + .unpack_in(&destination) + .map_err(|e| Error::unpack_error(e))?; + } + + Ok(()) + } } pub fn base_path(app_state: Data) -> PathBuf { diff --git a/src/storage/mod.rs b/src/storage/mod.rs index 137c464..a18c2a6 100644 --- a/src/storage/mod.rs +++ b/src/storage/mod.rs @@ -1,12 +1,21 @@ pub mod fs; -use std::path::PathBuf; +use std::{io::BufReader, path::PathBuf}; + +use flate2::read::GzDecoder; use crate::{Data, error::Result}; pub trait StorageImpl { - fn get_revision(&self, app_state: Data) -> Result; + fn get_revision_metadata(&self, app_state: Data) -> Result; fn store_revision(&self, app_state: Data) -> Result<()>; + fn retrieve_revision( + &self, + app_state: Data, + revision: usize, + ) -> Result>>>; + fn unpack_revision(&self, app_state: Data, destination: PathBuf, revision: usize) + -> Result<()>; } pub enum Storage { @@ -16,9 +25,9 @@ pub enum Storage { pub const IGNORE_FILES: [&str; 6] = ["assets", "cache", "catpacks", "logs", "meta", "metacache"]; impl StorageImpl for Storage { - fn get_revision(&self, app_state: Data) -> Result { + fn get_revision_metadata(&self, app_state: Data) -> Result { match self { - Self::FS(storage) => storage.get_revision(app_state), + Self::FS(storage) => storage.get_revision_metadata(app_state), } } @@ -27,6 +36,27 @@ impl StorageImpl for Storage { Self::FS(storage) => storage.store_revision(app_state), } } + + fn retrieve_revision( + &self, + app_state: Data, + revision: usize, + ) -> Result>>> { + match self { + Self::FS(storage) => storage.retrieve_revision(app_state, revision), + } + } + + fn unpack_revision( + &self, + app_state: Data, + destination: PathBuf, + revision: usize, + ) -> Result<()> { + match self { + Self::FS(storage) => storage.unpack_revision(app_state, destination, revision), + } + } } pub fn get_storage_from_env() -> Storage {