1use std::path::{Path, PathBuf};
2
3use async_fs as afs;
4use futures::io::{AsyncRead, AsyncReadExt, AsyncSeek, AsyncSeekExt};
5use std::future::Future;
6use std::io::{Read, Seek, SeekFrom};
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) => futures::io::SeekFrom::Start(n),
33 SeekFrom::End(i) => futures::io::SeekFrom::End(i),
34 SeekFrom::Current(i) => futures::io::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 futures::io::AsyncRead + Unpin + 'a),
76 &'a Path,
77 ) -> Pin<Box<dyn Future<Output = Result<bool, Error>> + 'a>>
78 + 'static,
79) -> Result<(), Error> {
80 decompress_path_impl(
81 src_path.as_ref(),
82 dest.as_ref().to_path_buf(),
83 Password::empty(),
84 move |entry, reader, path| extract_fn(entry, reader, path),
85 )
86 .await
87}
88
89pub async fn decompress<R: AsyncRead + AsyncSeek + Unpin>(
95 mut src_reader: R,
96 dest: impl AsRef<Path>,
97) -> Result<(), Error> {
98 let pos = AsyncSeekExt::stream_position(&mut src_reader).await?;
99 AsyncSeekExt::seek(&mut src_reader, futures::io::SeekFrom::Start(pos)).await?;
100 decompress_impl(
101 src_reader,
102 dest,
103 Password::empty(),
104 |entry, reader, dest| Box::pin(default_entry_extract_fn(entry, reader, dest)),
105 )
106 .await
107}
108
109#[cfg(not(target_arch = "wasm32"))]
118pub async fn decompress_with_extract_fn<R: AsyncRead + AsyncSeek + Unpin>(
119 mut src_reader: R,
120 dest: impl AsRef<Path>,
121 extract_fn: impl for<'a> FnMut(
122 &'a ArchiveEntry,
123 &'a mut (dyn futures::io::AsyncRead + Unpin + 'a),
124 &'a Path,
125 ) -> Pin<Box<dyn Future<Output = Result<bool, Error>> + 'a>>
126 + 'static,
127) -> Result<(), Error> {
128 let pos = AsyncSeekExt::stream_position(&mut src_reader).await?;
129 AsyncSeekExt::seek(&mut src_reader, futures::io::SeekFrom::Start(pos)).await?;
130 decompress_impl(src_reader, dest, Password::empty(), extract_fn).await
131}
132
133#[cfg(all(feature = "aes256", not(target_arch = "wasm32")))]
140pub async fn decompress_file_with_password(
141 src_path: impl AsRef<Path>,
142 dest: impl AsRef<Path>,
143 password: Password,
144) -> Result<(), Error> {
145 let dest_path = dest.as_ref().to_path_buf();
146 decompress_path_impl(
147 src_path.as_ref(),
148 dest_path,
149 password,
150 |entry, reader, dest| Box::pin(default_entry_extract_fn(entry, reader, dest)),
151 )
152 .await
153}
154
155#[cfg(all(feature = "aes256", not(target_arch = "wasm32")))]
162pub async fn decompress_with_password<R: AsyncRead + AsyncSeek + Unpin>(
163 mut src_reader: R,
164 dest: impl AsRef<Path>,
165 password: Password,
166) -> Result<(), Error> {
167 let pos = AsyncSeekExt::stream_position(&mut src_reader).await?;
168 AsyncSeekExt::seek(&mut src_reader, futures::io::SeekFrom::Start(pos)).await?;
169 decompress_impl(src_reader, dest, password, |entry, reader, dest| {
170 Box::pin(default_entry_extract_fn(entry, reader, dest))
171 })
172 .await
173}
174
175#[cfg(all(feature = "aes256", not(target_arch = "wasm32")))]
186pub async fn decompress_with_extract_fn_and_password<R: AsyncRead + AsyncSeek + Unpin>(
187 mut src_reader: R,
188 dest: impl AsRef<Path>,
189 password: Password,
190 extract_fn: impl for<'a> FnMut(
191 &'a ArchiveEntry,
192 &'a mut (dyn futures::io::AsyncRead + Unpin + 'a),
193 &'a Path,
194 ) -> Pin<Box<dyn Future<Output = Result<bool, Error>> + 'a>>
195 + 'static,
196) -> Result<(), Error> {
197 let pos = AsyncSeekExt::stream_position(&mut src_reader).await?;
198 AsyncSeekExt::seek(&mut src_reader, futures::io::SeekFrom::Start(pos)).await?;
199 decompress_impl(src_reader, dest, password, extract_fn).await
200}
201
202#[cfg(not(target_arch = "wasm32"))]
203async fn decompress_impl<R: AsyncRead + AsyncSeek + Unpin>(
204 mut src_reader: R,
205 dest: impl AsRef<Path>,
206 password: Password,
207 extract_fn: impl for<'a> FnMut(
208 &'a ArchiveEntry,
209 &'a mut (dyn futures::io::AsyncRead + Unpin + 'a),
210 &'a Path,
211 ) -> Pin<Box<dyn Future<Output = Result<bool, Error>> + 'a>>
212 + 'static,
213) -> Result<(), Error> {
214 let pos = AsyncSeekExt::stream_position(&mut src_reader).await?;
215 AsyncSeekExt::seek(&mut src_reader, futures::io::SeekFrom::Start(pos)).await?;
216 let mut seven = ArchiveReader::new(src_reader, password).await?;
217 let dest = PathBuf::from(dest.as_ref());
218 if !dest.exists() {
219 afs::create_dir_all(&dest).await?;
220 }
221 let extract_fn_cell = std::rc::Rc::new(std::cell::RefCell::new(extract_fn));
222 seven
223 .for_each_entries(|entry, reader| {
224 let dest_path = dest.join(entry.name());
225 let extract_fn_cell = std::rc::Rc::clone(&extract_fn_cell);
226 Box::pin(async move {
227 let fut = {
228 let mut f = extract_fn_cell.borrow_mut();
229 f(entry, reader, dest_path.as_path())
230 };
231 fut.await
232 })
233 })
234 .await?;
235
236 Ok(())
237}
238
239#[cfg(not(target_arch = "wasm32"))]
240async fn decompress_path_impl(
241 src_path: &Path,
242 dest: PathBuf,
243 password: Password,
244 extract_fn: impl for<'a> FnMut(
245 &'a ArchiveEntry,
246 &'a mut (dyn futures::io::AsyncRead + Unpin + 'a),
247 &'a Path,
248 ) -> Pin<Box<dyn Future<Output = Result<bool, Error>> + 'a>>
249 + 'static,
250) -> Result<(), Error> {
251 let mut seven = ArchiveReader::open(src_path, password).await?;
252 if !dest.exists() {
253 afs::create_dir_all(&dest).await?;
254 }
255 let extract_fn_cell = std::rc::Rc::new(std::cell::RefCell::new(extract_fn));
256 seven
257 .for_each_entries(|entry, reader| {
258 let dest_path = dest.join(entry.name());
259 let extract_fn_cell = std::rc::Rc::clone(&extract_fn_cell);
260 Box::pin(async move {
261 let fut = {
262 let mut f = extract_fn_cell.borrow_mut();
263 f(entry, reader, dest_path.as_path())
264 };
265 fut.await
266 })
267 })
268 .await?;
269 Ok(())
270}
271
272#[cfg(not(target_arch = "wasm32"))]
279pub async fn default_entry_extract_fn(
280 entry: &ArchiveEntry,
281 reader: &mut (dyn futures::io::AsyncRead + Unpin),
282 dest: &Path,
283) -> Result<bool, Error> {
284 if entry.is_directory() {
285 let dir = dest.to_path_buf();
286 afs::create_dir_all(&dir).await?;
287 } else {
288 let path = dest.to_path_buf();
289 if let Some(p) = path.parent() {
290 if !p.exists() {
291 afs::create_dir_all(p).await?;
292 }
293 }
294 if entry.size() > 0 {
295 let mut data = Vec::new();
296 AsyncReadExt::read_to_end(reader, &mut data)
297 .await
298 .map_err(|e| Error::io_msg(e, "read entry data"))?;
299 afs::write(&path, &data).await?;
300 } else {
301 afs::File::create(&path).await?;
302 }
303 }
304
305 Ok(true)
306}