Parch-backup/src/utils/compression/mod.rs
2024-06-25 22:54:21 +03:30

101 lines
3.1 KiB
Rust

use flate2::write::GzEncoder;
use flate2::Compression;
use std::fs::File;
use std::io;
use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicBool, Ordering};
use walkdir::WalkDir;
pub static ARCHIVE_EXT: &str = "tar.gz";
pub fn compress_directory<P: AsRef<Path>>(
source_dir: P,
target_file: P,
exclude_dirs: Option<&[String]>,
should_stop: &AtomicBool,
) -> io::Result<()> {
let tar_gz = File::create(target_file)?;
let enc = GzEncoder::new(tar_gz, Compression::default());
let mut tar = tar::Builder::new(enc);
let source_dir = source_dir.as_ref();
let exclude_paths: Vec<PathBuf> = exclude_dirs
.unwrap_or(&[])
.iter()
.map(|d| source_dir.join(d))
.collect();
for entry in WalkDir::new(source_dir).into_iter().filter_map(|e| e.ok()) {
if should_stop.load(Ordering::SeqCst) {
return Err(io::Error::new(io::ErrorKind::Interrupted, "Operation canceled"));
}
let entry_path = entry.path();
// Skip directories that need to be excluded
if exclude_paths.iter().any(|d| entry_path.starts_with(d)) {
continue;
}
let path_in_archive = match entry_path.strip_prefix(source_dir) {
Ok(p) => p,
Err(_) => continue,
};
if path_in_archive.components().count() == 0 {
// Skip empty paths
continue;
}
if entry.file_type().is_dir() {
tar.append_dir(path_in_archive, entry_path)?;
} else {
match File::open(entry_path) {
Ok(mut file) => {
tar.append_file(path_in_archive, &mut file)?;
}
Err(e) if e.kind() == io::ErrorKind::NotFound => {
eprintln!("Failed to backup file: {} - {}", entry_path.display(), e);
continue; // Skip not found files and continue the loop
}
Err(e) => return Err(e),
}
}
}
tar.finish()?;
Ok(())
}
/// Create a compressed tar archive (tar.gz) from specified files.
pub fn create_tar_gz_archive<P: AsRef<Path>>(
archive_name: P,
backup_files: &[(String, PathBuf)],
) -> io::Result<()> {
// Create the archive file
let archive_file = File::create(&archive_name)?;
let enc = GzEncoder::new(archive_file, Compression::default());
let mut tar = tar::Builder::new(enc);
// Add each backup file to the archive and remove original files
for (subdir, file) in backup_files {
let path_in_archive = Path::new(subdir).join(file.file_name().unwrap());
tar.append_path_with_name(file, path_in_archive)?;
std::fs::remove_file(file)?;
}
// Finalize the archive
tar.finish()?;
Ok(())
}
/// Opens and decodes a tar.gz archive file.
pub fn open_and_decode_archive<P: AsRef<Path>>(
archive_path: P,
) -> io::Result<tar::Archive<flate2::read::GzDecoder<File>>> {
// Open the archive file
let archive_file = File::open(archive_path)?;
let archive_decoder = flate2::read::GzDecoder::new(archive_file);
let tar = tar::Archive::new(archive_decoder);
Ok(tar)
}