Skip to content

Commit

Permalink
Improve hints when decompressing with no extension
Browse files Browse the repository at this point in the history
refactored `check_missing_formats_when_decompressing` to be aware of
missing extensions and unsupported extensions in order to give a more
detailed error message
  • Loading branch information
marcospb19 committed Sep 17, 2023
1 parent bc1d945 commit a26d3d3
Show file tree
Hide file tree
Showing 6 changed files with 81 additions and 31 deletions.
55 changes: 39 additions & 16 deletions src/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use std::{

use crate::{
error::FinalError,
extension::{build_archive_file_suggestion, Extension},
extension::{build_archive_file_suggestion, Extension, PRETTY_SUPPORTED_ALIASES, PRETTY_SUPPORTED_EXTENSIONS},
info,
utils::{pretty_format_list_of_paths, try_infer_extension, user_wants_to_continue, EscapedPathDisplay},
warning, QuestionAction, QuestionPolicy, Result,
Expand Down Expand Up @@ -127,32 +127,55 @@ pub fn check_archive_formats_position(formats: &[Extension], output_path: &Path)

/// Check if all provided files have formats to decompress.
pub fn check_missing_formats_when_decompressing(files: &[PathBuf], formats: &[Vec<Extension>]) -> Result<()> {
let files_missing_format: Vec<PathBuf> = files
let files_with_broken_extension: Vec<&PathBuf> = files
.iter()
.zip(formats)
.filter(|(_, format)| format.is_empty())
.map(|(input_path, _)| PathBuf::from(input_path))
.map(|(input_path, _)| input_path)
.collect();

if let Some(path) = files_missing_format.first() {
let error = FinalError::with_title("Cannot decompress files without extensions")
.detail(format!(
"Files without supported extensions: {}",
pretty_format_list_of_paths(&files_missing_format)
))
.detail("Decompression formats are detected automatically by the file extension")
.hint("Provide a file with a supported extension:")
.hint(" ouch decompress example.tar.gz")
if files_with_broken_extension.is_empty() {
return Ok(());
}

let (files_with_unsupported_extensions, files_missing_extension): (Vec<&PathBuf>, Vec<&PathBuf>) =
files_with_broken_extension
.iter()
.partition(|path| path.extension().is_some());

let mut error = FinalError::with_title("Cannot decompress files");

if !files_with_unsupported_extensions.is_empty() {
error = error.detail(format!(
"Files with unsupported extensions: {}",
pretty_format_list_of_paths(&files_with_unsupported_extensions)
));
}

if !files_missing_extension.is_empty() {
error = error.detail(format!(
"Files with missing extensions: {}",
pretty_format_list_of_paths(&files_missing_extension)
));
}

error = error
.detail("Decompression formats are detected automatically from file extension")
.hint(format!("Supported extensions are: {}", PRETTY_SUPPORTED_EXTENSIONS))
.hint(format!("Supported aliases are: {}", PRETTY_SUPPORTED_ALIASES));

// If there's exactly one file, give a suggestion to use `--format`
if let &[path] = files_with_broken_extension.as_slice() {
error = error
.hint("")
.hint("Or overwrite this option with the '--format' flag:")
.hint("Alternatively, you can pass an extension to the '--format' flag:")
.hint(format!(
" ouch decompress {} --format tar.gz",
EscapedPathDisplay::new(path),
));

return Err(error.into());
}
Ok(())

Err(error.into())
}

/// Check if there is a first format when compressing, and returns it.
Expand Down
15 changes: 7 additions & 8 deletions src/extension.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ use bstr::ByteSlice;
use self::CompressionFormat::*;
use crate::{error::Error, warning};

pub const SUPPORTED_EXTENSIONS: &[&str] = &["tar", "zip", "bz", "bz2", "gz", "lz4", "xz", "lzma", "sz", "zst"];
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";
pub const PRETTY_SUPPORTED_ALIASES: &str = "tgz, tbz, tlz4, txz, tzlma, tsz, tzst";

/// A wrapper around `CompressionFormat` that allows combinations like `tgz`
#[derive(Debug, Clone, Eq)]
#[non_exhaustive]
Expand Down Expand Up @@ -85,11 +90,6 @@ impl CompressionFormat {
}
}

pub const SUPPORTED_EXTENSIONS: &[&str] = &[
"tar", "tgz", "tbz", "tlz4", "txz", "tzlma", "tsz", "tzst", "zip", "bz", "bz2", "gz", "lz4", "xz", "lzma", "sz",
"zst",
];

fn to_extension(ext: &[u8]) -> Option<Extension> {
Some(Extension::new(
match ext {
Expand Down Expand Up @@ -156,7 +156,7 @@ pub fn separate_known_extensions_from_name(path: &Path) -> (&Path, Vec<Extension

if let Ok(name) = name.to_str() {
let file_stem = name.trim_matches('.');
if SUPPORTED_EXTENSIONS.contains(&file_stem) {
if SUPPORTED_EXTENSIONS.contains(&file_stem) || SUPPORTED_ALIASES.contains(&file_stem) {
warning!("Received a file with name '{file_stem}', but {file_stem} was expected as the extension.");
}
}
Expand Down Expand Up @@ -208,7 +208,7 @@ pub fn build_archive_file_suggestion(path: &Path, suggested_extension: &str) ->

// If the extension we got is a supported extension, generate the suggestion
// at the position we found
if SUPPORTED_EXTENSIONS.contains(&maybe_extension) {
if SUPPORTED_EXTENSIONS.contains(&maybe_extension) || SUPPORTED_ALIASES.contains(&maybe_extension) {
let mut path = path.to_string();
path.insert_str(position_to_insert - 1, suggested_extension);

Expand All @@ -227,7 +227,6 @@ mod tests {

#[test]
fn test_extensions_from_path() {
use CompressionFormat::*;
let path = Path::new("bolovo.tar.gz");

let extensions: Vec<Extension> = extensions_from_path(path);
Expand Down
Original file line number Diff line number Diff line change
@@ -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: <FOLDER>/b.unknown
- Files with missing extensions: <FOLDER>/a
- Decompression formats are detected automatically from file extension

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

Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
source: tests/ui.rs
expression: "run_ouch(\"ouch decompress b.unknown\", dir)"
---
[ERROR] Cannot decompress files
- Files with unsupported extensions: <FOLDER>/b.unknown
- Decompression formats are detected automatically from file extension

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:
hint: Alternatively, you can pass an extension to the '--format' flag:
hint: ouch decompress <FOLDER>/b.unknown --format tar.gz

Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
source: tests/ui.rs
expression: "run_ouch(\"ouch decompress a\", dir)"
---
[ERROR] Cannot decompress files without extensions
- Files without supported extensions: <FOLDER>/a
- Decompression formats are detected automatically by the file extension
[ERROR] Cannot decompress files
- Files with missing extensions: <FOLDER>/a
- Decompression formats are detected automatically from file extension

hint: Provide a file with a supported extension:
hint: ouch decompress example.tar.gz
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:
hint: Or overwrite this option with the '--format' flag:
hint: Alternatively, you can pass an extension to the '--format' flag:
hint: ouch decompress <FOLDER>/a --format tar.gz

4 changes: 3 additions & 1 deletion tests/ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,11 @@ fn ui_test_err_compress_missing_extension() {
fn ui_test_err_decompress_missing_extension() {
let (_dropper, dir) = testdir().unwrap();

run_in(dir, "touch", "a").unwrap();
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));
}

#[test]
Expand Down

0 comments on commit a26d3d3

Please sign in to comment.