Feature-gate RAR support

This commit is contained in:
cyqsimon 2023-12-04 00:20:39 +08:00 committed by João Marcos
parent 642552f75b
commit fa2d214fee
17 changed files with 126 additions and 32 deletions

View File

@ -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 }

View File

@ -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;

View File

@ -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<Item = crate::Result<F
})
}
pub fn no_compression_notice() {
const MESSAGE: &str = "Creating '.rar' archives is not supported due to licensing restrictions";
warning!("{}", MESSAGE);
pub fn no_compression() -> Error {
Error::UnsupportedFormat {
reason: "Creating RAR archives is not allowed due to licensing restrictions.".into(),
}
}

7
src/archive/rar_stub.rs Normal file
View File

@ -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(),
}
}

View File

@ -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() {

View File

@ -146,6 +146,7 @@ pub fn decompress_file(
return Ok(());
}
}
#[cfg(feature = "unrar")]
Rar => {
type UnpackResult = crate::Result<usize>;
let unpack_fn: Box<dyn FnOnce(&Path) -> 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();

View File

@ -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();

View File

@ -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<zip::result::ZipError> for Error {
}
}
#[cfg(feature = "unrar")]
impl From<unrar::error::UnrarError> for Error {
fn from(err: unrar::error::UnrarError) -> Self {
Self::Custom {

View File

@ -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

View File

@ -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<dyn std::error::Error>> {
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<dyn std::error::Error>> {
fn test_unpack_rar_single(input: &std::path::Path) -> Result<(), Box<dyn std::error::Error>> {
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"]

View File

@ -0,0 +1,14 @@
---
source: tests/ui.rs
expression: "run_ouch(\"ouch decompress a\", dir)"
---
[ERROR] Cannot decompress files
- Files with missing extensions: <TMP_DIR>/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 <TMP_DIR>/a --format tar.gz

View File

@ -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: <TMP_DIR>/b.unknown
- Files with missing extensions: <TMP_DIR>/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

View File

@ -0,0 +1,14 @@
---
source: tests/ui.rs
expression: "run_ouch(\"ouch decompress b.unknown\", dir)"
---
[ERROR] Cannot decompress files
- Files with unsupported extensions: <TMP_DIR>/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 <TMP_DIR>/b.unknown --format tar.gz

View File

@ -6,7 +6,7 @@ expression: "run_ouch(\"ouch decompress a\", dir)"
- Files with missing extensions: <TMP_DIR>/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:

View File

@ -7,6 +7,6 @@ expression: "run_ouch(\"ouch decompress a b.unknown\", dir)"
- Files with missing extensions: <TMP_DIR>/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

View File

@ -6,7 +6,7 @@ expression: "run_ouch(\"ouch decompress b.unknown\", dir)"
- Files with unsupported extensions: <TMP_DIR>/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:

View File

@ -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]