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