novel_api/common/utils/
zip.rs1use 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}