this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat(bzip2): add bzip2 support

+288
+22
Cargo.lock
··· 125 125 ] 126 126 127 127 [[package]] 128 + name = "bzip2" 129 + version = "0.4.4" 130 + source = "registry+https://github.com/rust-lang/crates.io-index" 131 + checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" 132 + dependencies = [ 133 + "bzip2-sys", 134 + "libc", 135 + ] 136 + 137 + [[package]] 138 + name = "bzip2-sys" 139 + version = "0.1.11+1.0.8" 140 + source = "registry+https://github.com/rust-lang/crates.io-index" 141 + checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" 142 + dependencies = [ 143 + "cc", 144 + "libc", 145 + "pkg-config", 146 + ] 147 + 148 + [[package]] 128 149 name = "cc" 129 150 version = "1.0.83" 130 151 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 185 206 dependencies = [ 186 207 "assert_cmd", 187 208 "assert_fs", 209 + "bzip2", 188 210 "clap", 189 211 "flate2", 190 212 "is-terminal",
+1
Cargo.toml
··· 16 16 tar = "0.4.38" 17 17 is-terminal = "0.4" 18 18 xz2 = "0.1.7" 19 + bzip2 = "0.4.4" 19 20 20 21 [dev-dependencies] 21 22 assert_cmd = "2.0.11"
+1
README.md
··· 9 9 10 10 Currently supports: 11 11 12 + - bzip2 12 13 - gzip 13 14 - tar 14 15 - xz
+105
src/bzip2.rs
··· 1 + use crate::utils::*; 2 + use bzip2::write::{BzDecoder, BzEncoder}; 3 + use bzip2::Compression; 4 + use std::{ 5 + fs::File, 6 + io::{self, Read, Write}, 7 + }; 8 + 9 + pub struct Bzip2 { 10 + pub level: u32, // 0-9 11 + } 12 + 13 + impl Default for Bzip2 { 14 + fn default() -> Self { 15 + Bzip2 { level: 6 } 16 + } 17 + } 18 + 19 + impl 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)] 72 + mod 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 + }
+24
src/main.rs
··· 1 + mod bzip2; 1 2 mod gzip; 2 3 mod tar; 3 4 mod utils; ··· 29 30 30 31 /// xz compression 31 32 Xz(XzArgs), 33 + 34 + /// bzip2 compression 35 + #[clap(visible_alias = "bz2")] 36 + Bzip2(Bzip2Args), 32 37 } 33 38 34 39 #[derive(Args, Debug)] ··· 93 98 /// Level of compression 94 99 /// 95 100 /// This is an int 0-9, with 0 being no compression and 9 being highest compression. 101 + #[arg(long, default_value_t = 6)] 102 + level: u32, 103 + } 104 + 105 + #[derive(Args, Debug)] 106 + struct Bzip2Args { 107 + #[clap(flatten)] 108 + common_args: CommonArgs, 109 + 110 + /// Level of compression 111 + /// 112 + /// This is an int 0-9, with 0 being no compression and 9 being highest compression. 113 + /// TODO: Support keywords none, fast, best 114 + /// Correspond to 0, 1, 9 96 115 #[arg(long, default_value_t = 6)] 97 116 level: u32, 98 117 } ··· 275 294 xz::Xz { level: args.level } 276 295 } 277 296 297 + fn parse_bzip2(args: &Bzip2Args) -> bzip2::Bzip2 { 298 + bzip2::Bzip2 { level: args.level } 299 + } 300 + 278 301 fn parse_tar(_args: &TarArgs) -> tar::Tar { 279 302 tar::Tar {} 280 303 } ··· 285 308 Some(Format::Tar(a)) => command(parse_tar(&a), &a.common_args), 286 309 Some(Format::Gzip(a)) => command(parse_gzip(&a), &a.common_args), 287 310 Some(Format::Xz(a)) => command(parse_xz(&a), &a.common_args), 311 + Some(Format::Bzip2(a)) => command(parse_bzip2(&a), &a.common_args), 288 312 _ => Err(io::Error::new(io::ErrorKind::Other, "unknown input")), 289 313 } 290 314 }
+135
tests/cli.rs
··· 393 393 394 394 Ok(()) 395 395 } 396 + 397 + /// Bzip2 roundtrip using files 398 + /// Compressing: input = test.txt, output = test.txt.bz2 399 + /// Extracting: input = test.txt.bz2, output = test.txt 400 + /// 401 + /// ``` bash 402 + /// cmprss bzip2 test.txt test.txt.bz2 403 + /// cmprss bzip2 --extract --ignore-pipes test.txt.bz2 404 + /// ``` 405 + #[test] 406 + fn bzip2_roundtrip_explicit() -> Result<(), Box<dyn std::error::Error>> { 407 + let file = assert_fs::NamedTempFile::new("test.txt")?; 408 + file.write_str("garbage data for testing")?; 409 + 410 + let working_dir = assert_fs::TempDir::new()?; 411 + let archive = working_dir.child("test.txt.bz2"); 412 + archive.assert(predicate::path::missing()); 413 + 414 + let mut compress = Command::cargo_bin("cmprss")?; 415 + compress 416 + .current_dir(&working_dir) 417 + .arg("bzip2") 418 + .arg(file.path()) 419 + .arg(archive.path()); 420 + compress.assert().success(); 421 + archive.assert(predicate::path::is_file()); 422 + 423 + let mut extract = Command::cargo_bin("cmprss")?; 424 + extract 425 + .current_dir(&working_dir) 426 + .arg("bzip2") 427 + .arg("--ignore-pipes") 428 + .arg("--extract") 429 + .arg(archive.path()); 430 + extract.assert().success(); 431 + 432 + // Assert the files are identical 433 + working_dir 434 + .child("test.txt") 435 + .assert(predicate::path::eq_file(file.path())); 436 + 437 + Ok(()) 438 + } 439 + 440 + /// Bzip2 roundtrip using stdin 441 + /// Compressing: input = stdin, output = test.txt.bz2 442 + /// Extracting: input = stdin(test.txt.bz2), output = test.txt 443 + /// 444 + /// ``` bash 445 + /// cat test.txt | cmprss bzip2 test.txt.bz2 446 + /// cat test.txt.bz2 | cmprss bzip2 --extract out.txt 447 + /// ``` 448 + #[test] 449 + fn bzip2_roundtrip_stdin() -> Result<(), Box<dyn std::error::Error>> { 450 + let file = assert_fs::NamedTempFile::new("test.txt")?; 451 + file.write_str("garbage data for testing")?; 452 + 453 + let working_dir = assert_fs::TempDir::new()?; 454 + let archive = working_dir.child("test.txt.bz2"); 455 + archive.assert(predicate::path::missing()); 456 + 457 + // Pipe file to stdin 458 + let mut compress = Command::cargo_bin("cmprss")?; 459 + compress 460 + .current_dir(&working_dir) 461 + .arg("bzip2") 462 + .arg("test.txt.bz2") 463 + .stdin(Stdio::from(File::open(file.path())?)); 464 + compress.assert().success(); 465 + archive.assert(predicate::path::is_file()); 466 + 467 + let mut extract = Command::cargo_bin("cmprss")?; 468 + extract 469 + .current_dir(&working_dir) 470 + .arg("bzip2") 471 + .stdin(Stdio::from(File::open(archive.path())?)) 472 + .arg("--extract") 473 + .arg("out.txt"); 474 + extract.assert().success(); 475 + 476 + // Assert the files are identical 477 + working_dir 478 + .child("out.txt") 479 + .assert(predicate::path::eq_file(file.path())); 480 + 481 + Ok(()) 482 + } 483 + 484 + /// Bzip2 roundtrip using stdout 485 + /// Compressing: input = test.txt, output = stdout 486 + /// Extracting: input = test.txt.bz2, output = stdout 487 + /// 488 + /// ``` bash 489 + /// cmprss bzip2 test.txt > test.txt.bz2 490 + /// cmprss bzip2 --extract test.txt.bz2 > out.txt 491 + /// ``` 492 + #[test] 493 + fn bzip2_roundtrip_stdout() -> Result<(), Box<dyn std::error::Error>> { 494 + let file = assert_fs::NamedTempFile::new("test.txt")?; 495 + file.write_str("garbage data for testing")?; 496 + 497 + let working_dir = assert_fs::TempDir::new()?; 498 + let archive = working_dir.child("test.txt.bz2"); 499 + archive.assert(predicate::path::missing()); 500 + 501 + // Compress file to stdout 502 + let mut compress = Command::cargo_bin("cmprss")?; 503 + compress 504 + .current_dir(&working_dir) 505 + .arg("bzip2") 506 + .arg(file.path()) 507 + .stdout(Stdio::from(File::create(archive.path())?)); 508 + compress.assert().success(); 509 + archive.assert(predicate::path::is_file()); 510 + 511 + // Extract file to stdout 512 + let mut extract = Command::cargo_bin("cmprss")?; 513 + extract 514 + .current_dir(&working_dir) 515 + .arg("bzip2") 516 + .arg("--ignore-stdin") 517 + .arg("--extract") 518 + .arg(archive.path()) 519 + .arg("out.txt"); 520 + // TODO: This fails, but manual testing shows it works fine 521 + //.stdout(Stdio::from(File::create("out.txt")?)); 522 + extract.assert().success(); 523 + 524 + // Assert the files are identical 525 + working_dir 526 + .child("out.txt") 527 + .assert(predicate::path::eq_file(file.path())); 528 + 529 + Ok(()) 530 + }