diff --git a/Cargo.toml b/Cargo.toml index 6e61868..78ace43 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,7 @@ snap = "1.1.1" tar = "0.4.40" tempfile = "3.8.1" time = { version = "0.3.30", default-features = false } -unrar = "0.5.2" +unrar = { version = "0.5.2", optional = true } xz2 = "0.1.7" zip = { version = "0.6.6", default-features = false, features = ["time"] } zstd = { version = "0.13.0", default-features = false } diff --git a/src/archive/mod.rs b/src/archive/mod.rs index 077d16b..4cfacc0 100644 --- a/src/archive/mod.rs +++ b/src/archive/mod.rs @@ -1,6 +1,9 @@ //! Archive compression algorithms +#[cfg(feature = "unrar")] pub mod rar; +#[cfg(not(feature = "unrar"))] +pub mod rar_stub; pub mod sevenz; pub mod tar; pub mod zip; diff --git a/src/archive/rar.rs b/src/archive/rar.rs index c08ec58..225ec1f 100644 --- a/src/archive/rar.rs +++ b/src/archive/rar.rs @@ -4,7 +4,7 @@ use std::path::Path; use unrar::{self, Archive}; -use crate::{info, list::FileInArchive, warning}; +use crate::{error::Error, info, list::FileInArchive}; /// Unpacks the archive given by `archive_path` into the folder given by `output_folder`. /// Assumes that output_folder is empty @@ -49,8 +49,8 @@ pub fn list_archive(archive_path: &Path) -> impl Iterator Error { + Error::UnsupportedFormat { + reason: "Creating RAR archives is not allowed due to licensing restrictions.".into(), + } } diff --git a/src/archive/rar_stub.rs b/src/archive/rar_stub.rs new file mode 100644 index 0000000..d6b22e4 --- /dev/null +++ b/src/archive/rar_stub.rs @@ -0,0 +1,7 @@ +use crate::Error; + +pub fn no_support() -> Error { + Error::UnsupportedFormat { + reason: "RAR support is disabled for this build, possibly due to licensing restrictions.".into(), + } +} diff --git a/src/commands/compress.rs b/src/commands/compress.rs index 5150fff..2f24cff 100644 --- a/src/commands/compress.rs +++ b/src/commands/compress.rs @@ -124,8 +124,11 @@ pub fn compress_files( io::copy(&mut vec_buffer, &mut writer)?; } Rar => { - archive::rar::no_compression_notice(); - return Ok(false); + #[cfg(feature = "unrar")] + return Err(archive::rar::no_compression()); + + #[cfg(not(feature = "unrar"))] + return Err(archive::rar_stub::no_support()); } SevenZip => { if !formats.is_empty() { diff --git a/src/commands/decompress.rs b/src/commands/decompress.rs index c717dbc..b36fc5b 100644 --- a/src/commands/decompress.rs +++ b/src/commands/decompress.rs @@ -146,6 +146,7 @@ pub fn decompress_file( return Ok(()); } } + #[cfg(feature = "unrar")] Rar => { type UnpackResult = crate::Result; let unpack_fn: Box UnpackResult> = if formats.len() > 1 { @@ -164,6 +165,10 @@ pub fn decompress_file( return Ok(()); } } + #[cfg(not(feature = "unrar"))] + Rar => { + return Err(crate::archive::rar_stub::no_support()); + } SevenZip => { if formats.len() > 1 { warn_user_about_loading_sevenz_in_memory(); diff --git a/src/commands/list.rs b/src/commands/list.rs index f89830f..448b51c 100644 --- a/src/commands/list.rs +++ b/src/commands/list.rs @@ -78,6 +78,7 @@ pub fn list_archive_contents( Box::new(crate::archive::zip::list_archive(zip_archive)) } + #[cfg(feature = "unrar")] Rar => { if formats.len() > 1 { let mut temp_file = tempfile::NamedTempFile::new()?; @@ -87,6 +88,10 @@ pub fn list_archive_contents( Box::new(crate::archive::rar::list_archive(archive_path)) } } + #[cfg(not(feature = "unrar"))] + Rar => { + return Err(crate::archive::rar_stub::no_support()); + } SevenZip => { if formats.len() > 1 { warn_user_about_loading_zip_in_memory(); diff --git a/src/error.rs b/src/error.rs index 1b48623..a54d0a2 100644 --- a/src/error.rs +++ b/src/error.rs @@ -36,6 +36,9 @@ pub enum Error { InvalidFormat { reason: String }, /// From sevenz_rust::Error SevenzipError(sevenz_rust::Error), + /// Recognised but unsupported format + // currently only RAR when built without the `unrar` feature + UnsupportedFormat { reason: String }, } /// Alias to std's Result with ouch's Error @@ -142,6 +145,9 @@ impl fmt::Display for Error { Error::InvalidFormat { reason } => FinalError::with_title("Invalid archive format").detail(reason.clone()), Error::Custom { reason } => reason.clone(), Error::SevenzipError(reason) => FinalError::with_title("7z error").detail(reason.to_string()), + Error::UnsupportedFormat { reason } => { + FinalError::with_title("Recognised but unsupported format").detail(reason.clone()) + } }; write!(f, "{err}") @@ -181,6 +187,7 @@ impl From for Error { } } +#[cfg(feature = "unrar")] impl From for Error { fn from(err: unrar::error::UnrarError) -> Self { Self::Custom { diff --git a/src/extension.rs b/src/extension.rs index b4029a8..4880d09 100644 --- a/src/extension.rs +++ b/src/extension.rs @@ -8,10 +8,28 @@ use self::CompressionFormat::*; use crate::{error::Error, warning}; pub const SUPPORTED_EXTENSIONS: &[&str] = &[ - "tar", "zip", "bz", "bz2", "gz", "lz4", "xz", "lzma", "sz", "zst", "rar", "7z", + "tar", + "zip", + "bz", + "bz2", + "gz", + "lz4", + "xz", + "lzma", + "sz", + "zst", + #[cfg(feature = "unrar")] + "rar", + "7z", ]; + pub const SUPPORTED_ALIASES: &[&str] = &["tgz", "tbz", "tlz4", "txz", "tzlma", "tsz", "tzst"]; -pub const PRETTY_SUPPORTED_EXTENSIONS: &str = "tar, zip, bz, bz2, gz, lz4, xz, lzma, sz, zst, rar"; + +#[cfg(not(feature = "unrar"))] +pub const PRETTY_SUPPORTED_EXTENSIONS: &str = "tar, zip, bz, bz2, gz, lz4, xz, lzma, sz, zst, 7z"; +#[cfg(feature = "unrar")] +pub const PRETTY_SUPPORTED_EXTENSIONS: &str = "tar, zip, bz, bz2, gz, lz4, xz, lzma, sz, zst, rar, 7z"; + pub const PRETTY_SUPPORTED_ALIASES: &str = "tgz, tbz, tlz4, txz, tzlma, tsz, tzst"; /// A wrapper around `CompressionFormat` that allows combinations like `tgz` @@ -74,6 +92,7 @@ pub enum CompressionFormat { Zstd, /// .zip Zip, + // even if built without RAR support, we still want to recognise the format /// .rar Rar, /// .7z diff --git a/tests/integration.rs b/tests/integration.rs index 5b17fc8..cfdbc7f 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -1,10 +1,7 @@ #[macro_use] mod utils; -use std::{ - iter::once, - path::{Path, PathBuf}, -}; +use std::{iter::once, path::PathBuf}; use fs_err as fs; use parse_display::Display; @@ -154,20 +151,20 @@ fn multiple_files( assert_same_directory(before, after, !matches!(ext, DirectoryExtension::Zip)); } -// test .rar decompression -fn test_unpack_rar_single(input: &Path) -> Result<(), Box> { - let dir = tempdir()?; - let dirpath = dir.path(); - let unpacked_path = &dirpath.join("testfile.txt"); - ouch!("-A", "d", input, "-d", dirpath); - let content = fs::read_to_string(unpacked_path)?; - assert_eq!(content, "Testing 123\n"); - - Ok(()) -} - +#[cfg(feature = "unrar")] #[test] fn unpack_rar() -> Result<(), Box> { + fn test_unpack_rar_single(input: &std::path::Path) -> Result<(), Box> { + let dir = tempdir()?; + let dirpath = dir.path(); + let unpacked_path = &dirpath.join("testfile.txt"); + ouch!("-A", "d", input, "-d", dirpath); + let content = fs::read_to_string(unpacked_path)?; + assert_eq!(content, "Testing 123\n"); + + Ok(()) + } + let mut datadir = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR")?); datadir.push("tests/data"); ["testfile.rar3.rar.gz", "testfile.rar5.rar"] 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 new file mode 100644 index 0000000..19b308c --- /dev/null +++ b/tests/snapshots/ui__ui_test_err_decompress_missing_extension_with_rar-1.snap @@ -0,0 +1,14 @@ +--- +source: tests/ui.rs +expression: "run_ouch(\"ouch decompress a\", dir)" +--- +[ERROR] Cannot decompress files + - Files with missing extensions: /a + - Decompression formats are detected automatically from file extension + +hint: Supported extensions are: tar, zip, bz, bz2, gz, lz4, xz, lzma, sz, zst, rar, 7z +hint: Supported aliases are: tgz, tbz, tlz4, txz, tzlma, tsz, tzst +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 new file mode 100644 index 0000000..184634d --- /dev/null +++ b/tests/snapshots/ui__ui_test_err_decompress_missing_extension_with_rar-2.snap @@ -0,0 +1,12 @@ +--- +source: tests/ui.rs +expression: "run_ouch(\"ouch decompress a b.unknown\", dir)" +--- +[ERROR] Cannot decompress files + - Files with unsupported extensions: /b.unknown + - Files with missing extensions: /a + - Decompression formats are detected automatically from file extension + +hint: Supported extensions are: tar, zip, bz, bz2, gz, lz4, xz, lzma, sz, zst, rar, 7z +hint: Supported aliases are: tgz, tbz, tlz4, txz, tzlma, tsz, tzst + 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 new file mode 100644 index 0000000..e9b9961 --- /dev/null +++ b/tests/snapshots/ui__ui_test_err_decompress_missing_extension_with_rar-3.snap @@ -0,0 +1,14 @@ +--- +source: tests/ui.rs +expression: "run_ouch(\"ouch decompress b.unknown\", dir)" +--- +[ERROR] Cannot decompress files + - Files with unsupported extensions: /b.unknown + - Decompression formats are detected automatically from file extension + +hint: Supported extensions are: tar, zip, bz, bz2, gz, lz4, xz, lzma, sz, zst, rar, 7z +hint: Supported aliases are: tgz, tbz, tlz4, txz, tzlma, tsz, tzst +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.snap b/tests/snapshots/ui__ui_test_err_decompress_missing_extension_without_rar-1.snap similarity index 96% rename from tests/snapshots/ui__ui_test_err_decompress_missing_extension.snap rename to tests/snapshots/ui__ui_test_err_decompress_missing_extension_without_rar-1.snap index ac4c03b..23a3a91 100644 --- a/tests/snapshots/ui__ui_test_err_decompress_missing_extension.snap +++ b/tests/snapshots/ui__ui_test_err_decompress_missing_extension_without_rar-1.snap @@ -6,7 +6,7 @@ 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, gz, lz4, xz, lzma, sz, zst, rar +hint: Supported extensions are: tar, zip, bz, bz2, gz, lz4, xz, lzma, sz, zst, 7z hint: Supported aliases are: tgz, tbz, tlz4, txz, tzlma, tsz, tzst hint: hint: Alternatively, you can pass an extension to the '--format' flag: diff --git a/tests/snapshots/ui__ui_test_err_decompress_missing_extension-2.snap b/tests/snapshots/ui__ui_test_err_decompress_missing_extension_without_rar-2.snap similarity index 95% rename from tests/snapshots/ui__ui_test_err_decompress_missing_extension-2.snap rename to tests/snapshots/ui__ui_test_err_decompress_missing_extension_without_rar-2.snap index cc3fa02..36fd604 100644 --- a/tests/snapshots/ui__ui_test_err_decompress_missing_extension-2.snap +++ b/tests/snapshots/ui__ui_test_err_decompress_missing_extension_without_rar-2.snap @@ -7,6 +7,6 @@ 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, gz, lz4, xz, lzma, sz, zst, rar +hint: Supported extensions are: tar, zip, bz, bz2, gz, lz4, xz, lzma, sz, zst, 7z hint: Supported aliases are: tgz, tbz, tlz4, txz, tzlma, tsz, tzst diff --git a/tests/snapshots/ui__ui_test_err_decompress_missing_extension-3.snap b/tests/snapshots/ui__ui_test_err_decompress_missing_extension_without_rar-3.snap similarity index 96% rename from tests/snapshots/ui__ui_test_err_decompress_missing_extension-3.snap rename to tests/snapshots/ui__ui_test_err_decompress_missing_extension_without_rar-3.snap index d2eb579..0a6cd5d 100644 --- a/tests/snapshots/ui__ui_test_err_decompress_missing_extension-3.snap +++ b/tests/snapshots/ui__ui_test_err_decompress_missing_extension_without_rar-3.snap @@ -6,7 +6,7 @@ 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, gz, lz4, xz, lzma, sz, zst, rar +hint: Supported extensions are: tar, zip, bz, bz2, gz, lz4, xz, lzma, sz, zst, 7z hint: Supported aliases are: tgz, tbz, tlz4, txz, tzlma, tsz, tzst hint: hint: Alternatively, you can pass an extension to the '--format' flag: diff --git a/tests/ui.rs b/tests/ui.rs index 8d6cdef..698c86e 100644 --- a/tests/ui.rs +++ b/tests/ui.rs @@ -65,9 +65,17 @@ fn ui_test_err_decompress_missing_extension() { run_in(dir, "touch", "a b.unknown").unwrap(); - ui!(run_ouch("ouch decompress a", dir)); - ui!(run_ouch("ouch decompress a b.unknown", dir)); - ui!(run_ouch("ouch decompress b.unknown", dir)); + let name = { + let suffix = if cfg!(feature = "unrar") { + "with_rar" + } else { + "without_rar" + }; + format!("ui_test_err_decompress_missing_extension_{suffix}") + }; + ui!(format!("{name}-1"), run_ouch("ouch decompress a", dir)); + ui!(format!("{name}-2"), run_ouch("ouch decompress a b.unknown", dir)); + ui!(format!("{name}-3"), run_ouch("ouch decompress b.unknown", dir)); } #[test]