mirror of
https://github.com/ouch-org/ouch.git
synced 2025-06-06 11:35:45 +00:00
Feature-gate RAR support
This commit is contained in:
parent
642552f75b
commit
fa2d214fee
@ -31,7 +31,7 @@ snap = "1.1.1"
|
|||||||
tar = "0.4.40"
|
tar = "0.4.40"
|
||||||
tempfile = "3.8.1"
|
tempfile = "3.8.1"
|
||||||
time = { version = "0.3.30", default-features = false }
|
time = { version = "0.3.30", default-features = false }
|
||||||
unrar = "0.5.2"
|
unrar = { version = "0.5.2", optional = true }
|
||||||
xz2 = "0.1.7"
|
xz2 = "0.1.7"
|
||||||
zip = { version = "0.6.6", default-features = false, features = ["time"] }
|
zip = { version = "0.6.6", default-features = false, features = ["time"] }
|
||||||
zstd = { version = "0.13.0", default-features = false }
|
zstd = { version = "0.13.0", default-features = false }
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
//! Archive compression algorithms
|
//! Archive compression algorithms
|
||||||
|
|
||||||
|
#[cfg(feature = "unrar")]
|
||||||
pub mod rar;
|
pub mod rar;
|
||||||
|
#[cfg(not(feature = "unrar"))]
|
||||||
|
pub mod rar_stub;
|
||||||
pub mod sevenz;
|
pub mod sevenz;
|
||||||
pub mod tar;
|
pub mod tar;
|
||||||
pub mod zip;
|
pub mod zip;
|
||||||
|
@ -4,7 +4,7 @@ use std::path::Path;
|
|||||||
|
|
||||||
use unrar::{self, Archive};
|
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`.
|
/// Unpacks the archive given by `archive_path` into the folder given by `output_folder`.
|
||||||
/// Assumes that output_folder is empty
|
/// 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() {
|
pub fn no_compression() -> Error {
|
||||||
const MESSAGE: &str = "Creating '.rar' archives is not supported due to licensing restrictions";
|
Error::UnsupportedFormat {
|
||||||
|
reason: "Creating RAR archives is not allowed due to licensing restrictions.".into(),
|
||||||
warning!("{}", MESSAGE);
|
}
|
||||||
}
|
}
|
||||||
|
7
src/archive/rar_stub.rs
Normal file
7
src/archive/rar_stub.rs
Normal 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(),
|
||||||
|
}
|
||||||
|
}
|
@ -124,8 +124,11 @@ pub fn compress_files(
|
|||||||
io::copy(&mut vec_buffer, &mut writer)?;
|
io::copy(&mut vec_buffer, &mut writer)?;
|
||||||
}
|
}
|
||||||
Rar => {
|
Rar => {
|
||||||
archive::rar::no_compression_notice();
|
#[cfg(feature = "unrar")]
|
||||||
return Ok(false);
|
return Err(archive::rar::no_compression());
|
||||||
|
|
||||||
|
#[cfg(not(feature = "unrar"))]
|
||||||
|
return Err(archive::rar_stub::no_support());
|
||||||
}
|
}
|
||||||
SevenZip => {
|
SevenZip => {
|
||||||
if !formats.is_empty() {
|
if !formats.is_empty() {
|
||||||
|
@ -146,6 +146,7 @@ pub fn decompress_file(
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[cfg(feature = "unrar")]
|
||||||
Rar => {
|
Rar => {
|
||||||
type UnpackResult = crate::Result<usize>;
|
type UnpackResult = crate::Result<usize>;
|
||||||
let unpack_fn: Box<dyn FnOnce(&Path) -> UnpackResult> = if formats.len() > 1 {
|
let unpack_fn: Box<dyn FnOnce(&Path) -> UnpackResult> = if formats.len() > 1 {
|
||||||
@ -164,6 +165,10 @@ pub fn decompress_file(
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[cfg(not(feature = "unrar"))]
|
||||||
|
Rar => {
|
||||||
|
return Err(crate::archive::rar_stub::no_support());
|
||||||
|
}
|
||||||
SevenZip => {
|
SevenZip => {
|
||||||
if formats.len() > 1 {
|
if formats.len() > 1 {
|
||||||
warn_user_about_loading_sevenz_in_memory();
|
warn_user_about_loading_sevenz_in_memory();
|
||||||
|
@ -78,6 +78,7 @@ pub fn list_archive_contents(
|
|||||||
|
|
||||||
Box::new(crate::archive::zip::list_archive(zip_archive))
|
Box::new(crate::archive::zip::list_archive(zip_archive))
|
||||||
}
|
}
|
||||||
|
#[cfg(feature = "unrar")]
|
||||||
Rar => {
|
Rar => {
|
||||||
if formats.len() > 1 {
|
if formats.len() > 1 {
|
||||||
let mut temp_file = tempfile::NamedTempFile::new()?;
|
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))
|
Box::new(crate::archive::rar::list_archive(archive_path))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[cfg(not(feature = "unrar"))]
|
||||||
|
Rar => {
|
||||||
|
return Err(crate::archive::rar_stub::no_support());
|
||||||
|
}
|
||||||
SevenZip => {
|
SevenZip => {
|
||||||
if formats.len() > 1 {
|
if formats.len() > 1 {
|
||||||
warn_user_about_loading_zip_in_memory();
|
warn_user_about_loading_zip_in_memory();
|
||||||
|
@ -36,6 +36,9 @@ pub enum Error {
|
|||||||
InvalidFormat { reason: String },
|
InvalidFormat { reason: String },
|
||||||
/// From sevenz_rust::Error
|
/// From sevenz_rust::Error
|
||||||
SevenzipError(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
|
/// 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::InvalidFormat { reason } => FinalError::with_title("Invalid archive format").detail(reason.clone()),
|
||||||
Error::Custom { reason } => reason.clone(),
|
Error::Custom { reason } => reason.clone(),
|
||||||
Error::SevenzipError(reason) => FinalError::with_title("7z error").detail(reason.to_string()),
|
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}")
|
write!(f, "{err}")
|
||||||
@ -181,6 +187,7 @@ impl From<zip::result::ZipError> for Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "unrar")]
|
||||||
impl From<unrar::error::UnrarError> for Error {
|
impl From<unrar::error::UnrarError> for Error {
|
||||||
fn from(err: unrar::error::UnrarError) -> Self {
|
fn from(err: unrar::error::UnrarError) -> Self {
|
||||||
Self::Custom {
|
Self::Custom {
|
||||||
|
@ -8,10 +8,28 @@ use self::CompressionFormat::*;
|
|||||||
use crate::{error::Error, warning};
|
use crate::{error::Error, warning};
|
||||||
|
|
||||||
pub const SUPPORTED_EXTENSIONS: &[&str] = &[
|
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 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";
|
pub const PRETTY_SUPPORTED_ALIASES: &str = "tgz, tbz, tlz4, txz, tzlma, tsz, tzst";
|
||||||
|
|
||||||
/// A wrapper around `CompressionFormat` that allows combinations like `tgz`
|
/// A wrapper around `CompressionFormat` that allows combinations like `tgz`
|
||||||
@ -74,6 +92,7 @@ pub enum CompressionFormat {
|
|||||||
Zstd,
|
Zstd,
|
||||||
/// .zip
|
/// .zip
|
||||||
Zip,
|
Zip,
|
||||||
|
// even if built without RAR support, we still want to recognise the format
|
||||||
/// .rar
|
/// .rar
|
||||||
Rar,
|
Rar,
|
||||||
/// .7z
|
/// .7z
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
use std::{
|
use std::{iter::once, path::PathBuf};
|
||||||
iter::once,
|
|
||||||
path::{Path, PathBuf},
|
|
||||||
};
|
|
||||||
|
|
||||||
use fs_err as fs;
|
use fs_err as fs;
|
||||||
use parse_display::Display;
|
use parse_display::Display;
|
||||||
@ -154,20 +151,20 @@ fn multiple_files(
|
|||||||
assert_same_directory(before, after, !matches!(ext, DirectoryExtension::Zip));
|
assert_same_directory(before, after, !matches!(ext, DirectoryExtension::Zip));
|
||||||
}
|
}
|
||||||
|
|
||||||
// test .rar decompression
|
#[cfg(feature = "unrar")]
|
||||||
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(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn unpack_rar() -> Result<(), Box<dyn std::error::Error>> {
|
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")?);
|
let mut datadir = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR")?);
|
||||||
datadir.push("tests/data");
|
datadir.push("tests/data");
|
||||||
["testfile.rar3.rar.gz", "testfile.rar5.rar"]
|
["testfile.rar3.rar.gz", "testfile.rar5.rar"]
|
||||||
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -6,7 +6,7 @@ expression: "run_ouch(\"ouch decompress a\", dir)"
|
|||||||
- Files with missing extensions: <TMP_DIR>/a
|
- Files with missing extensions: <TMP_DIR>/a
|
||||||
- Decompression formats are detected automatically from file extension
|
- 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: Supported aliases are: tgz, tbz, tlz4, txz, tzlma, tsz, tzst
|
||||||
hint:
|
hint:
|
||||||
hint: Alternatively, you can pass an extension to the '--format' flag:
|
hint: Alternatively, you can pass an extension to the '--format' flag:
|
@ -7,6 +7,6 @@ expression: "run_ouch(\"ouch decompress a b.unknown\", dir)"
|
|||||||
- Files with missing extensions: <TMP_DIR>/a
|
- Files with missing extensions: <TMP_DIR>/a
|
||||||
- Decompression formats are detected automatically from file extension
|
- 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: Supported aliases are: tgz, tbz, tlz4, txz, tzlma, tsz, tzst
|
||||||
|
|
@ -6,7 +6,7 @@ expression: "run_ouch(\"ouch decompress b.unknown\", dir)"
|
|||||||
- Files with unsupported extensions: <TMP_DIR>/b.unknown
|
- Files with unsupported extensions: <TMP_DIR>/b.unknown
|
||||||
- Decompression formats are detected automatically from file extension
|
- 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: Supported aliases are: tgz, tbz, tlz4, txz, tzlma, tsz, tzst
|
||||||
hint:
|
hint:
|
||||||
hint: Alternatively, you can pass an extension to the '--format' flag:
|
hint: Alternatively, you can pass an extension to the '--format' flag:
|
14
tests/ui.rs
14
tests/ui.rs
@ -65,9 +65,17 @@ fn ui_test_err_decompress_missing_extension() {
|
|||||||
|
|
||||||
run_in(dir, "touch", "a b.unknown").unwrap();
|
run_in(dir, "touch", "a b.unknown").unwrap();
|
||||||
|
|
||||||
ui!(run_ouch("ouch decompress a", dir));
|
let name = {
|
||||||
ui!(run_ouch("ouch decompress a b.unknown", dir));
|
let suffix = if cfg!(feature = "unrar") {
|
||||||
ui!(run_ouch("ouch decompress b.unknown", dir));
|
"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]
|
#[test]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user