1use std::fs::File;
2use std::io;
3use std::io::{Read, Write};
4use std::path::PathBuf;
5
6use zip::{CompressionMethod, ZipWriter};
7use zip::result::ZipResult;
8use zip::write::{FileOptionExtension, FileOptions, SimpleFileOptions};
9
10use crate::file_utils::{make_relative_path, path_as_string};
11
12pub fn zip_create_from_directory(archive_file: &PathBuf, directory: &PathBuf) -> ZipResult<()> {
14 let options = SimpleFileOptions::default().compression_method(CompressionMethod::Stored);
15 zip_create_from_directory_with_options(archive_file, directory, |_| options)
16}
17
18pub fn zip_create_from_directory_with_options<F, T>(
20 archive_file: &PathBuf,
21 directory: &PathBuf,
22 cb_file_options: F,
23) -> ZipResult<()>
24where
25 T: FileOptionExtension,
26 F: Fn(&PathBuf) -> FileOptions<T>,
27{
28 let file = File::create(archive_file)?;
29 let zip_writer = ZipWriter::new(file);
30 zip_writer.create_from_directory_with_options(directory, cb_file_options)
31}
32
33pub trait ZipWriterExtensions {
34 fn create_from_directory(self, directory: &PathBuf) -> ZipResult<()>;
36
37 fn create_from_directory_with_options<F, T>(
39 self,
40 directory: &PathBuf,
41 cb_file_options: F,
42 ) -> ZipResult<()>
43 where
44 T: FileOptionExtension,
45 F: Fn(&PathBuf) -> FileOptions<T>;
46}
47
48impl<W: Write + io::Seek> ZipWriterExtensions for ZipWriter<W> {
49 fn create_from_directory(self, directory: &PathBuf) -> ZipResult<()> {
50 let options = SimpleFileOptions::default().compression_method(CompressionMethod::Stored);
51 self.create_from_directory_with_options(directory, |_| options)
52 }
53
54 fn create_from_directory_with_options<F, T>(
55 mut self,
56 directory: &PathBuf,
57 cb_file_options: F,
58 ) -> ZipResult<()>
59 where
60 T: FileOptionExtension,
61 F: Fn(&PathBuf) -> FileOptions<T>,
62 {
63 let mut paths_queue: Vec<PathBuf> = vec![];
64 paths_queue.push(directory.clone());
65
66 let mut buffer = Vec::new();
67
68 while let Some(next) = paths_queue.pop() {
69 let directory_entry_iterator = std::fs::read_dir(next)?;
70
71 for entry in directory_entry_iterator {
72 let entry_path = entry?.path();
73 let file_options = cb_file_options(&entry_path);
74 let entry_metadata = std::fs::metadata(entry_path.clone())?;
75 if entry_metadata.is_file() {
76 let mut f = File::open(&entry_path)?;
77 f.read_to_end(&mut buffer)?;
78 let relative_path = make_relative_path(&directory, &entry_path);
79 self.start_file(path_as_string(&relative_path), file_options)?;
80 self.write_all(buffer.as_ref())?;
81 buffer.clear();
82 } else if entry_metadata.is_dir() {
83 let relative_path = make_relative_path(&directory, &entry_path);
84 self.add_directory(path_as_string(&relative_path), file_options)?;
85 paths_queue.push(entry_path.clone());
86 }
87 }
88 }
89
90 self.finish()?;
91 Ok(())
92 }
93}