this repo has no description
1use crate::utils::*;
2use bzip2::write::{BzDecoder, BzEncoder};
3use bzip2::Compression;
4use std::{
5 fs::File,
6 io::{self, Read, Write},
7};
8
9pub struct Bzip2 {
10 pub level: u32, // 0-9
11}
12
13impl Default for Bzip2 {
14 fn default() -> Self {
15 Bzip2 { level: 6 }
16 }
17}
18
19impl Compressor for Bzip2 {
20 /// The standard extension for the bz2 format.
21 fn extension(&self) -> &str {
22 "bz2"
23 }
24
25 /// Full name for bz2.
26 fn name(&self) -> &str {
27 "bzip2"
28 }
29
30 /// Compress an input file or pipe to a bz2 archive
31 fn compress(&self, input: CmprssInput, output: CmprssOutput) -> Result<(), io::Error> {
32 let mut input_stream = match input {
33 CmprssInput::Path(paths) => {
34 if paths.len() > 1 {
35 return cmprss_error("only 1 file can be compressed at a time");
36 }
37 Box::new(File::open(paths[0].as_path())?)
38 }
39 CmprssInput::Pipe(pipe) => Box::new(pipe) as Box<dyn Read + Send>,
40 };
41 let output_stream: Box<dyn Write + Send> = match output {
42 CmprssOutput::Path(path) => Box::new(File::create(path)?),
43 CmprssOutput::Pipe(pipe) => Box::new(pipe) as Box<dyn Write + Send>,
44 };
45 let mut encoder = BzEncoder::new(output_stream, Compression::new(self.level));
46 io::copy(&mut input_stream, &mut encoder)?;
47 Ok(())
48 }
49
50 /// Extract a bz2 archive to a file or pipe
51 fn extract(&self, input: CmprssInput, output: CmprssOutput) -> Result<(), io::Error> {
52 let mut input_stream = match input {
53 CmprssInput::Path(paths) => {
54 if paths.len() > 1 {
55 return cmprss_error("only 1 file can be extracted at a time");
56 }
57 Box::new(File::open(paths[0].as_path())?)
58 }
59 CmprssInput::Pipe(pipe) => Box::new(pipe) as Box<dyn Read + Send>,
60 };
61 let output_stream: Box<dyn Write + Send> = match output {
62 CmprssOutput::Path(path) => Box::new(File::create(path)?),
63 CmprssOutput::Pipe(pipe) => Box::new(pipe) as Box<dyn Write + Send>,
64 };
65 let mut decoder = BzDecoder::new(output_stream);
66 io::copy(&mut input_stream, &mut decoder)?;
67 Ok(())
68 }
69}
70
71#[cfg(test)]
72mod tests {
73 use super::*;
74 use assert_fs::prelude::*;
75 use predicates::prelude::*;
76
77 #[test]
78 fn roundtrip() -> Result<(), Box<dyn std::error::Error>> {
79 let compressor = Bzip2::default();
80
81 let file = assert_fs::NamedTempFile::new("test.txt")?;
82 file.write_str("garbage data for testing")?;
83 let working_dir = assert_fs::TempDir::new()?;
84 let archive = working_dir.child("archive.".to_owned() + compressor.extension());
85 archive.assert(predicate::path::missing());
86
87 // Roundtrip compress/extract
88 compressor.compress(
89 CmprssInput::Path(vec![file.path().to_path_buf()]),
90 CmprssOutput::Path(archive.path().to_path_buf()),
91 )?;
92 archive.assert(predicate::path::is_file());
93 compressor.extract(
94 CmprssInput::Path(vec![archive.path().to_path_buf()]),
95 CmprssOutput::Path(working_dir.child("test.txt").path().to_path_buf()),
96 )?;
97
98 // Assert the files are identical
99 working_dir
100 .child("test.txt")
101 .assert(predicate::path::eq_file(file.path()));
102
103 Ok(())
104 }
105}