diff --git a/CHANGELOG.md b/CHANGELOG.md index e44b061..ab5fda3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ Categories Used: - Add multithreading support for `zstd` compression [\#689](https://github.com/ouch-org/ouch/pull/689) ([nalabrie](https://github.com/nalabrie)) - Add `bzip3` support [\#522](https://github.com/ouch-org/ouch/pull/522) ([freijon](https://github.com/freijon)) - Add `--remove` flag for decompression subcommand to remove files after successful decompression [\#757](https://github.com/ouch-org/ouch/pull/757) ([ttys3](https://github.com/ttys3)) +- Add `br` (Brotli) support [\#765](https://github.com/ouch-org/ouch/pull/765) ([killercup](https://github.com/killercup)) ### Bug Fixes diff --git a/Cargo.lock b/Cargo.lock index 83712b5..5fad15b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -28,6 +28,21 @@ dependencies = [ "memchr", ] +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + [[package]] name = "anstream" version = "0.6.18" @@ -198,6 +213,27 @@ dependencies = [ "generic-array", ] +[[package]] +name = "brotli" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "4.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74fa05ad7d803d413eb8380983b092cbbaf9a85f151b871360e7b00cd7060b37" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + [[package]] name = "bstr" version = "1.11.0" @@ -1032,6 +1068,7 @@ version = "0.5.1" dependencies = [ "assert_cmd", "atty", + "brotli", "bstr", "bytesize", "bzip2", diff --git a/Cargo.toml b/Cargo.toml index f9f602d..4192a77 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ description = "A command-line utility for easily compressing and decompressing f [dependencies] atty = "0.2.14" +brotli = "7.0.0" bstr = { version = "1.10.0", default-features = false, features = ["std"] } bytesize = "1.3.0" bzip2 = "0.4.4" diff --git a/README.md b/README.md index 70caede..ae34414 100644 --- a/README.md +++ b/README.md @@ -111,9 +111,9 @@ Output: # Supported formats -| Format | `.tar` | `.zip` | `7z` | `.gz` | `.xz`, `.lzma` | `.bz`, `.bz2` | `.bz3` | `.lz4` | `.sz` (Snappy) | `.zst` | `.rar` | -|:---------:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:| -| Supported | ✓ | ✓¹ | ✓¹ | ✓² | ✓ | ✓ | ✓ | ✓ | ✓² | ✓² | ✓³ | +| Format | `.tar` | `.zip` | `7z` | `.gz` | `.xz`, `.lzma` | `.bz`, `.bz2` | `.bz3` | `.lz4` | `.sz` (Snappy) | `.zst` | `.rar` | `.br` | +|:---------:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:| +| Supported | ✓ | ✓¹ | ✓¹ | ✓² | ✓ | ✓ | ✓ | ✓ | ✓² | ✓² | ✓³ | ✓ | ✓: Supports compression and decompression. diff --git a/src/cli/args.rs b/src/cli/args.rs index 22b8372..5701f4e 100644 --- a/src/cli/args.rs +++ b/src/cli/args.rs @@ -5,7 +5,7 @@ use clap::{Parser, ValueHint}; // Ouch command line options (docstrings below are part of --help) /// A command-line utility for easily compressing and decompressing files and directories. /// -/// Supported formats: tar, zip, gz, 7z, xz/lzma, bz/bz2, bz3, lz4, sz (Snappy), zst and rar. +/// Supported formats: tar, zip, gz, 7z, xz/lzma, bz/bz2, bz3, lz4, sz (Snappy), zst, rar and br. /// /// Repository: https://github.com/ouch-org/ouch #[derive(Parser, Debug, PartialEq)] diff --git a/src/commands/compress.rs b/src/commands/compress.rs index 180bb2e..59987b8 100644 --- a/src/commands/compress.rs +++ b/src/commands/compress.rs @@ -83,6 +83,12 @@ pub fn compress_files( zstd_encoder.multithread(num_cpus::get_physical() as u32)?; Box::new(zstd_encoder.auto_finish()) } + Brotli => { + let default_level = 11; // Same as brotli CLI, default to highest compression + let level = level.unwrap_or(default_level).clamp(0, 11) as u32; + let win_size = 22; // default to 2^22 = 4 MiB window size + Box::new(brotli::CompressorWriter::new(encoder, BUFFER_CAPACITY, level, win_size)) + } Tar | Zip | Rar | SevenZip => unreachable!(), }; Ok(encoder) @@ -95,7 +101,7 @@ pub fn compress_files( } match first_format { - Gzip | Bzip | Bzip3 | Lz4 | Lzma | Snappy | Zstd => { + Gzip | Bzip | Bzip3 | Lz4 | Lzma | Snappy | Zstd | Brotli => { writer = chain_writer_encoder(&first_format, writer)?; let mut reader = fs::File::open(&files[0])?; diff --git a/src/commands/decompress.rs b/src/commands/decompress.rs index 1d0152a..6d2efee 100644 --- a/src/commands/decompress.rs +++ b/src/commands/decompress.rs @@ -119,6 +119,7 @@ pub fn decompress_file(options: DecompressOptions) -> crate::Result<()> { Lzma => Box::new(xz2::read::XzDecoder::new(decoder)), Snappy => Box::new(snap::read::FrameDecoder::new(decoder)), Zstd => Box::new(zstd::stream::Decoder::new(decoder)?), + Brotli => Box::new(brotli::Decompressor::new(decoder, BUFFER_CAPACITY)), Tar | Zip | Rar | SevenZip => unreachable!(), }; Ok(decoder) @@ -131,7 +132,7 @@ pub fn decompress_file(options: DecompressOptions) -> crate::Result<()> { } let files_unpacked = match first_extension { - Gzip | Bzip | Bzip3 | Lz4 | Lzma | Snappy | Zstd => { + Gzip | Bzip | Bzip3 | Lz4 | Lzma | Snappy | Zstd | Brotli => { reader = chain_reader_decoder(&first_extension, reader)?; let mut writer = match utils::ask_to_create_file(&options.output_file_path, options.question_policy)? { diff --git a/src/commands/list.rs b/src/commands/list.rs index 1821d6d..a64c55c 100644 --- a/src/commands/list.rs +++ b/src/commands/list.rs @@ -55,6 +55,7 @@ pub fn list_archive_contents( Lzma => Box::new(xz2::read::XzDecoder::new(decoder)), Snappy => Box::new(snap::read::FrameDecoder::new(decoder)), Zstd => Box::new(zstd::stream::Decoder::new(decoder)?), + Brotli => Box::new(brotli::Decompressor::new(decoder, BUFFER_CAPACITY)), Tar | Zip | Rar | SevenZip => unreachable!(), }; Ok(decoder) @@ -112,7 +113,7 @@ pub fn list_archive_contents( Box::new(sevenz::list_archive(archive_path, password)?) } - Gzip | Bzip | Bzip3 | Lz4 | Lzma | Snappy | Zstd => { + Gzip | Bzip | Bzip3 | Lz4 | Lzma | Snappy | Zstd | Brotli => { panic!("Not an archive! This should never happen, if it does, something is wrong with `CompressionFormat::is_archive()`. Please report this error!"); } }; diff --git a/src/extension.rs b/src/extension.rs index f8d7389..7250a57 100644 --- a/src/extension.rs +++ b/src/extension.rs @@ -21,6 +21,7 @@ pub const SUPPORTED_EXTENSIONS: &[&str] = &[ #[cfg(feature = "unrar")] "rar", "7z", + "br", ]; pub const SUPPORTED_ALIASES: &[&str] = &["tgz", "tbz", "tlz4", "txz", "tzlma", "tsz", "tzst"]; @@ -96,6 +97,8 @@ pub enum CompressionFormat { Rar, /// .7z SevenZip, + /// .br + Brotli, } impl CompressionFormat { @@ -111,6 +114,7 @@ impl CompressionFormat { Lzma => false, Snappy => false, Zstd => false, + Brotli => false, } } } @@ -136,6 +140,7 @@ fn to_extension(ext: &[u8]) -> Option { b"zst" => &[Zstd], b"rar" => &[Rar], b"7z" => &[SevenZip], + b"br" => &[Brotli], _ => return None, }, ext.to_str_lossy(), diff --git a/tests/integration.rs b/tests/integration.rs index 779766a..7241cd1 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -44,6 +44,7 @@ enum FileExtension { Sz, Xz, Zst, + Br, } #[derive(Arbitrary, Debug, Display)] diff --git a/tests/snapshots/ui__ui_test_usage_help_flag.snap b/tests/snapshots/ui__ui_test_usage_help_flag.snap index e807565..6913b83 100644 --- a/tests/snapshots/ui__ui_test_usage_help_flag.snap +++ b/tests/snapshots/ui__ui_test_usage_help_flag.snap @@ -5,7 +5,7 @@ snapshot_kind: text --- A command-line utility for easily compressing and decompressing files and directories. -Supported formats: tar, zip, gz, 7z, xz/lzma, bz/bz2, bz3, lz4, sz (Snappy), zst and rar. +Supported formats: tar, zip, gz, 7z, xz/lzma, bz/bz2, bz3, lz4, sz (Snappy), zst, rar and br. Repository: https://github.com/ouch-org/ouch