zip_extensions/
write.rs

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
12/// Creates a zip archive that contains the files and directories from the specified directory.
13pub 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
18/// Creates a zip archive that contains the files and directories from the specified directory, uses the specified compression level.
19pub 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    /// Creates a zip archive that contains the files and directories from the specified directory.
35    fn create_from_directory(self, directory: &PathBuf) -> ZipResult<()>;
36
37    /// Creates a zip archive that contains the files and directories from the specified directory, uses the specified compression level.
38    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}