sevenz_rust/
en_funcs.rs

1//! 7z Compressor helper functions
2//!
3
4use std::{
5    fs::File,
6    io::{Seek, Write},
7    path::{Path, PathBuf},
8};
9
10use crate::*;
11
12/// hepler function to compress `src` path to `dest` writer
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")]
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
46/// hepler function to compress `src` path to `dest` path
47pub 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    /// [Solid compression](https://en.wikipedia.org/wiki/Solid_compression)
122    /// compress all files in [path].
123    /// With multiple files in one block.
124    ///
125    #[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    /// [Non-solid compression]
136    /// compress all files in [path].
137    /// With one file per block.
138    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; //4G
172fn 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}