feat: add revision retrieving

This commit is contained in:
2026-02-06 23:10:51 -08:00
parent e40f1bb3d6
commit a562005f6c
3 changed files with 80 additions and 12 deletions

View File

@@ -10,7 +10,7 @@ pub enum ErrorKind {
Io(#[from] std::io::Error), Io(#[from] std::io::Error),
#[error("error parsing revision lockfile")] #[error("error parsing revision lockfile")]
RevisionLock, RevisionLock,
#[error("error reading revision lockfile at {path}")] #[error("error reading revision at {path}")]
ReadRevision { ReadRevision {
path: PathBuf, path: PathBuf,
#[source] #[source]
@@ -52,6 +52,8 @@ pub enum ErrorKind {
#[source] #[source]
source: std::io::Error, source: std::io::Error,
}, },
#[error("error unpacking revision")]
UnpackError(#[source] std::io::Error),
} }
pub type Result<T> = core::result::Result<T, Error>; pub type Result<T> = core::result::Result<T, Error>;

View File

@@ -2,6 +2,7 @@ use std::io::{self, BufReader, BufWriter, Read, Write};
use std::path::Path; use std::path::Path;
use std::{fs, path::PathBuf}; use std::{fs, path::PathBuf};
use flate2::read::GzDecoder;
use indicatif::ProgressBar; use indicatif::ProgressBar;
use crate::Data; use crate::Data;
@@ -39,7 +40,7 @@ fn copy_with_progress(src: &Path, dst: &Path, progress: &ProgressBar) -> io::Res
} }
impl StorageImpl for FSStorage { impl StorageImpl for FSStorage {
fn get_revision(&self, app_state: Data) -> Result<usize> { fn get_revision_metadata(&self, app_state: Data) -> Result<usize> {
let path = base_path(app_state).join(LOCKFILE_NAME); let path = base_path(app_state).join(LOCKFILE_NAME);
let revision = match fs::read_to_string(&path) { let revision = match fs::read_to_string(&path) {
Ok(rev) => rev.parse::<usize>().map_err(|_| Error::revision_lock())?, Ok(rev) => rev.parse::<usize>().map_err(|_| Error::revision_lock())?,
@@ -55,15 +56,12 @@ impl StorageImpl for FSStorage {
} }
fn store_revision(&self, app_state: Data) -> Result<()> { 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 new_revision = revision + 1;
let reporter = Reporter::new(app_state.clone()); let reporter = Reporter::new(app_state.clone());
let result = create_archive(app_state.clone(), &reporter)?; let result = create_archive(app_state.clone(), &reporter)?;
let archive_path = match &app_state.config.sync.fs_dir { let archive_path =
Some(dir) => PathBuf::from(shellexpand::tilde(&dir).as_ref()) base_path(app_state.clone()).join(&format!("revision_{revision}.tar.gz"));
.join(&format!("revision_{new_revision}.tar.gz")),
_ => base_path(app_state.clone()).join(&format!("revision_{new_revision}.tar.gz")),
};
match result.temp_file.persist(&archive_path) { match result.temp_file.persist(&archive_path) {
Ok(_) => {} Ok(_) => {}
@@ -96,6 +94,44 @@ impl StorageImpl for FSStorage {
tracing::debug!(revision, path = ?remove_path, "cleared revisions"); tracing::debug!(revision, path = ?remove_path, "cleared revisions");
Ok(()) Ok(())
} }
fn retrieve_revision(
&self,
app_state: Data,
revision: usize,
) -> Result<tar::Archive<flate2::read::GzDecoder<BufReader<std::fs::File>>>> {
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 { pub fn base_path(app_state: Data) -> PathBuf {

View File

@@ -1,12 +1,21 @@
pub mod fs; pub mod fs;
use std::path::PathBuf; use std::{io::BufReader, path::PathBuf};
use flate2::read::GzDecoder;
use crate::{Data, error::Result}; use crate::{Data, error::Result};
pub trait StorageImpl { pub trait StorageImpl {
fn get_revision(&self, app_state: Data) -> Result<usize>; fn get_revision_metadata(&self, app_state: Data) -> Result<usize>;
fn store_revision(&self, app_state: Data) -> Result<()>; fn store_revision(&self, app_state: Data) -> Result<()>;
fn retrieve_revision(
&self,
app_state: Data,
revision: usize,
) -> Result<tar::Archive<GzDecoder<BufReader<std::fs::File>>>>;
fn unpack_revision(&self, app_state: Data, destination: PathBuf, revision: usize)
-> Result<()>;
} }
pub enum Storage { pub enum Storage {
@@ -16,9 +25,9 @@ pub enum Storage {
pub const IGNORE_FILES: [&str; 6] = ["assets", "cache", "catpacks", "logs", "meta", "metacache"]; pub const IGNORE_FILES: [&str; 6] = ["assets", "cache", "catpacks", "logs", "meta", "metacache"];
impl StorageImpl for Storage { impl StorageImpl for Storage {
fn get_revision(&self, app_state: Data) -> Result<usize> { fn get_revision_metadata(&self, app_state: Data) -> Result<usize> {
match self { 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), Self::FS(storage) => storage.store_revision(app_state),
} }
} }
fn retrieve_revision(
&self,
app_state: Data,
revision: usize,
) -> Result<tar::Archive<GzDecoder<BufReader<std::fs::File>>>> {
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 { pub fn get_storage_from_env() -> Storage {