1use std::path::{Path, PathBuf};
2
3use async_fs as afs;
4use futures_lite::io::{AsyncRead, AsyncReadExt, AsyncSeek, AsyncSeekExt, SeekFrom};
5use std::future::Future;
6use std::io::{Read, Seek};
7use std::pin::Pin;
8
9use crate::{Error, Password, *};
10
11pub(crate) struct AsyncReadSeekAsStd<R: AsyncRead + Unpin> {
12 inner: R,
13}
14
15impl<R: AsyncRead + Unpin> AsyncReadSeekAsStd<R> {
16 pub(crate) fn new(inner: R) -> Self {
17 Self { inner }
18 }
19}
20
21impl<R: AsyncRead + Unpin> Read for AsyncReadSeekAsStd<R> {
22 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
23 async_io::block_on(AsyncReadExt::read(&mut self.inner, buf))
24 }
25}
26
27impl<R: AsyncRead + AsyncSeek + Unpin> Seek for AsyncReadSeekAsStd<R> {
28 fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
29 async_io::block_on(AsyncSeekExt::seek(
30 &mut self.inner,
31 match pos {
32 SeekFrom::Start(n) => SeekFrom::Start(n),
33 SeekFrom::End(i) => SeekFrom::End(i),
34 SeekFrom::Current(i) => SeekFrom::Current(i),
35 },
36 ))
37 }
38}
39
40pub async fn decompress_file(
48 src_path: impl AsRef<Path>,
49 dest: impl AsRef<Path>,
50) -> Result<(), Error> {
51 let dest_path = dest.as_ref().to_path_buf();
52 decompress_path_impl(
53 src_path.as_ref(),
54 dest_path,
55 Password::empty(),
56 |entry, reader, dest| Box::pin(default_entry_extract_fn(entry, reader, dest)),
57 )
58 .await
59}
60
61pub async fn decompress_file_with_extract_fn(
71 src_path: impl AsRef<Path>,
72 dest: impl AsRef<Path>,
73 mut extract_fn: impl for<'a> FnMut(
74 &'a ArchiveEntry,
75 &'a mut (dyn AsyncRead + Unpin + Send + 'a),
76 &'a Path,
77 )
78 -> Pin<Box<dyn Future<Output = Result<bool, Error>> + Send + 'a>>
79 + 'static
80 + Send,
81) -> Result<(), Error> {
82 decompress_path_impl(
83 src_path.as_ref(),
84 dest.as_ref().to_path_buf(),
85 Password::empty(),
86 move |entry, reader, path| extract_fn(entry, reader, path),
87 )
88 .await
89}
90
91pub async fn decompress<R: AsyncRead + AsyncSeek + Unpin + Send>(
97 mut src_reader: R,
98 dest: impl AsRef<Path>,
99) -> Result<(), Error> {
100 let pos = src_reader.seek(SeekFrom::Current(0)).await?;
101 AsyncSeekExt::seek(&mut src_reader, SeekFrom::Start(pos)).await?;
102 decompress_impl(
103 src_reader,
104 dest,
105 Password::empty(),
106 |entry, reader, dest| Box::pin(default_entry_extract_fn(entry, reader, dest)),
107 )
108 .await
109}
110
111#[cfg(not(target_arch = "wasm32"))]
120pub async fn decompress_with_extract_fn<R: AsyncRead + AsyncSeek + Unpin + Send>(
121 mut src_reader: R,
122 dest: impl AsRef<Path>,
123 extract_fn: impl for<'a> FnMut(
124 &'a ArchiveEntry,
125 &'a mut (dyn AsyncRead + Unpin + Send + 'a),
126 &'a Path,
127 )
128 -> Pin<Box<dyn Future<Output = Result<bool, Error>> + Send + 'a>>
129 + 'static
130 + Send,
131) -> Result<(), Error> {
132 let pos = src_reader.seek(SeekFrom::Current(0)).await?;
133 AsyncSeekExt::seek(&mut src_reader, SeekFrom::Start(pos)).await?;
134 decompress_impl(src_reader, dest, Password::empty(), extract_fn).await
135}
136
137#[cfg(all(feature = "aes256", not(target_arch = "wasm32")))]
144pub async fn decompress_file_with_password(
145 src_path: impl AsRef<Path>,
146 dest: impl AsRef<Path>,
147 password: Password,
148) -> Result<(), Error> {
149 let dest_path = dest.as_ref().to_path_buf();
150 decompress_path_impl(
151 src_path.as_ref(),
152 dest_path,
153 password,
154 |entry, reader, dest| Box::pin(default_entry_extract_fn(entry, reader, dest)),
155 )
156 .await
157}
158
159#[cfg(all(feature = "aes256", not(target_arch = "wasm32")))]
166pub async fn decompress_with_password<R: AsyncRead + AsyncSeek + Unpin + Send>(
167 mut src_reader: R,
168 dest: impl AsRef<Path>,
169 password: Password,
170) -> Result<(), Error> {
171 let pos = src_reader.seek(SeekFrom::Current(0)).await?;
172 AsyncSeekExt::seek(&mut src_reader, SeekFrom::Start(pos)).await?;
173 decompress_impl(src_reader, dest, password, |entry, reader, dest| {
174 Box::pin(default_entry_extract_fn(entry, reader, dest))
175 })
176 .await
177}
178
179#[cfg(all(feature = "aes256", not(target_arch = "wasm32")))]
190pub async fn decompress_with_extract_fn_and_password<R: AsyncRead + AsyncSeek + Unpin + Send>(
191 mut src_reader: R,
192 dest: impl AsRef<Path>,
193 password: Password,
194 extract_fn: impl for<'a> FnMut(
195 &'a ArchiveEntry,
196 &'a mut (dyn AsyncRead + Unpin + Send + 'a),
197 &'a Path,
198 )
199 -> Pin<Box<dyn Future<Output = Result<bool, Error>> + Send + 'a>>
200 + 'static
201 + Send,
202) -> Result<(), Error> {
203 let pos = src_reader.seek(SeekFrom::Current(0)).await?;
204 AsyncSeekExt::seek(&mut src_reader, SeekFrom::Start(pos)).await?;
205 decompress_impl(src_reader, dest, password, extract_fn).await
206}
207
208#[cfg(not(target_arch = "wasm32"))]
209async fn decompress_impl<R: AsyncRead + AsyncSeek + Unpin + Send>(
210 mut src_reader: R,
211 dest: impl AsRef<Path>,
212 password: Password,
213 extract_fn: impl for<'a> FnMut(
214 &'a ArchiveEntry,
215 &'a mut (dyn AsyncRead + Unpin + Send + 'a),
216 &'a Path,
217 )
218 -> Pin<Box<dyn Future<Output = Result<bool, Error>> + Send + 'a>>
219 + 'static
220 + Send,
221) -> Result<(), Error> {
222 let pos = src_reader.seek(SeekFrom::Current(0)).await?;
223 AsyncSeekExt::seek(&mut src_reader, SeekFrom::Start(pos)).await?;
224 let mut seven = ArchiveReader::new(src_reader, password).await?;
225 let dest = PathBuf::from(dest.as_ref());
226 if !dest.exists() {
227 afs::create_dir_all(&dest).await?;
228 }
229 let extract_fn_cell = std::sync::Arc::new(std::sync::Mutex::new(extract_fn));
230 seven
231 .for_each_entries(|entry, reader| {
232 let dest_path = dest.join(entry.name());
233 let extract_fn_cell = std::sync::Arc::clone(&extract_fn_cell);
234 Box::pin(async move {
235 let fut = {
236 let mut f = extract_fn_cell.lock().unwrap();
237 f(entry, reader, dest_path.as_path())
238 };
239 fut.await
240 })
241 })
242 .await?;
243
244 Ok(())
245}
246
247#[cfg(not(target_arch = "wasm32"))]
248async fn decompress_path_impl(
249 src_path: &Path,
250 dest: PathBuf,
251 password: Password,
252 extract_fn: impl for<'a> FnMut(
253 &'a ArchiveEntry,
254 &'a mut (dyn AsyncRead + Unpin + Send + 'a),
255 &'a Path,
256 )
257 -> Pin<Box<dyn Future<Output = Result<bool, Error>> + Send + 'a>>
258 + 'static
259 + Send,
260) -> Result<(), Error> {
261 let mut seven = ArchiveReader::open(src_path, password).await?;
262 if !dest.exists() {
263 afs::create_dir_all(&dest).await?;
264 }
265 let extract_fn_cell = std::sync::Arc::new(std::sync::Mutex::new(extract_fn));
266 seven
267 .for_each_entries(|entry, reader| {
268 let dest_path = dest.join(entry.name());
269 let extract_fn_cell = std::sync::Arc::clone(&extract_fn_cell);
270 Box::pin(async move {
271 let fut = {
272 let mut f = extract_fn_cell.lock().unwrap();
273 f(entry, reader, dest_path.as_path())
274 };
275 fut.await
276 })
277 })
278 .await?;
279 Ok(())
280}
281
282#[cfg(not(target_arch = "wasm32"))]
289pub async fn default_entry_extract_fn(
290 entry: &ArchiveEntry,
291 reader: &mut (dyn AsyncRead + Unpin + Send),
292 dest: &Path,
293) -> Result<bool, Error> {
294 if entry.is_directory() {
295 let dir = dest.to_path_buf();
296 afs::create_dir_all(&dir).await?;
297 } else {
298 let path = dest.to_path_buf();
299 if let Some(p) = path.parent() {
300 if !p.exists() {
301 afs::create_dir_all(p).await?;
302 }
303 }
304 if entry.size() > 0 {
305 let mut data = Vec::new();
306 AsyncReadExt::read_to_end(reader, &mut data)
307 .await
308 .map_err(|e| Error::io_msg(e, "read entry data"))?;
309 afs::write(&path, &data).await?;
310 } else {
311 afs::File::create(&path).await?;
312 }
313 }
314
315 Ok(true)
316}