Source code

Revision control

Copy as Markdown

Other Tools

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;
#[cfg(feature = "zstd")]
const METHOD_ZSTD: Option<zip::CompressionMethod> = Some(zip::CompressionMethod::Zstd);
#[cfg(not(feature = "zstd"))]
const METHOD_ZSTD: 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, METHOD_ZSTD].iter() {
if method.is_none() {
continue;
}
match doit(src_dir, dst_file, method.unwrap()) {
Ok(_) => println!("done: {src_dir} written to {dst_file}"),
Err(e) => println!("Error: {e:?}"),
}
}
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 {path:?} as {name:?} ...");
#[allow(deprecated)]
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().is_empty() {
// Only if not root! Avoids path spec / warning
// and mapname conversion failed error on unzip
println!("adding dir {path:?} as {name:?} ...");
#[allow(deprecated)]
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);
let it = walkdir.into_iter();
zip_dir(&mut it.filter_map(|e| e.ok()), src_dir, file, method)?;
Ok(())
}