use std::fs::File;
use std::io::Write;
use std::path::{Path, PathBuf};
use walkdir::WalkDir;
use std::io::Read;
use zip::{write::SimpleFileOptions, CompressionMethod, ZipWriter};
pub fn zip_folder(folder_dir: &Path, file_name: Option<&str>) -> anyhow::Result<PathBuf> {
if !folder_dir.is_dir() {
anyhow::bail!("Provided path is not a directory, or does not exist: {:?}", folder_dir);
}
let zip_path = match file_name {
Some(name) => folder_dir.parent().unwrap_or_else(|| Path::new(".")).join(name).with_extension("zip"),
None => folder_dir.with_extension("zip"),
};
let file = File::create(&zip_path)?;
let mut zip = ZipWriter::new(file);
let options = SimpleFileOptions::default()
.compression_method(CompressionMethod::Deflated)
.unix_permissions(0o755);
let mut buffer = Vec::new();
for entry in WalkDir::new(folder_dir).into_iter().filter_map(Result::ok) {
let path = entry.path();
let name = path.strip_prefix(folder_dir)?;
if path.is_file() {
zip.start_file(name.to_string_lossy(), options)?;
let mut f = File::open(path)?;
f.read_to_end(&mut buffer)?;
zip.write_all(&buffer)?;
buffer.clear();
} else if path.is_dir() {
zip.add_directory(name.to_string_lossy(), options)?;
}
}
zip.finish()?;
Ok(zip_path)
}
pub fn unzip_folder(zip_file_dir: &Path, file_name: Option<&str>) -> anyhow::Result<PathBuf> {
if !zip_file_dir.is_file() {
anyhow::bail!("Provided path is not a file, or does not exist: {:?}", zip_file_dir);
}
let base_name = match file_name {
Some(name) => name.to_string(),
None => zip_file_dir.file_stem().unwrap_or_default().to_string_lossy().into_owned(),
};
let mut output_dir = zip_file_dir.parent().unwrap_or_else(|| Path::new(".")).join(&base_name);
let mut counter = 1;
while output_dir.exists() {
output_dir = zip_file_dir
.parent()
.unwrap_or_else(|| Path::new("."))
.join(format!("{}-{}", base_name, counter));
counter += 1;
}
std::fs::create_dir_all(&output_dir)?;
let file = File::open(zip_file_dir)?;
let mut archive = zip::ZipArchive::new(file)?;
for i in 0..archive.len() {
let mut file = archive.by_index(i)?;
let out_path = output_dir.join(file.mangled_name());
if file.is_dir() {
std::fs::create_dir_all(&out_path)?;
} else {
if let Some(parent) = out_path.parent() {
std::fs::create_dir_all(parent)?;
}
let mut outfile = File::create(&out_path)?;
std::io::copy(&mut file, &mut outfile)?;
}
#[cfg(unix)]
if let Some(mode) = file.unix_mode() {
use std::os::unix::fs::PermissionsExt;
std::fs::set_permissions(&out_path, std::fs::Permissions::from_mode(mode))?;
}
}
Ok(output_dir)
}