1#[cfg(target_os = "macos")]
2use std::os::macos::fs::FileTimesExt;
3#[cfg(windows)]
4use std::os::windows::fs::FileTimesExt;
5use std::{
6 fs::FileTimes,
7 io::{Read, Seek},
8 path::{Path, PathBuf},
9};
10
11use crate::{Error, Password, *};
12
13#[cfg_attr(docsrs, doc(cfg(feature = "util")))]
21pub fn decompress_file(src_path: impl AsRef<Path>, dest: impl AsRef<Path>) -> Result<(), Error> {
22 let file = std::fs::File::open(src_path.as_ref())
23 .map_err(|e| Error::file_open(e, src_path.as_ref().to_string_lossy().to_string()))?;
24 decompress(file, dest)
25}
26
27#[cfg_attr(docsrs, doc(cfg(feature = "util")))]
37pub fn decompress_file_with_extract_fn(
38 src_path: impl AsRef<Path>,
39 dest: impl AsRef<Path>,
40 extract_fn: impl FnMut(&ArchiveEntry, &mut dyn Read, &PathBuf) -> Result<bool, Error>,
41) -> Result<(), Error> {
42 let file = std::fs::File::open(src_path.as_ref())
43 .map_err(|e| Error::file_open(e, src_path.as_ref().to_string_lossy().to_string()))?;
44 decompress_with_extract_fn(file, dest, extract_fn)
45}
46
47#[cfg_attr(docsrs, doc(cfg(feature = "util")))]
53pub fn decompress<R: Read + Seek>(src_reader: R, dest: impl AsRef<Path>) -> Result<(), Error> {
54 decompress_with_extract_fn(src_reader, dest, default_entry_extract_fn)
55}
56
57#[cfg(not(target_arch = "wasm32"))]
66#[cfg_attr(docsrs, doc(cfg(feature = "util")))]
67pub fn decompress_with_extract_fn<R: Read + Seek>(
68 src_reader: R,
69 dest: impl AsRef<Path>,
70 extract_fn: impl FnMut(&ArchiveEntry, &mut dyn Read, &PathBuf) -> Result<bool, Error>,
71) -> Result<(), Error> {
72 decompress_impl(src_reader, dest, Password::empty(), extract_fn)
73}
74
75#[cfg(all(feature = "aes256", not(target_arch = "wasm32")))]
82#[cfg_attr(docsrs, doc(cfg(all(feature = "aes256", feature = "util"))))]
83pub fn decompress_file_with_password(
84 src_path: impl AsRef<Path>,
85 dest: impl AsRef<Path>,
86 password: Password,
87) -> Result<(), Error> {
88 let file = std::fs::File::open(src_path.as_ref())
89 .map_err(|e| Error::file_open(e, src_path.as_ref().to_string_lossy().to_string()))?;
90 decompress_with_password(file, dest, password)
91}
92
93#[cfg(all(feature = "aes256", not(target_arch = "wasm32")))]
100#[cfg_attr(docsrs, doc(cfg(all(feature = "aes256", feature = "util"))))]
101pub fn decompress_with_password<R: Read + Seek>(
102 src_reader: R,
103 dest: impl AsRef<Path>,
104 password: Password,
105) -> Result<(), Error> {
106 decompress_impl(src_reader, dest, password, default_entry_extract_fn)
107}
108
109#[cfg(all(feature = "aes256", not(target_arch = "wasm32")))]
120#[cfg_attr(docsrs, doc(cfg(all(feature = "aes256", feature = "util"))))]
121pub fn decompress_with_extract_fn_and_password<R: Read + Seek>(
122 src_reader: R,
123 dest: impl AsRef<Path>,
124 password: Password,
125 extract_fn: impl FnMut(&ArchiveEntry, &mut dyn Read, &PathBuf) -> Result<bool, Error>,
126) -> Result<(), Error> {
127 decompress_impl(src_reader, dest, password, extract_fn)
128}
129
130#[cfg(not(target_arch = "wasm32"))]
131fn decompress_impl<R: Read + Seek>(
132 mut src_reader: R,
133 dest: impl AsRef<Path>,
134 password: Password,
135 mut extract_fn: impl FnMut(&ArchiveEntry, &mut dyn Read, &PathBuf) -> Result<bool, Error>,
136) -> Result<(), Error> {
137 use std::io::SeekFrom;
138
139 let pos = src_reader.stream_position().map_err(Error::io)?;
140 src_reader.seek(SeekFrom::Start(pos)).map_err(Error::io)?;
141 let mut seven = ArchiveReader::new(src_reader, password)?;
142 let dest = PathBuf::from(dest.as_ref());
143 if !dest.exists() {
144 std::fs::create_dir_all(&dest).map_err(Error::io)?;
145 }
146 seven.for_each_entries(|entry, reader| {
147 let dest_path = dest.join(entry.name());
148 extract_fn(entry, reader, &dest_path)
149 })?;
150
151 Ok(())
152}
153
154#[cfg(not(target_arch = "wasm32"))]
161#[cfg_attr(docsrs, doc(cfg(feature = "util")))]
162pub fn default_entry_extract_fn(
163 entry: &ArchiveEntry,
164 reader: &mut dyn Read,
165 dest: &PathBuf,
166) -> Result<bool, Error> {
167 use std::{fs::File, io::BufWriter};
168
169 if entry.is_directory() {
170 let dir = dest;
171 if !dir.exists() {
172 std::fs::create_dir_all(dir).map_err(Error::io)?;
173 }
174 } else {
175 let path = dest;
176 path.parent().and_then(|p| {
177 if !p.exists() {
178 std::fs::create_dir_all(p).ok()
179 } else {
180 None
181 }
182 });
183 let file = File::create(path)
184 .map_err(|e| Error::file_open(e, path.to_string_lossy().to_string()))?;
185 if entry.size() > 0 {
186 let mut writer = BufWriter::new(file);
187 std::io::copy(reader, &mut writer).map_err(Error::io)?;
188
189 let file = writer.get_mut();
190 let file_times = FileTimes::new()
191 .set_accessed(entry.access_date().into())
192 .set_modified(entry.last_modified_date().into());
193
194 #[cfg(any(windows, target_os = "macos"))]
195 let file_times = file_times.set_created(entry.creation_date().into());
196
197 let _ = file.set_times(file_times);
198 }
199 }
200
201 Ok(true)
202}