Skip to main content

novel_api/common/utils/
zip.rs

1use std::fs::{self, File};
2use std::io::{self, Read, Seek, Write};
3use std::path::Path;
4
5use walkdir::WalkDir;
6use zip::write::SimpleFileOptions;
7use zip::{CompressionMethod, ZipArchive, ZipWriter};
8
9use crate::Error;
10
11pub fn unzip<T, R>(reader: T, out_dir: R) -> Result<(), Error>
12where
13    T: Read + Seek,
14    R: AsRef<Path>,
15{
16    let mut archive = ZipArchive::new(reader)?;
17
18    for i in 0..archive.len() {
19        let mut file = archive.by_index(i)?;
20        let out_path = file.enclosed_name().ok_or(Error::NovelApi(format!(
21            "unable to extract file {} because it has an invalid path",
22            file.name()
23        )))?;
24        let out_path = out_dir.as_ref().join(out_path);
25
26        if file.is_dir() {
27            fs::create_dir_all(&out_path)?;
28        } else {
29            if let Some(p) = out_path.parent()
30                && !p.try_exists()?
31            {
32                fs::create_dir_all(p)?;
33            }
34
35            File::create(&out_path).and_then(|mut outfile| io::copy(&mut file, &mut outfile))?;
36        }
37
38        #[cfg(unix)]
39        {
40            use std::fs::Permissions;
41            use std::os::unix::fs::PermissionsExt;
42
43            if let Some(mode) = file.unix_mode() {
44                fs::set_permissions(&out_path, Permissions::from_mode(mode))?;
45            }
46        }
47    }
48
49    Ok(())
50}
51
52pub fn zip_dir<T, R>(src_dir: T, dest_file: R) -> Result<(), Error>
53where
54    T: AsRef<Path>,
55    R: AsRef<Path>,
56{
57    let file = File::create(dest_file)?;
58    let walkdir = WalkDir::new(src_dir.as_ref());
59
60    let mut zip = ZipWriter::new(file);
61    let options = SimpleFileOptions::default()
62        .compression_method(CompressionMethod::Deflated)
63        .unix_permissions(0o644);
64
65    let prefix = src_dir.as_ref();
66    let mut buffer = Vec::new();
67    for entry in walkdir.into_iter().filter_map(|e| e.ok()) {
68        let path = entry.path();
69        let name = path.strip_prefix(prefix)?;
70        let path_as_string = name
71            .to_str()
72            .map(str::to_owned)
73            .ok_or_else(|| Error::NovelApi(format!("{name:?} is a Non UTF-8 Path")))?;
74
75        if path.is_file() {
76            zip.start_file(path_as_string, options)?;
77            let mut f = File::open(path)?;
78
79            f.read_to_end(&mut buffer)?;
80            zip.write_all(&buffer)?;
81            buffer.clear();
82        } else if !name.as_os_str().is_empty() {
83            zip.add_directory(path_as_string, options)?;
84        }
85    }
86    zip.finish()?;
87
88    Ok(())
89}