Allow using other than default flate2 backend for deflated compression method. The motivation for this change was to allow using different backends in dependent crates.
118 lines
3.2 KiB
Rust
118 lines
3.2 KiB
Rust
use std::io::prelude::*;
|
|
use std::io::{Seek, Write};
|
|
use std::iter::Iterator;
|
|
use zip::result::ZipError;
|
|
use zip::write::FileOptions;
|
|
|
|
use std::fs::File;
|
|
use std::path::Path;
|
|
use walkdir::{DirEntry, WalkDir};
|
|
|
|
fn main() {
|
|
std::process::exit(real_main());
|
|
}
|
|
|
|
const METHOD_STORED: Option<zip::CompressionMethod> = Some(zip::CompressionMethod::Stored);
|
|
|
|
#[cfg(any(
|
|
feature = "deflate",
|
|
feature = "deflate-miniz",
|
|
feature = "deflate-zlib"
|
|
))]
|
|
const METHOD_DEFLATED: Option<zip::CompressionMethod> = Some(zip::CompressionMethod::Deflated);
|
|
#[cfg(not(any(
|
|
feature = "deflate",
|
|
feature = "deflate-miniz",
|
|
feature = "deflate-zlib"
|
|
)))]
|
|
const METHOD_DEFLATED: Option<zip::CompressionMethod> = None;
|
|
|
|
#[cfg(feature = "bzip2")]
|
|
const METHOD_BZIP2: Option<zip::CompressionMethod> = Some(zip::CompressionMethod::Bzip2);
|
|
#[cfg(not(feature = "bzip2"))]
|
|
const METHOD_BZIP2: Option<zip::CompressionMethod> = None;
|
|
|
|
fn real_main() -> i32 {
|
|
let args: Vec<_> = std::env::args().collect();
|
|
if args.len() < 3 {
|
|
println!(
|
|
"Usage: {} <source_directory> <destination_zipfile>",
|
|
args[0]
|
|
);
|
|
return 1;
|
|
}
|
|
|
|
let src_dir = &*args[1];
|
|
let dst_file = &*args[2];
|
|
for &method in [METHOD_STORED, METHOD_DEFLATED, METHOD_BZIP2].iter() {
|
|
if method.is_none() {
|
|
continue;
|
|
}
|
|
match doit(src_dir, dst_file, method.unwrap()) {
|
|
Ok(_) => println!("done: {} written to {}", src_dir, dst_file),
|
|
Err(e) => println!("Error: {:?}", e),
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
fn zip_dir<T>(
|
|
it: &mut dyn Iterator<Item = DirEntry>,
|
|
prefix: &str,
|
|
writer: T,
|
|
method: zip::CompressionMethod,
|
|
) -> zip::result::ZipResult<()>
|
|
where
|
|
T: Write + Seek,
|
|
{
|
|
let mut zip = zip::ZipWriter::new(writer);
|
|
let options = FileOptions::default()
|
|
.compression_method(method)
|
|
.unix_permissions(0o755);
|
|
|
|
let mut buffer = Vec::new();
|
|
for entry in it {
|
|
let path = entry.path();
|
|
let name = path.strip_prefix(Path::new(prefix)).unwrap();
|
|
|
|
// Write file or directory explicitly
|
|
// Some unzip tools unzip files with directory paths correctly, some do not!
|
|
if path.is_file() {
|
|
println!("adding file {:?} as {:?} ...", path, name);
|
|
zip.start_file_from_path(name, 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().len() != 0 {
|
|
// Only if not root! Avoids path spec / warning
|
|
// and mapname conversion failed error on unzip
|
|
println!("adding dir {:?} as {:?} ...", path, name);
|
|
zip.add_directory_from_path(name, options)?;
|
|
}
|
|
}
|
|
zip.finish()?;
|
|
Result::Ok(())
|
|
}
|
|
|
|
fn doit(
|
|
src_dir: &str,
|
|
dst_file: &str,
|
|
method: zip::CompressionMethod,
|
|
) -> zip::result::ZipResult<()> {
|
|
if !Path::new(src_dir).is_dir() {
|
|
return Err(ZipError::FileNotFound);
|
|
}
|
|
|
|
let path = Path::new(dst_file);
|
|
let file = File::create(&path).unwrap();
|
|
|
|
let walkdir = WalkDir::new(src_dir.to_string());
|
|
let it = walkdir.into_iter();
|
|
|
|
zip_dir(&mut it.filter_map(|e| e.ok()), src_dir, file, method)?;
|
|
|
|
Ok(())
|
|
}
|