sevenz_rust2/
en_funcs.rs

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