use anyhow::{Context, Result, bail};
use std::{
fs::{self, File},
path::Path,
};
use zip::ZipArchive;
pub fn extract_archive(path: &Path, out: &Path) -> Result<()> {
if !path.exists() {
bail!("'{}' does not exist.", path.display());
}
let archive_type = detect_archive_type(path)?;
match archive_type {
ArchiveType::Zip => extract_zip(path, out),
}
}
fn extract_zip(path: &Path, out: &Path) -> Result<()> {
let file = File::open(path).context("Failed to open ZIP archive")?;
let mut archive = ZipArchive::new(file).context("Failed to read ZIP archive")?;
for i in 0..archive.len() {
let mut file = archive.by_index(i)?;
let out_path = out.join(file.mangled_name());
if file.name().ends_with('/') {
fs::create_dir_all(&out_path)?;
} else {
if let Some(p) = out_path.parent() {
fs::create_dir_all(p)?;
}
let mut out_file = fs::File::create(&out_path)?;
std::io::copy(&mut file, &mut out_file)?;
}
}
Ok(())
}
fn detect_archive_type(path: &Path) -> Result<ArchiveType> {
let name = path
.file_name()
.and_then(|s| s.to_str())
.map(|s| s.to_ascii_lowercase())
.ok_or_else(|| anyhow::anyhow!("invalid archive filename"))?;
match () {
_ if name.ends_with(".zip") => Ok(ArchiveType::Zip),
_ => bail!("unsupported archive type: {}", path.display()),
}
}
pub enum ArchiveType {
Zip,
}