1use std::{
2 ffi::OsStr,
3 fs::File,
4 io::{Read, Write},
5 path::Path,
6};
7
8use zip::{ZipArchive, ZipWriter, write::SimpleFileOptions};
9
10use crate::Bundle;
11
12use super::{BundleUnzipError, BundleZipError};
13
14pub fn zip<S, F>(
52 path: &S,
53 target_path: &str,
54 compression_method: zip::CompressionMethod,
55 mut callback: Option<F>,
56) -> Result<(), BundleZipError>
57where
58 S: AsRef<OsStr> + ?Sized,
59 F: FnMut(&Path),
60{
61 let path = Path::new(path);
62 let target_path =
63 Path::new(target_path).join(path.file_name().ok_or(BundleZipError::NoNameFailed)?);
64
65 if !path.is_dir() {
66 return Err(BundleZipError::MissingBundleFailed);
67 }
68
69 match target_path.exists() {
70 true if target_path.is_file() => Ok(()),
71 true => Err(BundleZipError::ContainSameDirFailed),
72 false => Ok({
73 let file =
74 File::create(target_path).map_err(|e| BundleZipError::CreateBundleFailed(e))?;
75 let mut archive = ZipWriter::new(file);
76 let options = SimpleFileOptions::default()
77 .compression_method(compression_method)
78 .unix_permissions(0o755);
79
80 let mut buffer = Vec::new();
81 for entry in walkdir::WalkDir::new(path)
82 .into_iter()
83 .filter_map(|e| e.ok())
84 {
85 let entry_path = entry.path();
86 let name = entry_path.strip_prefix(path).unwrap();
87
88 if entry_path.is_file() {
89 #[allow(deprecated)]
90 archive.start_file_from_path(name, options)?;
91 let mut f = File::open(entry_path)?;
92
93 f.read_to_end(&mut buffer)?;
94 archive.write_all(&buffer)?;
95 buffer.clear();
96 } else if !name.as_os_str().is_empty() {
97 #[allow(deprecated)]
98 archive.add_directory_from_path(name, options)?;
99 }
100
101 callback.as_mut().map(|callback| callback(name));
102 }
103 }),
104 }
105}
106
107pub fn unzip<S>(path: &S, target_path: &str) -> Result<Bundle, BundleUnzipError>
137where
138 S: AsRef<OsStr> + ?Sized,
139{
140 let path = Path::new(path);
141 let target_path =
142 Path::new(target_path).join(path.file_name().ok_or(BundleUnzipError::NoNameFailed)?);
143
144 if !path.is_file() {
145 return Err(BundleUnzipError::MissingBundleFailed);
146 }
147
148 match target_path.exists() {
149 true if target_path.is_dir() => Ok(()),
150 true => Err(BundleUnzipError::ContainSameFileFailed),
151 false => Ok({
152 let file = File::open(path)?;
153 let mut archive = ZipArchive::new(file)?;
154 archive.extract(&target_path)?;
155 }),
156 }?;
157
158 Ok(Bundle::from_filename(target_path.file_name().unwrap())?)
159}
160
161#[test]
162fn test_zip() {
163 let temp_path = "./tests/bundles/temp";
164 if !Path::new(temp_path).exists() {
165 std::fs::create_dir_all(temp_path).unwrap();
166 }
167
168 let name = "plugin_a-v1.0.0.vpl";
169 let path = format!("./tests/bundles/{name}");
170
171 let target_path = temp_path;
172 zip(
173 &path,
174 target_path,
175 zip::CompressionMethod::Stored,
176 Some(|name: &Path| println!("{}", name.display())),
177 )
178 .unwrap();
179
180 std::fs::remove_file(format!("{target_path}/{name}")).unwrap();
181}
182
183#[test]
184fn test_unzip() {
185 let temp_path = "./tests/bundles/temp";
186 if !Path::new(temp_path).exists() {
187 std::fs::create_dir_all(temp_path).unwrap();
188 }
189
190 let name = "plugin_b-v1.0.0.vpl";
191 let path = format!("./tests/bundles/{name}");
192
193 let target_path = temp_path;
194 let bundle = unzip(&path, target_path).unwrap();
195 println!("{bundle}");
196
197 std::fs::remove_dir_all(format!("{target_path}/{name}")).unwrap();
198}