1 use std::io::prelude::*;
2 use std::io::{Seek, Write};
3 use std::iter::Iterator;
4 use zip::result::ZipError;
5 use zip::write::FileOptions;
6
7 use std::fs::File;
8 use std::path::Path;
9 use walkdir::{DirEntry, WalkDir};
10
main()11 fn main() {
12 std::process::exit(real_main());
13 }
14
15 const METHOD_STORED: Option<zip::CompressionMethod> = Some(zip::CompressionMethod::Stored);
16
17 #[cfg(any(
18 feature = "deflate",
19 feature = "deflate-miniz",
20 feature = "deflate-zlib"
21 ))]
22 const METHOD_DEFLATED: Option<zip::CompressionMethod> = Some(zip::CompressionMethod::Deflated);
23 #[cfg(not(any(
24 feature = "deflate",
25 feature = "deflate-miniz",
26 feature = "deflate-zlib"
27 )))]
28 const METHOD_DEFLATED: Option<zip::CompressionMethod> = None;
29
30 #[cfg(feature = "bzip2")]
31 const METHOD_BZIP2: Option<zip::CompressionMethod> = Some(zip::CompressionMethod::Bzip2);
32 #[cfg(not(feature = "bzip2"))]
33 const METHOD_BZIP2: Option<zip::CompressionMethod> = None;
34
35 #[cfg(feature = "zstd")]
36 const METHOD_ZSTD: Option<zip::CompressionMethod> = Some(zip::CompressionMethod::Zstd);
37 #[cfg(not(feature = "zstd"))]
38 const METHOD_ZSTD: Option<zip::CompressionMethod> = None;
39
real_main() -> i3240 fn real_main() -> i32 {
41 let args: Vec<_> = std::env::args().collect();
42 if args.len() < 3 {
43 println!(
44 "Usage: {} <source_directory> <destination_zipfile>",
45 args[0]
46 );
47 return 1;
48 }
49
50 let src_dir = &*args[1];
51 let dst_file = &*args[2];
52 for &method in [METHOD_STORED, METHOD_DEFLATED, METHOD_BZIP2, METHOD_ZSTD].iter() {
53 if method.is_none() {
54 continue;
55 }
56 match doit(src_dir, dst_file, method.unwrap()) {
57 Ok(_) => println!("done: {src_dir} written to {dst_file}"),
58 Err(e) => println!("Error: {e:?}"),
59 }
60 }
61
62 0
63 }
64
zip_dir<T>( it: &mut dyn Iterator<Item = DirEntry>, prefix: &str, writer: T, method: zip::CompressionMethod, ) -> zip::result::ZipResult<()> where T: Write + Seek,65 fn zip_dir<T>(
66 it: &mut dyn Iterator<Item = DirEntry>,
67 prefix: &str,
68 writer: T,
69 method: zip::CompressionMethod,
70 ) -> zip::result::ZipResult<()>
71 where
72 T: Write + Seek,
73 {
74 let mut zip = zip::ZipWriter::new(writer);
75 let options = FileOptions::default()
76 .compression_method(method)
77 .unix_permissions(0o755);
78
79 let mut buffer = Vec::new();
80 for entry in it {
81 let path = entry.path();
82 let name = path.strip_prefix(Path::new(prefix)).unwrap();
83
84 // Write file or directory explicitly
85 // Some unzip tools unzip files with directory paths correctly, some do not!
86 if path.is_file() {
87 println!("adding file {path:?} as {name:?} ...");
88 #[allow(deprecated)]
89 zip.start_file_from_path(name, options)?;
90 let mut f = File::open(path)?;
91
92 f.read_to_end(&mut buffer)?;
93 zip.write_all(&buffer)?;
94 buffer.clear();
95 } else if !name.as_os_str().is_empty() {
96 // Only if not root! Avoids path spec / warning
97 // and mapname conversion failed error on unzip
98 println!("adding dir {path:?} as {name:?} ...");
99 #[allow(deprecated)]
100 zip.add_directory_from_path(name, options)?;
101 }
102 }
103 zip.finish()?;
104 Result::Ok(())
105 }
106
doit( src_dir: &str, dst_file: &str, method: zip::CompressionMethod, ) -> zip::result::ZipResult<()>107 fn doit(
108 src_dir: &str,
109 dst_file: &str,
110 method: zip::CompressionMethod,
111 ) -> zip::result::ZipResult<()> {
112 if !Path::new(src_dir).is_dir() {
113 return Err(ZipError::FileNotFound);
114 }
115
116 let path = Path::new(dst_file);
117 let file = File::create(path).unwrap();
118
119 let walkdir = WalkDir::new(src_dir);
120 let it = walkdir.into_iter();
121
122 zip_dir(&mut it.filter_map(|e| e.ok()), src_dir, file, method)?;
123
124 Ok(())
125 }
126