1use std::{
5 fs::File,
6 io::{Seek, Write},
7 path::{Path, PathBuf},
8};
9
10use crate::*;
11
12pub fn compress<W: Write + Seek>(src: impl AsRef<Path>, dest: W) -> Result<W, Error> {
14 let mut z = SevenZWriter::new(dest)?;
15 let parent = if src.as_ref().is_dir() {
16 src.as_ref()
17 } else {
18 src.as_ref().parent().unwrap_or(src.as_ref())
19 };
20 compress_path(src.as_ref(), parent, &mut z)?;
21 z.finish().map_err(Error::io)
22}
23
24#[cfg(feature = "aes256")]
25pub fn compress_encypted<W: Write + Seek>(
26 src: impl AsRef<Path>,
27 dest: W,
28 password: Password,
29) -> Result<W, Error> {
30 let mut z = SevenZWriter::new(dest)?;
31 if !password.is_empty() {
32 z.set_content_methods(vec![
33 aes256sha256::AesEncoderOptions::new(password).into(),
34 SevenZMethod::LZMA2.into(),
35 ]);
36 }
37 let parent = if src.as_ref().is_dir() {
38 src.as_ref()
39 } else {
40 src.as_ref().parent().unwrap_or(src.as_ref())
41 };
42 compress_path(src.as_ref(), parent, &mut z)?;
43 z.finish().map_err(Error::io)
44}
45
46pub fn compress_to_path(src: impl AsRef<Path>, dest: impl AsRef<Path>) -> Result<(), Error> {
48 if let Some(p) = dest.as_ref().parent() {
49 if !p.exists() {
50 std::fs::create_dir_all(p)
51 .map_err(|e| Error::io_msg(e, format!("Create dir failed:{:?}", dest.as_ref())))?;
52 }
53 }
54 compress(
55 src,
56 File::create(dest.as_ref())
57 .map_err(|e| Error::file_open(e, dest.as_ref().to_string_lossy().to_string()))?,
58 )?;
59 Ok(())
60}
61
62#[cfg(feature = "aes256")]
63pub fn compress_to_path_encrypted(
64 src: impl AsRef<Path>,
65 dest: impl AsRef<Path>,
66 password: Password,
67) -> Result<(), Error> {
68 if let Some(p) = dest.as_ref().parent() {
69 if !p.exists() {
70 std::fs::create_dir_all(p)
71 .map_err(|e| Error::io_msg(e, format!("Create dir failed:{:?}", dest.as_ref())))?;
72 }
73 }
74 compress_encypted(
75 src,
76 File::create(dest.as_ref())
77 .map_err(|e| Error::file_open(e, dest.as_ref().to_string_lossy().to_string()))?,
78 password,
79 )?;
80 Ok(())
81}
82
83fn compress_path<W: Write + Seek, P: AsRef<Path>>(
84 src: P,
85 root: &Path,
86 z: &mut SevenZWriter<W>,
87) -> Result<(), Error> {
88 let entry_name = src
89 .as_ref()
90 .strip_prefix(root)
91 .map_err(|e| Error::other(e.to_string()))?
92 .to_string_lossy()
93 .to_string();
94 let entry = SevenZArchiveEntry::from_path(src.as_ref(), entry_name);
95 let path = src.as_ref();
96 if path.is_dir() {
97 z.push_archive_entry::<&[u8]>(entry, None)?;
98 for dir in path
99 .read_dir()
100 .map_err(|e| Error::io_msg(e, "error read dir"))?
101 {
102 let dir = dir.map_err(Error::io)?;
103 let ftype = dir.file_type().map_err(Error::io)?;
104 if ftype.is_dir() || ftype.is_file() {
105 compress_path(dir.path(), root, z)?;
106 }
107 }
108 } else {
109 z.push_archive_entry(
110 entry,
111 Some(
112 File::open(path)
113 .map_err(|e| Error::file_open(e, path.to_string_lossy().to_string()))?,
114 ),
115 )?;
116 }
117 Ok(())
118}
119
120impl<W: Write + Seek> SevenZWriter<W> {
121 #[inline]
126 pub fn push_source_path(
127 &mut self,
128 path: impl AsRef<Path>,
129 filter: impl Fn(&Path) -> bool,
130 ) -> Result<&mut Self, crate::Error> {
131 encode_path(true, &path, self, filter)?;
132 Ok(self)
133 }
134
135 pub fn push_source_path_non_solid(
139 &mut self,
140 path: impl AsRef<Path>,
141 filter: impl Fn(&Path) -> bool,
142 ) -> Result<&mut Self, crate::Error> {
143 encode_path(false, &path, self, filter)?;
144 Ok(self)
145 }
146}
147
148fn collect_file_paths(
149 src: impl AsRef<Path>,
150 paths: &mut Vec<PathBuf>,
151 filter: &dyn Fn(&Path) -> bool,
152) -> std::io::Result<()> {
153 let path = src.as_ref();
154 if !filter(path) {
155 return Ok(());
156 }
157 if path.is_dir() {
158 for dir in path.read_dir()? {
159 let dir = dir?;
160 let ftype = dir.file_type()?;
161 if ftype.is_file() || ftype.is_dir() {
162 collect_file_paths(dir.path(), paths, filter)?;
163 }
164 }
165 } else {
166 paths.push(path.to_path_buf())
167 }
168 Ok(())
169}
170
171const MAX_BLOCK_SIZE: u64 = 4 * 1024 * 1024 * 1024; fn encode_path<W: Write + Seek>(
173 solid: bool,
174 src: impl AsRef<Path>,
175 zip: &mut SevenZWriter<W>,
176 filter: impl Fn(&Path) -> bool,
177) -> Result<(), crate::Error> {
178 let mut entries = Vec::new();
179 let mut paths = Vec::new();
180 collect_file_paths(&src, &mut paths, &filter).map_err(|e| {
181 crate::Error::io_msg(
182 e,
183 format!("Failed to collect entries from path:{:?}", src.as_ref()),
184 )
185 })?;
186 if !solid {
187 for ele in paths.into_iter() {
188 let name = ele
189 .strip_prefix(&src)
190 .unwrap()
191 .to_string_lossy()
192 .to_string();
193 zip.push_archive_entry(
194 SevenZArchiveEntry::from_path(ele.as_path(), name),
195 Some(File::open(ele.as_path()).map_err(crate::Error::io)?),
196 )?;
197 }
198 return Ok(());
199 }
200 let mut files = Vec::new();
201 let mut file_size = 0;
202 for ele in paths.into_iter() {
203 let size = ele.metadata()?.len();
204 let name = ele
205 .strip_prefix(&src)
206 .unwrap()
207 .to_string_lossy()
208 .to_string();
209 if size >= MAX_BLOCK_SIZE {
210 zip.push_archive_entry(
211 SevenZArchiveEntry::from_path(ele.as_path(), name),
212 Some(File::open(ele.as_path()).map_err(crate::Error::io)?),
213 )?;
214 continue;
215 }
216 if file_size + size >= MAX_BLOCK_SIZE {
217 zip.push_archive_entries(entries, SeqReader::new(files))?;
218 entries = Vec::new();
219 files = Vec::new();
220 file_size = 0;
221 }
222 file_size += size;
223 entries.push(SevenZArchiveEntry::from_path(ele.as_path(), name));
224 files.push(LazyFileReader::new(ele).into());
225 }
226 if !entries.is_empty() {
227 zip.push_archive_entries(entries, SeqReader::new(files))?;
228 }
229
230 Ok(())
231}