use std::fs::{self, File};
use std::io::{self, Read, Seek, Write};
use std::path::Path;
use walkdir::WalkDir;
use zip::write::SimpleFileOptions;
use zip::{CompressionMethod, ZipArchive, ZipWriter};
use crate::Error;
pub fn unzip<T, R>(reader: T, out_dir: R) -> Result<(), Error>
where
T: Read + Seek,
R: AsRef<Path>,
{
let mut archive = ZipArchive::new(reader)?;
for i in 0..archive.len() {
let mut file = archive.by_index(i)?;
let out_path = file.enclosed_name().ok_or(Error::NovelApi(format!(
"unable to extract file {} because it has an invalid path",
file.name()
)))?;
let out_path = out_dir.as_ref().join(out_path);
if file.is_dir() {
fs::create_dir_all(&out_path)?;
} else {
if let Some(p) = out_path.parent()
&& !p.try_exists()?
{
fs::create_dir_all(p)?;
}
File::create(&out_path).and_then(|mut outfile| io::copy(&mut file, &mut outfile))?;
}
#[cfg(unix)]
{
use std::fs::Permissions;
use std::os::unix::fs::PermissionsExt;
if let Some(mode) = file.unix_mode() {
fs::set_permissions(&out_path, Permissions::from_mode(mode))?;
}
}
}
Ok(())
}
pub fn zip_dir<T, R>(src_dir: T, dest_file: R) -> Result<(), Error>
where
T: AsRef<Path>,
R: AsRef<Path>,
{
let file = File::create(dest_file)?;
let walkdir = WalkDir::new(src_dir.as_ref());
let mut zip = ZipWriter::new(file);
let options = SimpleFileOptions::default()
.compression_method(CompressionMethod::Deflated)
.unix_permissions(0o644);
let prefix = src_dir.as_ref();
let mut buffer = Vec::new();
for entry in walkdir.into_iter().filter_map(|e| e.ok()) {
let path = entry.path();
let name = path.strip_prefix(prefix)?;
let path_as_string = name
.to_str()
.map(str::to_owned)
.ok_or_else(|| Error::NovelApi(format!("{name:?} is a Non UTF-8 Path")))?;
if path.is_file() {
zip.start_file(path_as_string, options)?;
let mut f = File::open(path)?;
f.read_to_end(&mut buffer)?;
zip.write_all(&buffer)?;
buffer.clear();
} else if !name.as_os_str().is_empty() {
zip.add_directory(path_as_string, options)?;
}
}
zip.finish()?;
Ok(())
}