use std::{
error::Error,
fs::{self, File},
io::BufReader,
path::{Path, PathBuf},
};
use tar::{Archive as TarArchive, Builder as TarBuilder};
use xz2::write::XzEncoder;
#[derive(Debug)]
pub enum ArchiveOperation {
Archive,
Unarchive,
}
#[derive(Debug)]
pub struct Archive {
source_path: PathBuf,
operation: ArchiveOperation,
}
impl Archive {
pub fn new(source_path: PathBuf, operation: ArchiveOperation) -> Self {
Self {
source_path,
operation,
}
}
pub fn execute(&self, delete_dir: bool) -> Result<(), Box<dyn Error>> {
match self.operation {
ArchiveOperation::Archive => {
self.create_archive()?;
if delete_dir {
fs::remove_dir_all(&self.source_path)?;
}
}
ArchiveOperation::Unarchive => {
self.extract_archive()?;
if delete_dir {
fs::remove_file(&self.source_path)?;
}
}
}
Ok(())
}
fn create_archive(&self) -> Result<(), Box<dyn Error>> {
let parent_dir = self.source_path.parent().unwrap_or_else(|| Path::new("."));
let file_stem = self
.source_path
.file_name()
.ok_or("Source path has no file name!")?;
let archive_name = format!("{}.tar.xz", file_stem.to_string_lossy());
let archive_path = parent_dir.join(archive_name);
let tar_xz_file = File::create(&archive_path)?;
let xz_encoder = XzEncoder::new(tar_xz_file, 6); let mut tar_builder = TarBuilder::new(xz_encoder);
tar_builder.append_dir_all(".", &self.source_path)?;
let xz_encoder = tar_builder.into_inner()?;
xz_encoder.finish()?;
Ok(())
}
fn extract_archive(&self) -> Result<(), Box<dyn Error>> {
let tar_xz_file = File::open(&self.source_path)?;
let buf_reader = BufReader::new(tar_xz_file);
let xz_decoder = xz2::read::XzDecoder::new(buf_reader);
let mut tar_archive = TarArchive::new(xz_decoder);
let parent_dir = self.source_path.parent().unwrap_or_else(|| Path::new("."));
let source_file_stem = self
.source_path
.file_stem()
.ok_or("Could not determine file stem from .tar.xz")?;
let mut out_dir_name = source_file_stem.to_os_string();
if out_dir_name.to_string_lossy().ends_with(".tar") {
let trimmed = out_dir_name
.to_string_lossy()
.trim_end_matches(".tar")
.to_string();
out_dir_name = trimmed.into();
}
let destination = parent_dir.join(out_dir_name);
fs::create_dir_all(&destination)?;
tar_archive.unpack(&destination)?;
Ok(())
}
}