From d0494504a1df4e13ecd4eeae0495066c81060cad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Marcos?= Date: Tue, 15 Jul 2025 15:44:34 -0300 Subject: [PATCH 1/5] Fix clippy warnings (#833) --- src/check.rs | 3 +-- src/commands/decompress.rs | 2 +- src/error.rs | 4 ++-- src/extension.rs | 2 +- src/utils/formatting.rs | 2 +- src/utils/fs.rs | 2 +- src/utils/question.rs | 8 ++++---- tests/integration.rs | 2 +- 8 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/check.rs b/src/check.rs index bcad3cf..c091b8a 100644 --- a/src/check.rs +++ b/src/check.rs @@ -56,8 +56,7 @@ pub fn check_mime_type( .ends_with(detected_format.compression_formats) { warning(format!( - "The file extension: `{}` differ from the detected extension: `{}`", - outer_ext, detected_format + "The file extension: `{outer_ext}` differ from the detected extension: `{detected_format}`" )); if !user_wants_to_continue(path, question_policy, QuestionAction::Decompression)? { diff --git a/src/commands/decompress.rs b/src/commands/decompress.rs index 6c254ad..780c392 100644 --- a/src/commands/decompress.rs +++ b/src/commands/decompress.rs @@ -295,7 +295,7 @@ pub fn decompress_file(options: DecompressOptions) -> crate::Result<()> { "Successfully decompressed archive in {}", nice_directory_display(options.output_dir) )); - info_accessible(format!("Files unpacked: {}", files_unpacked)); + info_accessible(format!("Files unpacked: {files_unpacked}")); if !input_is_stdin && options.remove { fs::remove_file(options.input_file_path)?; diff --git a/src/error.rs b/src/error.rs index b3c33df..167bc7b 100644 --- a/src/error.rs +++ b/src/error.rs @@ -139,8 +139,8 @@ impl FinalError { /// hint: Supported aliases are: tgz, tbz, tlz4, txz, tzlma, tsz, tzst /// ``` pub fn hint_all_supported_formats(self) -> Self { - self.hint(format!("Supported extensions are: {}", PRETTY_SUPPORTED_EXTENSIONS)) - .hint(format!("Supported aliases are: {}", PRETTY_SUPPORTED_ALIASES)) + self.hint(format!("Supported extensions are: {PRETTY_SUPPORTED_EXTENSIONS}")) + .hint(format!("Supported aliases are: {PRETTY_SUPPORTED_ALIASES}")) } } diff --git a/src/extension.rs b/src/extension.rs index 48be0cb..e886634 100644 --- a/src/extension.rs +++ b/src/extension.rs @@ -173,7 +173,7 @@ pub fn parse_format_flag(input: &OsStr) -> crate::Result> { .map(|extension| { to_extension(extension.as_bytes()).ok_or_else(|| Error::InvalidFormatFlag { text: input.to_owned(), - reason: format!("Unsupported extension '{}'", extension), + reason: format!("Unsupported extension '{extension}'"), }) }) .collect::>()?; diff --git a/src/utils/formatting.rs b/src/utils/formatting.rs index 3b82a09..8c045ca 100644 --- a/src/utils/formatting.rs +++ b/src/utils/formatting.rs @@ -109,7 +109,7 @@ impl std::fmt::Display for Bytes { debug_assert!(num >= 0.0); if num < 1_f64 { - return write!(f, "{:>6.2} B", num); + return write!(f, "{num:>6.2} B"); } let delimiter = 1000_f64; diff --git a/src/utils/fs.rs b/src/utils/fs.rs index f65f152..21c2457 100644 --- a/src/utils/fs.rs +++ b/src/utils/fs.rs @@ -82,7 +82,7 @@ pub fn rename_or_increment_filename(path: &Path) -> PathBuf { let number = number_str.parse::().unwrap_or(0); format!("{}_{}", base, number + 1) } - _ => format!("{}_1", filename), + _ => format!("{filename}_1"), }; let mut new_path = parent.join(new_filename); diff --git a/src/utils/question.rs b/src/utils/question.rs index 4b4c97e..fbed15c 100644 --- a/src/utils/question.rs +++ b/src/utils/question.rs @@ -187,7 +187,7 @@ impl<'a, T: Default> ChoicePrompt<'a, T> { #[cfg(not(feature = "allow_piped_choice"))] if !stdin().is_terminal() { - eprintln!("{}", message); + eprintln!("{message}"); eprintln!("Pass --yes to proceed"); return Ok(T::default()); } @@ -222,10 +222,10 @@ impl<'a, T: Default> ChoicePrompt<'a, T> { .collect::>() .join("/"); - format!("[{}]", choises) + format!("[{choises}]") }; - eprintln!("{} {}", message, choice_prompt); + eprintln!("{message} {choice_prompt}"); let mut answer = String::new(); let bytes_read = stdin_lock.read_line(&mut answer)?; @@ -284,7 +284,7 @@ impl<'a> Confirmation<'a> { #[cfg(not(feature = "allow_piped_choice"))] if !stdin().is_terminal() { - eprintln!("{}", message); + eprintln!("{message}"); eprintln!("Pass --yes to proceed"); return Ok(false); } diff --git a/tests/integration.rs b/tests/integration.rs index 9f870db..1883b0e 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -175,7 +175,7 @@ fn single_file_stdin( fs::create_dir(before).unwrap(); let before_file = &before.join("file"); let format = merge_extensions(&ext, &exts); - let archive = &dir.join(format!("file.{}", format)); + let archive = &dir.join(format!("file.{format}")); let after = &dir.join("after"); write_random_content( &mut fs::File::create(before_file).unwrap(), From b531a2585563413c1572aa5fb381ef269d772ba7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Marcos?= Date: Tue, 15 Jul 2025 15:50:50 -0300 Subject: [PATCH 2/5] Improve CONTRIBUTING.md (#845) --- CONTRIBUTING.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0a9b8e5..83ac503 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -27,6 +27,10 @@ Before opening the PR, open an issue to discuss your addition, this increases th [CHANGELOG.md]: https://github.com/ouch-org/ouch +### CI Tests + +The CI tests will run for a combination of features, `--no-default-features` will also be tested. + ## Dealing with UI tests We use snapshots to do UI testing and guarantee a consistent output, this way, you can catch accidental changes or see what output changed in the PR diff. @@ -46,3 +50,5 @@ cargo insta review ``` - After addressing all, you should be able to `git add` and `commit` accordingly. + +NOTE: Sometimes, you'll have to run these two commands multiple times. From cffa37741a5b860cba7d758c490090fb92a1c705 Mon Sep 17 00:00:00 2001 From: Mathias Zhang Date: Wed, 16 Jul 2025 03:27:52 +0800 Subject: [PATCH 3/5] Add listing and decompressing support for LZMA1 and Lzip (#838) * Replace xz2 dependency by liblzma * feat: Add list and decompressing support for legacy lzma (LZMA1) * feat: Add listing and decompressing support for lzip --- CHANGELOG.md | 2 + Cargo.lock | 42 +++++++++---------- Cargo.toml | 2 +- README.md | 11 +++-- src/cli/args.rs | 2 +- src/commands/compress.rs | 14 ++++++- src/commands/decompress.rs | 12 +++++- src/commands/list.rs | 12 +++++- src/error.rs | 4 +- src/extension.rs | 36 ++++++++-------- src/utils/fs.rs | 12 +++++- tests/integration.rs | 2 - tests/mime.rs | 5 +-- ...compress_missing_extension_with_rar-1.snap | 4 +- ...compress_missing_extension_with_rar-2.snap | 4 +- ...compress_missing_extension_with_rar-3.snap | 4 +- ...press_missing_extension_without_rar-1.snap | 4 +- ...press_missing_extension_without_rar-2.snap | 4 +- ...press_missing_extension_without_rar-3.snap | 4 +- ...i__ui_test_err_format_flag_with_rar-1.snap | 4 +- ...i__ui_test_err_format_flag_with_rar-2.snap | 4 +- ...i__ui_test_err_format_flag_with_rar-3.snap | 4 +- ...ui_test_err_format_flag_without_rar-1.snap | 4 +- ...ui_test_err_format_flag_without_rar-2.snap | 4 +- ...ui_test_err_format_flag_without_rar-3.snap | 4 +- .../ui__ui_test_usage_help_flag.snap | 2 +- 26 files changed, 121 insertions(+), 85 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e47501e..3062d1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,8 @@ Categories Used: - Merge folders in decompression [\#798](https://github.com/ouch-org/ouch/pull/798) ([tommady](https://github.com/tommady)) - Add `--no-smart-unpack` flag to decompression command to disable smart unpack [\#809](https://github.com/ouch-org/ouch/pull/809) ([talis-fb](https://github.com/talis-fb)) - Provide Nushell completions (packages still need to install them) [\#827](https://github.com/ouch-org/ouch/pull/827) ([FrancescElies](https://github.com/FrancescElies)) +- Support `.lz` decompression [\#838](https://github.com/ouch-org/ouch/pull/838) ([zzzsyyy](https://github.com/zzzsyyy)) +- Support `.lzma` decompression (and fix `.lzma` being a wrong alias for `.xz`) [\#838](https://github.com/ouch-org/ouch/pull/838) ([zzzsyyy](https://github.com/zzzsyyy)) ### Improvements diff --git a/Cargo.lock b/Cargo.lock index 71b8cf9..93dce30 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -926,6 +926,26 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "liblzma" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0791ab7e08ccc8e0ce893f6906eb2703ed8739d8e89b57c0714e71bad09024c8" +dependencies = [ + "liblzma-sys", +] + +[[package]] +name = "liblzma-sys" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01b9596486f6d60c3bbe644c0e1be1aa6ccc472ad630fe8927b456973d7cb736" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + [[package]] name = "libredox" version = "0.1.3" @@ -995,17 +1015,6 @@ dependencies = [ "byteorder", ] -[[package]] -name = "lzma-sys" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27" -dependencies = [ - "cc", - "libc", - "pkg-config", -] - [[package]] name = "memchr" version = "2.7.4" @@ -1115,6 +1124,7 @@ dependencies = [ "is_executable", "itertools", "libc", + "liblzma", "linked-hash-map", "lz4_flex", "memchr", @@ -1134,7 +1144,6 @@ dependencies = [ "test-strategy", "time", "unrar", - "xz2", "zip", "zstd", ] @@ -2078,15 +2087,6 @@ dependencies = [ "rustix", ] -[[package]] -name = "xz2" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2" -dependencies = [ - "lzma-sys", -] - [[package]] name = "yansi" version = "1.0.1" diff --git a/Cargo.toml b/Cargo.toml index 5d31493..1fe15af 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,7 +41,7 @@ tar = "0.4.42" tempfile = "3.10.1" time = { version = "0.3.36", default-features = false } unrar = { version = "0.5.7", optional = true } -xz2 = "0.1.7" +liblzma = "0.4" zip = { version = "0.6.6", default-features = false, features = [ "time", "aes-crypto", diff --git a/README.md b/README.md index ae34414..50664e2 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` | `.br` | -|:---------:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:| -| Supported | ✓ | ✓¹ | ✓¹ | ✓² | ✓ | ✓ | ✓ | ✓ | ✓² | ✓² | ✓³ | ✓ | +| Format | `.tar` | `.zip` | `7z` | `.gz` | `.xz` | `.lzma` | `.lz` | `.bz`, `.bz2` | `.bz3` | `.lz4` | `.sz` (Snappy) | `.zst` | `.rar` | `.br` | +|:---------:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:| +| Supported | ✓ | ✓¹ | ✓¹ | ✓² | ✓ | ✓⁴ | ✓⁴ | ✓ | ✓ | ✓ | ✓² | ✓² | ✓³ | ✓ | ✓: Supports compression and decompression. @@ -122,10 +122,13 @@ Output: ✓²: Supported, and compression runs in parallel. ✓³: Due to RAR's restrictive license, only decompression and listing can be supported. + +✓⁴: Only decompression is supported, compression is not implemented yet. + If you wish to exclude non-free code from your build, you can disable RAR support by building without the `unrar` feature. -`tar` aliases are also supported: `tgz`, `tbz`, `tbz2`, `tlz4`, `txz`, `tlzma`, `tsz`, `tzst`. +`tar` aliases are also supported: `tgz`, `tbz`, `tbz2`, `tlz4`, `txz`, `tlzma`, `tsz`, `tzst`, `tlz`. Formats can be chained: diff --git a/src/cli/args.rs b/src/cli/args.rs index b28156c..b756df5 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, rar and br. +/// Supported formats: tar, zip, gz, 7z, xz, lzma, lzip, 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 15880bd..0e6233b 100644 --- a/src/commands/compress.rs +++ b/src/commands/compress.rs @@ -68,10 +68,20 @@ pub fn compress_files( ) } Lz4 => Box::new(lz4_flex::frame::FrameEncoder::new(encoder).auto_finish()), - Lzma => Box::new(xz2::write::XzEncoder::new( + Lzma => { + return Err(crate::Error::UnsupportedFormat { + reason: "LZMA1 compression is not supported in ouch, use .xz instead.".to_string(), + }) + } + Xz => Box::new(liblzma::write::XzEncoder::new( encoder, level.map_or(6, |l| (l as u32).clamp(0, 9)), )), + Lzip => { + return Err(crate::Error::UnsupportedFormat { + reason: "Lzip compression is not supported in ouch.".to_string(), + }) + } Snappy => Box::new( gzp::par::compress::ParCompress::::builder() .compression_level(gzp::par::compress::Compression::new( @@ -108,7 +118,7 @@ pub fn compress_files( } match first_format { - Gzip | Bzip | Bzip3 | Lz4 | Lzma | Snappy | Zstd | Brotli => { + Gzip | Bzip | Bzip3 | Lz4 | Lzma | Xz | Lzip | 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 780c392..f5dc712 100644 --- a/src/commands/decompress.rs +++ b/src/commands/decompress.rs @@ -128,7 +128,15 @@ pub fn decompress_file(options: DecompressOptions) -> crate::Result<()> { Box::new(bzip3::read::Bz3Decoder::new(decoder)?) } Lz4 => Box::new(lz4_flex::frame::FrameDecoder::new(decoder)), - Lzma => Box::new(xz2::read::XzDecoder::new(decoder)), + Lzma => Box::new(liblzma::read::XzDecoder::new_stream( + decoder, + liblzma::stream::Stream::new_lzma_decoder(u64::MAX).unwrap(), + )), + Xz => Box::new(liblzma::read::XzDecoder::new(decoder)), + Lzip => Box::new(liblzma::read::XzDecoder::new_stream( + decoder, + liblzma::stream::Stream::new_lzip_decoder(u64::MAX, 0).unwrap(), + )), 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)), @@ -144,7 +152,7 @@ pub fn decompress_file(options: DecompressOptions) -> crate::Result<()> { } let files_unpacked = match first_extension { - Gzip | Bzip | Bzip3 | Lz4 | Lzma | Snappy | Zstd | Brotli => { + Gzip | Bzip | Bzip3 | Lz4 | Lzma | Xz | Lzip | Snappy | Zstd | Brotli => { reader = chain_reader_decoder(&first_extension, reader)?; let mut writer = match utils::ask_to_create_file( diff --git a/src/commands/list.rs b/src/commands/list.rs index 4a344a7..a2e7915 100644 --- a/src/commands/list.rs +++ b/src/commands/list.rs @@ -57,7 +57,15 @@ pub fn list_archive_contents( Box::new(bzip3::read::Bz3Decoder::new(decoder).unwrap()) } Lz4 => Box::new(lz4_flex::frame::FrameDecoder::new(decoder)), - Lzma => Box::new(xz2::read::XzDecoder::new(decoder)), + Lzma => Box::new(liblzma::read::XzDecoder::new_stream( + decoder, + liblzma::stream::Stream::new_lzma_decoder(u64::MAX).unwrap(), + )), + Xz => Box::new(liblzma::read::XzDecoder::new(decoder)), + Lzip => Box::new(liblzma::read::XzDecoder::new_stream( + decoder, + liblzma::stream::Stream::new_lzip_decoder(u64::MAX, 0).unwrap(), + )), 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)), @@ -127,7 +135,7 @@ pub fn list_archive_contents( Box::new(archive::sevenz::list_archive(io::Cursor::new(vec), password)?) } - Gzip | Bzip | Bzip3 | Lz4 | Lzma | Snappy | Zstd | Brotli => { + Gzip | Bzip | Bzip3 | Lz4 | Lzma | Xz | Lzip | Snappy | Zstd | Brotli => { unreachable!("Not an archive, should be validated before calling this function."); } }; diff --git a/src/error.rs b/src/error.rs index 167bc7b..6e607ab 100644 --- a/src/error.rs +++ b/src/error.rs @@ -135,8 +135,8 @@ impl FinalError { /// /// This is what it looks like: /// ``` - /// hint: Supported extensions are: tar, zip, bz, bz2, gz, lz4, xz, lzma, sz, zst - /// hint: Supported aliases are: tgz, tbz, tlz4, txz, tzlma, tsz, tzst + /// hint: Supported extensions are: tar, zip, bz, bz2, gz, lz4, xz, lzma, lz, sz, zst + /// hint: Supported aliases are: tgz, tbz, tlz4, txz, tlzma, tsz, tzst, tlz /// ``` pub fn hint_all_supported_formats(self) -> Self { self.hint(format!("Supported extensions are: {PRETTY_SUPPORTED_EXTENSIONS}")) diff --git a/src/extension.rs b/src/extension.rs index e886634..c6935f4 100644 --- a/src/extension.rs +++ b/src/extension.rs @@ -19,6 +19,7 @@ pub const SUPPORTED_EXTENSIONS: &[&str] = &[ "lz4", "xz", "lzma", + "lz", "sz", "zst", #[cfg(feature = "unrar")] @@ -27,14 +28,14 @@ pub const SUPPORTED_EXTENSIONS: &[&str] = &[ "br", ]; -pub const SUPPORTED_ALIASES: &[&str] = &["tgz", "tbz", "tlz4", "txz", "tzlma", "tsz", "tzst"]; +pub const SUPPORTED_ALIASES: &[&str] = &["tgz", "tbz", "tlz4", "txz", "tlzma", "tsz", "tzst", "tlz"]; #[cfg(not(feature = "unrar"))] -pub const PRETTY_SUPPORTED_EXTENSIONS: &str = "tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, sz, zst, 7z"; +pub const PRETTY_SUPPORTED_EXTENSIONS: &str = "tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, lz, sz, zst, 7z"; #[cfg(feature = "unrar")] -pub const PRETTY_SUPPORTED_EXTENSIONS: &str = "tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, sz, zst, rar, 7z"; +pub const PRETTY_SUPPORTED_EXTENSIONS: &str = "tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, lz, sz, zst, rar, 7z"; -pub const PRETTY_SUPPORTED_ALIASES: &str = "tgz, tbz, tlz4, txz, tzlma, tsz, tzst"; +pub const PRETTY_SUPPORTED_ALIASES: &str = "tgz, tbz, tlz4, txz, tlzma, tsz, tzst, tlz"; /// A wrapper around `CompressionFormat` that allows combinations like `tgz` #[derive(Debug, Clone)] @@ -85,8 +86,12 @@ pub enum CompressionFormat { Bzip3, /// .lz4 Lz4, - /// .xz .lzma + /// .xz + Xz, + /// .lzma Lzma, + /// .lzip + Lzip, /// .sz Snappy, /// tar, tgz, tbz, tbz2, tbz3, txz, tlz4, tlzma, tsz, tzst @@ -95,7 +100,6 @@ pub enum CompressionFormat { Zstd, /// .zip Zip, - // even if built without RAR support, we still want to recognise the format /// .rar Rar, /// .7z @@ -105,19 +109,11 @@ pub enum CompressionFormat { } impl CompressionFormat { - /// Currently supported archive formats are .tar (and aliases to it) and .zip pub fn archive_format(&self) -> bool { - // Keep this match like that without a wildcard `_` so we don't forget to update it + // Keep this match without a wildcard `_` so we never forget to update it match self { Tar | Zip | Rar | SevenZip => true, - Gzip => false, - Bzip => false, - Bzip3 => false, - Lz4 => false, - Lzma => false, - Snappy => false, - Zstd => false, - Brotli => false, + Bzip | Bzip3 | Lz4 | Lzma | Xz | Lzip | Snappy | Zstd | Brotli | Gzip => false, } } } @@ -130,7 +126,9 @@ fn to_extension(ext: &[u8]) -> Option { b"tbz" | b"tbz2" => &[Tar, Bzip], b"tbz3" => &[Tar, Bzip3], b"tlz4" => &[Tar, Lz4], - b"txz" | b"tlzma" => &[Tar, Lzma], + b"txz" => &[Tar, Xz], + b"tlzma" => &[Tar, Lzma], + b"tlz" => &[Tar, Lzip], b"tsz" => &[Tar, Snappy], b"tzst" => &[Tar, Zstd], b"zip" => &[Zip], @@ -138,7 +136,9 @@ fn to_extension(ext: &[u8]) -> Option { b"bz3" => &[Bzip3], b"gz" => &[Gzip], b"lz4" => &[Lz4], - b"xz" | b"lzma" => &[Lzma], + b"xz" => &[Xz], + b"lzma" => &[Lzma], + b"lz" => &[Lzip], b"sz" => &[Snappy], b"zst" => &[Zstd], b"rar" => &[Rar], diff --git a/src/utils/fs.rs b/src/utils/fs.rs index 21c2457..0f55252 100644 --- a/src/utils/fs.rs +++ b/src/utils/fs.rs @@ -135,9 +135,15 @@ pub fn try_infer_extension(path: &Path) -> Option { fn is_bz3(buf: &[u8]) -> bool { buf.starts_with(b"BZ3v1") } + fn is_lzma(buf: &[u8]) -> bool { + buf.len() >= 14 && buf[0] == 0x5d && (buf[12] == 0x00 || buf[12] == 0xff) && buf[13] == 0x00 + } fn is_xz(buf: &[u8]) -> bool { buf.starts_with(&[0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00]) } + fn is_lzip(buf: &[u8]) -> bool { + buf.starts_with(&[0x4C, 0x5A, 0x49, 0x50]) + } fn is_lz4(buf: &[u8]) -> bool { buf.starts_with(&[0x04, 0x22, 0x4D, 0x18]) } @@ -183,8 +189,12 @@ pub fn try_infer_extension(path: &Path) -> Option { Some(Extension::new(&[Bzip], "bz2")) } else if is_bz3(&buf) { Some(Extension::new(&[Bzip3], "bz3")) + } else if is_lzma(&buf) { + Some(Extension::new(&[Lzma], "lzma")) } else if is_xz(&buf) { - Some(Extension::new(&[Lzma], "xz")) + Some(Extension::new(&[Xz], "xz")) + } else if is_lzip(&buf) { + Some(Extension::new(&[Lzip], "lzip")) } else if is_lz4(&buf) { Some(Extension::new(&[Lz4], "lz4")) } else if is_sz(&buf) { diff --git a/tests/integration.rs b/tests/integration.rs index 1883b0e..7df3995 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -33,7 +33,6 @@ enum DirectoryExtension { Tbz3, Tgz, Tlz4, - Tlzma, Tsz, Txz, Tzst, @@ -50,7 +49,6 @@ enum FileExtension { Bz3, Gz, Lz4, - Lzma, Sz, Xz, Zst, diff --git a/tests/mime.rs b/tests/mime.rs index bf52179..a09b131 100644 --- a/tests/mime.rs +++ b/tests/mime.rs @@ -17,8 +17,7 @@ fn sanity_check_through_mime() { write_random_content(test_file, &mut SmallRng::from_entropy()); let formats = [ - "7z", "tar", "zip", "tar.gz", "tgz", "tbz", "tbz2", "txz", "tlzma", "tzst", "tar.bz", "tar.bz2", "tar.lzma", - "tar.xz", "tar.zst", + "7z", "tar", "zip", "tar.gz", "tgz", "tbz", "tbz2", "txz", "tzst", "tar.bz", "tar.bz2", "tar.xz", "tar.zst", ]; let expected_mimes = [ @@ -30,12 +29,10 @@ fn sanity_check_through_mime() { "application/x-bzip2", "application/x-bzip2", "application/x-xz", - "application/x-xz", "application/zstd", "application/x-bzip2", "application/x-bzip2", "application/x-xz", - "application/x-xz", "application/zstd", ]; diff --git a/tests/snapshots/ui__ui_test_err_decompress_missing_extension_with_rar-1.snap b/tests/snapshots/ui__ui_test_err_decompress_missing_extension_with_rar-1.snap index 61ffed6..37b1add 100644 --- a/tests/snapshots/ui__ui_test_err_decompress_missing_extension_with_rar-1.snap +++ b/tests/snapshots/ui__ui_test_err_decompress_missing_extension_with_rar-1.snap @@ -6,8 +6,8 @@ expression: "run_ouch(\"ouch decompress a\", dir)" - Files with missing extensions: /a - Decompression formats are detected automatically from file extension -hint: Supported extensions are: tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, sz, zst, rar, 7z -hint: Supported aliases are: tgz, tbz, tlz4, txz, tzlma, tsz, tzst +hint: Supported extensions are: tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, lz, sz, zst, rar, 7z +hint: Supported aliases are: tgz, tbz, tlz4, txz, tlzma, tsz, tzst, tlz hint: hint: Alternatively, you can pass an extension to the '--format' flag: hint: ouch decompress /a --format tar.gz diff --git a/tests/snapshots/ui__ui_test_err_decompress_missing_extension_with_rar-2.snap b/tests/snapshots/ui__ui_test_err_decompress_missing_extension_with_rar-2.snap index fe1bbf9..20516bf 100644 --- a/tests/snapshots/ui__ui_test_err_decompress_missing_extension_with_rar-2.snap +++ b/tests/snapshots/ui__ui_test_err_decompress_missing_extension_with_rar-2.snap @@ -7,5 +7,5 @@ expression: "run_ouch(\"ouch decompress a b.unknown\", dir)" - Files with missing extensions: /a - Decompression formats are detected automatically from file extension -hint: Supported extensions are: tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, sz, zst, rar, 7z -hint: Supported aliases are: tgz, tbz, tlz4, txz, tzlma, tsz, tzst +hint: Supported extensions are: tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, lz, sz, zst, rar, 7z +hint: Supported aliases are: tgz, tbz, tlz4, txz, tlzma, tsz, tzst, tlz diff --git a/tests/snapshots/ui__ui_test_err_decompress_missing_extension_with_rar-3.snap b/tests/snapshots/ui__ui_test_err_decompress_missing_extension_with_rar-3.snap index 0ef66a2..50cbb0d 100644 --- a/tests/snapshots/ui__ui_test_err_decompress_missing_extension_with_rar-3.snap +++ b/tests/snapshots/ui__ui_test_err_decompress_missing_extension_with_rar-3.snap @@ -6,8 +6,8 @@ expression: "run_ouch(\"ouch decompress b.unknown\", dir)" - Files with unsupported extensions: /b.unknown - Decompression formats are detected automatically from file extension -hint: Supported extensions are: tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, sz, zst, rar, 7z -hint: Supported aliases are: tgz, tbz, tlz4, txz, tzlma, tsz, tzst +hint: Supported extensions are: tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, lz, sz, zst, rar, 7z +hint: Supported aliases are: tgz, tbz, tlz4, txz, tlzma, tsz, tzst, tlz hint: hint: Alternatively, you can pass an extension to the '--format' flag: hint: ouch decompress /b.unknown --format tar.gz diff --git a/tests/snapshots/ui__ui_test_err_decompress_missing_extension_without_rar-1.snap b/tests/snapshots/ui__ui_test_err_decompress_missing_extension_without_rar-1.snap index 2885d83..c9f0719 100644 --- a/tests/snapshots/ui__ui_test_err_decompress_missing_extension_without_rar-1.snap +++ b/tests/snapshots/ui__ui_test_err_decompress_missing_extension_without_rar-1.snap @@ -6,8 +6,8 @@ expression: "run_ouch(\"ouch decompress a\", dir)" - Files with missing extensions: /a - Decompression formats are detected automatically from file extension -hint: Supported extensions are: tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, sz, zst, 7z -hint: Supported aliases are: tgz, tbz, tlz4, txz, tzlma, tsz, tzst +hint: Supported extensions are: tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, lz, sz, zst, 7z +hint: Supported aliases are: tgz, tbz, tlz4, txz, tlzma, tsz, tzst, tlz hint: hint: Alternatively, you can pass an extension to the '--format' flag: hint: ouch decompress /a --format tar.gz diff --git a/tests/snapshots/ui__ui_test_err_decompress_missing_extension_without_rar-2.snap b/tests/snapshots/ui__ui_test_err_decompress_missing_extension_without_rar-2.snap index 9cdcbd6..cc7dcaf 100644 --- a/tests/snapshots/ui__ui_test_err_decompress_missing_extension_without_rar-2.snap +++ b/tests/snapshots/ui__ui_test_err_decompress_missing_extension_without_rar-2.snap @@ -7,5 +7,5 @@ expression: "run_ouch(\"ouch decompress a b.unknown\", dir)" - Files with missing extensions: /a - Decompression formats are detected automatically from file extension -hint: Supported extensions are: tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, sz, zst, 7z -hint: Supported aliases are: tgz, tbz, tlz4, txz, tzlma, tsz, tzst +hint: Supported extensions are: tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, lz, sz, zst, 7z +hint: Supported aliases are: tgz, tbz, tlz4, txz, tlzma, tsz, tzst, tlz diff --git a/tests/snapshots/ui__ui_test_err_decompress_missing_extension_without_rar-3.snap b/tests/snapshots/ui__ui_test_err_decompress_missing_extension_without_rar-3.snap index 5e57d8b..ebf00d5 100644 --- a/tests/snapshots/ui__ui_test_err_decompress_missing_extension_without_rar-3.snap +++ b/tests/snapshots/ui__ui_test_err_decompress_missing_extension_without_rar-3.snap @@ -6,8 +6,8 @@ expression: "run_ouch(\"ouch decompress b.unknown\", dir)" - Files with unsupported extensions: /b.unknown - Decompression formats are detected automatically from file extension -hint: Supported extensions are: tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, sz, zst, 7z -hint: Supported aliases are: tgz, tbz, tlz4, txz, tzlma, tsz, tzst +hint: Supported extensions are: tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, lz, sz, zst, 7z +hint: Supported aliases are: tgz, tbz, tlz4, txz, tlzma, tsz, tzst, tlz hint: hint: Alternatively, you can pass an extension to the '--format' flag: hint: ouch decompress /b.unknown --format tar.gz diff --git a/tests/snapshots/ui__ui_test_err_format_flag_with_rar-1.snap b/tests/snapshots/ui__ui_test_err_format_flag_with_rar-1.snap index 5cb36e4..43277da 100644 --- a/tests/snapshots/ui__ui_test_err_format_flag_with_rar-1.snap +++ b/tests/snapshots/ui__ui_test_err_format_flag_with_rar-1.snap @@ -5,8 +5,8 @@ expression: "run_ouch(\"ouch compress input output --format tar.gz.unknown\", di [ERROR] Failed to parse `--format tar.gz.unknown` - Unsupported extension 'unknown' -hint: Supported extensions are: tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, sz, zst, rar, 7z -hint: Supported aliases are: tgz, tbz, tlz4, txz, tzlma, tsz, tzst +hint: Supported extensions are: tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, lz, sz, zst, rar, 7z +hint: Supported aliases are: tgz, tbz, tlz4, txz, tlzma, tsz, tzst, tlz hint: hint: Examples: hint: --format tar diff --git a/tests/snapshots/ui__ui_test_err_format_flag_with_rar-2.snap b/tests/snapshots/ui__ui_test_err_format_flag_with_rar-2.snap index a1196c7..43b9c08 100644 --- a/tests/snapshots/ui__ui_test_err_format_flag_with_rar-2.snap +++ b/tests/snapshots/ui__ui_test_err_format_flag_with_rar-2.snap @@ -5,8 +5,8 @@ expression: "run_ouch(\"ouch compress input output --format targz\", dir)" [ERROR] Failed to parse `--format targz` - Unsupported extension 'targz' -hint: Supported extensions are: tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, sz, zst, rar, 7z -hint: Supported aliases are: tgz, tbz, tlz4, txz, tzlma, tsz, tzst +hint: Supported extensions are: tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, lz, sz, zst, rar, 7z +hint: Supported aliases are: tgz, tbz, tlz4, txz, tlzma, tsz, tzst, tlz hint: hint: Examples: hint: --format tar diff --git a/tests/snapshots/ui__ui_test_err_format_flag_with_rar-3.snap b/tests/snapshots/ui__ui_test_err_format_flag_with_rar-3.snap index 4269a23..861fdf9 100644 --- a/tests/snapshots/ui__ui_test_err_format_flag_with_rar-3.snap +++ b/tests/snapshots/ui__ui_test_err_format_flag_with_rar-3.snap @@ -5,8 +5,8 @@ expression: "run_ouch(\"ouch compress input output --format .tar.$#!@.rest\", di [ERROR] Failed to parse `--format .tar.$#!@.rest` - Unsupported extension '$#!@' -hint: Supported extensions are: tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, sz, zst, rar, 7z -hint: Supported aliases are: tgz, tbz, tlz4, txz, tzlma, tsz, tzst +hint: Supported extensions are: tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, lz, sz, zst, rar, 7z +hint: Supported aliases are: tgz, tbz, tlz4, txz, tlzma, tsz, tzst, tlz hint: hint: Examples: hint: --format tar diff --git a/tests/snapshots/ui__ui_test_err_format_flag_without_rar-1.snap b/tests/snapshots/ui__ui_test_err_format_flag_without_rar-1.snap index cd7ccbc..b3a3b1a 100644 --- a/tests/snapshots/ui__ui_test_err_format_flag_without_rar-1.snap +++ b/tests/snapshots/ui__ui_test_err_format_flag_without_rar-1.snap @@ -5,8 +5,8 @@ expression: "run_ouch(\"ouch compress input output --format tar.gz.unknown\", di [ERROR] Failed to parse `--format tar.gz.unknown` - Unsupported extension 'unknown' -hint: Supported extensions are: tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, sz, zst, 7z -hint: Supported aliases are: tgz, tbz, tlz4, txz, tzlma, tsz, tzst +hint: Supported extensions are: tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, lz, sz, zst, 7z +hint: Supported aliases are: tgz, tbz, tlz4, txz, tlzma, tsz, tzst, tlz hint: hint: Examples: hint: --format tar diff --git a/tests/snapshots/ui__ui_test_err_format_flag_without_rar-2.snap b/tests/snapshots/ui__ui_test_err_format_flag_without_rar-2.snap index 0913264..4c1a7c0 100644 --- a/tests/snapshots/ui__ui_test_err_format_flag_without_rar-2.snap +++ b/tests/snapshots/ui__ui_test_err_format_flag_without_rar-2.snap @@ -5,8 +5,8 @@ expression: "run_ouch(\"ouch compress input output --format targz\", dir)" [ERROR] Failed to parse `--format targz` - Unsupported extension 'targz' -hint: Supported extensions are: tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, sz, zst, 7z -hint: Supported aliases are: tgz, tbz, tlz4, txz, tzlma, tsz, tzst +hint: Supported extensions are: tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, lz, sz, zst, 7z +hint: Supported aliases are: tgz, tbz, tlz4, txz, tlzma, tsz, tzst, tlz hint: hint: Examples: hint: --format tar diff --git a/tests/snapshots/ui__ui_test_err_format_flag_without_rar-3.snap b/tests/snapshots/ui__ui_test_err_format_flag_without_rar-3.snap index 7d9b12c..7cbbe2f 100644 --- a/tests/snapshots/ui__ui_test_err_format_flag_without_rar-3.snap +++ b/tests/snapshots/ui__ui_test_err_format_flag_without_rar-3.snap @@ -5,8 +5,8 @@ expression: "run_ouch(\"ouch compress input output --format .tar.$#!@.rest\", di [ERROR] Failed to parse `--format .tar.$#!@.rest` - Unsupported extension '$#!@' -hint: Supported extensions are: tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, sz, zst, 7z -hint: Supported aliases are: tgz, tbz, tlz4, txz, tzlma, tsz, tzst +hint: Supported extensions are: tar, zip, bz, bz2, bz3, gz, lz4, xz, lzma, lz, sz, zst, 7z +hint: Supported aliases are: tgz, tbz, tlz4, txz, tlzma, tsz, tzst, tlz hint: hint: Examples: hint: --format tar diff --git a/tests/snapshots/ui__ui_test_usage_help_flag.snap b/tests/snapshots/ui__ui_test_usage_help_flag.snap index 6913b83..9f3f8db 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, rar and br. +Supported formats: tar, zip, gz, 7z, xz, lzma, lzip, bz/bz2, bz3, lz4, sz (Snappy), zst, rar and br. Repository: https://github.com/ouch-org/ouch From 7a6c4ecfb1262cf25c2ddcb7a23a05a7f97a65c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Marcos?= Date: Tue, 15 Jul 2025 16:58:05 -0300 Subject: [PATCH 4/5] Update CONTRIBUTING.md (#849) --- CONTRIBUTING.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 83ac503..8b1d853 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -39,11 +39,11 @@ We use snapshots to do UI testing and guarantee a consistent output, this way, y ```sh cargo test -# Only run UI tests -cargo test -- ui +# Or, if you only want to run UI tests +# cargo test -- ui ``` -- If some UI test failed, you should review it: +- If some UI test failed, you should review them (requires `cargo install cargo-insta`): ```sh cargo insta review From bbce74666682aa26f62fe0cc980b196257f846fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Marcos?= Date: Tue, 15 Jul 2025 17:01:59 -0300 Subject: [PATCH 5/5] Add aliases for password flag (#847) --- CHANGELOG.md | 1 + src/cli/args.rs | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3062d1f..f4b20ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ Categories Used: ### Improvements - Give better error messages when archive extensions are invalid [\#817](https://github.com/ouch-org/ouch/pull/817) ([marcospb19](https://github.com/marcospb19)) +- Add aliases for `--password` flag (`--pass` and `--pw`) [\#847](https://github.com/ouch-org/ouch/pull/847) ([marcospb19](https://github.com/marcospb19)) ### Bug Fixes diff --git a/src/cli/args.rs b/src/cli/args.rs index b756df5..105f004 100644 --- a/src/cli/args.rs +++ b/src/cli/args.rs @@ -30,11 +30,11 @@ pub struct CliArgs { pub hidden: bool, /// Silence output - #[arg(short = 'q', long, global = true)] + #[arg(short, long, global = true)] pub quiet: bool, /// Ignore files matched by git's ignore files - #[arg(short = 'g', long, global = true)] + #[arg(short, long, global = true)] pub gitignore: bool, /// Specify the format of the archive @@ -42,7 +42,7 @@ pub struct CliArgs { pub format: Option, /// Decompress or list with password - #[arg(short = 'p', long = "password", global = true)] + #[arg(short, long = "password", aliases = ["pass", "pw"], global = true)] pub password: Option, /// Concurrent working threads