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