• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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