diff --git a/src/cli/args.rs b/src/cli/args.rs index 7cf037f..8e77f7f 100644 --- a/src/cli/args.rs +++ b/src/cli/args.rs @@ -44,10 +44,6 @@ pub struct CliArgs { /// Ouch and claps subcommands #[command(subcommand)] pub cmd: Subcommand, - - /// Compression raw level as each algo has - #[arg(short = 'l', long)] - pub level: Option, } #[derive(Parser, PartialEq, Eq, Debug)] @@ -63,6 +59,10 @@ pub enum Subcommand { /// The resulting file. Its extensions can be used to specify the compression formats #[arg(required = true, value_hint = ValueHint::FilePath)] output: PathBuf, + + /// Compression raw level as each algo has + #[arg(short, long)] + level: Option, }, /// Decompresses one or more files, optionally into another folder #[command(visible_alias = "d")] diff --git a/src/commands/compress.rs b/src/commands/compress.rs index 44eb9b1..71d6e46 100644 --- a/src/commands/compress.rs +++ b/src/commands/compress.rs @@ -45,40 +45,41 @@ pub fn compress_files( // by default, ParCompress uses a default compression level of 3 // instead of the regular default that flate2 uses gzp::par::compress::ParCompress::::builder() - .compression_level(level.map_or_else(|| Default::default(), |l| gzp::Compression::new(l as u32))) + .compression_level(level.map_or_else(Default::default, |l| gzp::Compression::new(l as u32))) .from_writer(encoder), ), Bzip => Box::new(bzip2::write::BzEncoder::new( encoder, - level.map_or_else(|| Default::default(), |l| bzip2::Compression::new(l as u32)), + level.map_or_else(Default::default, |l| { + if l < 1 || l > 9 { + bzip2::Compression::new(1) + } else { + bzip2::Compression::new(l as u32) + } + }), )), Lz4 => Box::new(lzzzz::lz4f::WriteCompressor::new( encoder, lzzzz::lz4f::PreferencesBuilder::new() - .compression_level(level.map_or_else(|| Default::default(), |l| l as i32)) + .compression_level(level.map_or(0, |l| l as i32)) .build(), )?), Lzma => Box::new(xz2::write::XzEncoder::new( encoder, - level.map_or_else(|| Default::default(), |l| l as u32), + level.map_or(6, |l| if l < 0 || l > 9 { 6 } else { l as u32 }), )), Snappy => Box::new( gzp::par::compress::ParCompress::::builder() .compression_level(gzp::par::compress::Compression::new( - level.map_or_else(|| Default::default(), |l| l as u32), + level.map_or_else(Default::default, |l| l as u32), )) .from_writer(encoder), ), - Zstd => { - Box::new( - zstd::stream::write::Encoder::new(encoder, level.map_or_else(|| Default::default(), |l| l as i32)) - .unwrap() - .auto_finish(), - ) - // Safety: - // Encoder::new() can only fail if `level` is invalid, but Default::default() - // is guaranteed to be valid - } + Zstd => Box::new( + zstd::stream::write::Encoder::new(encoder, level.map_or(0, |l| l as i32)) + .unwrap() + .auto_finish(), + ), Tar | Zip => unreachable!(), }; Ok(encoder) diff --git a/src/commands/mod.rs b/src/commands/mod.rs index e45e3fe..a4a3c82 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -44,6 +44,7 @@ pub fn run( Subcommand::Compress { files, output: output_path, + level, } => { // After cleaning, if there are no input files left, exit if files.is_empty() { @@ -80,7 +81,7 @@ pub fn run( args.quiet, question_policy, file_visibility_policy, - args.level, + level, ); if let Ok(true) = compress_result { diff --git a/tests/integration.rs b/tests/integration.rs index eec7a29..35832ac 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -133,3 +133,23 @@ fn multiple_files( ouch!("-A", "d", archive, "-d", after); assert_same_directory(before, after, !matches!(ext, DirectoryExtension::Zip)); } + +// compress and decompress a single file with comp levels +#[proptest(cases = 512)] +fn single_file_level( + ext: Extension, + #[any(size_range(0..8).lift())] exts: Vec, + #[strategy(-3i16..30)] level: i16, +) { + let dir = tempdir().unwrap(); + let dir = dir.path(); + let before = &dir.join("before"); + fs::create_dir(before).unwrap(); + let before_file = &before.join("file"); + let archive = &dir.join(format!("file.{}", merge_extensions(ext, exts))); + let after = &dir.join("after"); + fs::write(before_file, []).unwrap(); + ouch!("-A", "c", "-l", level.to_string(), before_file, archive); + ouch!("-A", "d", archive, "-d", after); + assert_same_directory(before, after, false); +}