pub mod zip_directory_util { use std::io::prelude::*; use zip::write::SimpleFileOptions; use std::fs::File; use std::path::Path; use walkdir::{DirEntry, WalkDir}; fn zip_dir( it: &mut dyn Iterator, prefix: &Path, writer: T, method: zip::CompressionMethod, ) where T: Write + Seek, { let mut zip = zip::ZipWriter::new(writer); let options = SimpleFileOptions::default() .compression_method(method) .unix_permissions(0o755); let prefix = Path::new(prefix); let mut buffer = Vec::new(); for entry in it { let path = entry.path(); let name = path.strip_prefix(prefix).unwrap(); let path_as_string = name .to_str() .map(str::to_owned) .expect("Failed to parse path"); // Write file or directory explicitly // Some unzip tools unzip files with directory paths correctly, some do not! if path.is_file() { println!("adding file {path:?} as {name:?} ..."); zip.start_file(path_as_string, options) .expect("Failed to add file"); let mut f = File::open(path).unwrap(); f.read_to_end(&mut buffer).expect("Failed to read file"); zip.write_all(&buffer).expect("Failed to write file"); buffer.clear(); } else if !name.as_os_str().is_empty() { // Only if not root! Avoids path spec / warning // and mapname conversion failed error on unzip println!("adding dir {path_as_string:?} as {name:?} ..."); zip.add_directory(path_as_string, options) .expect("Failed to add directory"); } } zip.finish().expect("Failed to ZIP"); } pub fn zip_source_dir_to_dst_file(src_dir: &Path, dst_file: &Path) { if !Path::new(src_dir).is_dir() { panic!("src_dir must be a directory"); } let method = zip::CompressionMethod::Stored; let path = Path::new(dst_file); let file = File::create(path).unwrap(); let walkdir = WalkDir::new(src_dir); let it = walkdir.into_iter(); zip_dir(&mut it.filter_map(|e| e.ok()), src_dir, file, method); } }