You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
121 lines
3.3 KiB
121 lines
3.3 KiB
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);
|
|
#[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().len() != 0 {
|
|
// Only if not root! Avoids path spec / warning
|
|
// and mapname conversion failed error on unzip
|
|
println!("adding dir {:?} as {:?} ...", path, 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.to_string());
|
|
let it = walkdir.into_iter();
|
|
|
|
zip_dir(&mut it.filter_map(|e| e.ok()), src_dir, file, method)?;
|
|
|
|
Ok(())
|
|
}
|