Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use path display and remove our custom display impl #639

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/archive/rar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

use std::path::Path;

use unrar::{self, Archive};
use unrar::Archive;

use crate::{error::Error, info, list::FileInArchive};

Expand Down
4 changes: 2 additions & 2 deletions src/archive/sevenz.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use same_file::Handle;
use crate::{
error::FinalError,
info,
utils::{self, cd_into_same_dir_as, Bytes, EscapedPathDisplay, FileVisibilityPolicy},
utils::{self, cd_into_same_dir_as, Bytes, FileVisibilityPolicy},
warning,
};

Expand Down Expand Up @@ -56,7 +56,7 @@ where
// spoken text for users using screen readers, braille displays
// and so on
if !quiet {
info!(inaccessible, "Compressing '{}'.", EscapedPathDisplay::new(path));
info!(inaccessible, "Compressing '{}'.", path.display());
}

let metadata = match path.metadata() {
Expand Down
4 changes: 2 additions & 2 deletions src/archive/tar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use crate::{
error::FinalError,
info,
list::FileInArchive,
utils::{self, Bytes, EscapedPathDisplay, FileVisibilityPolicy},
utils::{self, Bytes, FileVisibilityPolicy},
warning,
};

Expand Down Expand Up @@ -120,7 +120,7 @@ where
// spoken text for users using screen readers, braille displays
// and so on
if !quiet {
info!(inaccessible, "Compressing '{}'.", EscapedPathDisplay::new(path));
info!(inaccessible, "Compressing '{}'.", path.display());
}

if path.is_dir() {
Expand Down
6 changes: 3 additions & 3 deletions src/archive/zip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ use filetime_creation::{set_file_mtime, FileTime};
use fs_err as fs;
use same_file::Handle;
use time::OffsetDateTime;
use zip::{self, read::ZipFile, DateTime, ZipArchive};
use zip::{read::ZipFile, DateTime, ZipArchive};

use crate::{
error::FinalError,
info,
list::FileInArchive,
utils::{
self, cd_into_same_dir_as, get_invalid_utf8_paths, pretty_format_list_of_paths, strip_cur_dir, Bytes,
EscapedPathDisplay, FileVisibilityPolicy,
FileVisibilityPolicy,
},
warning,
};
Expand Down Expand Up @@ -191,7 +191,7 @@ where
// spoken text for users using screen readers, braille displays
// and so on
if !quiet {
info!(inaccessible, "Compressing '{}'.", EscapedPathDisplay::new(path));
info!(inaccessible, "Compressing '{}'.", path.display());
}

let metadata = match path.metadata() {
Expand Down
50 changes: 21 additions & 29 deletions src/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ use std::{

use crate::{
error::FinalError,
extension::{build_archive_file_suggestion, Extension, PRETTY_SUPPORTED_ALIASES, PRETTY_SUPPORTED_EXTENSIONS},
extension::{build_archive_file_suggestion, Extension},
info,
utils::{pretty_format_list_of_paths, try_infer_extension, user_wants_to_continue, EscapedPathDisplay},
utils::{pretty_format_list_of_paths, try_infer_extension, user_wants_to_continue},
warning, QuestionAction, QuestionPolicy, Result,
};

Expand Down Expand Up @@ -103,22 +103,19 @@ pub fn check_for_non_archive_formats(files: &[PathBuf], formats: &[Vec<Extension
/// Show error if archive format is not the first format in the chain.
pub fn check_archive_formats_position(formats: &[Extension], output_path: &Path) -> Result<()> {
if let Some(format) = formats.iter().skip(1).find(|format| format.is_archive()) {
let error = FinalError::with_title(format!(
"Cannot compress to '{}'.",
EscapedPathDisplay::new(output_path)
))
.detail(format!("Found the format '{format}' in an incorrect position."))
.detail(format!(
"'{format}' can only be used at the start of the file extension."
))
.hint(format!(
"If you wish to compress multiple files, start the extension with '{format}'."
))
.hint(format!(
"Otherwise, remove the last '{}' from '{}'.",
format,
EscapedPathDisplay::new(output_path)
));
let error = FinalError::with_title(format!("Cannot compress to '{}'.", output_path.display()))
.detail(format!("Found the format '{format}' in an incorrect position."))
.detail(format!(
"'{format}' can only be used at the start of the file extension."
))
.hint(format!(
"If you wish to compress multiple files, start the extension with '{format}'."
))
.hint(format!(
"Otherwise, remove the last '{}' from '{}'.",
format,
output_path.display()
));

return Err(error.into());
}
Expand Down Expand Up @@ -159,20 +156,15 @@ pub fn check_missing_formats_when_decompressing(files: &[PathBuf], formats: &[Ve
));
}

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));
error = error.detail("Decompression formats are detected automatically from file extension");
error = error.hint_all_supported_formats();

// 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("Alternatively, you can pass an extension to the '--format' flag:")
.hint(format!(
" ouch decompress {} --format tar.gz",
EscapedPathDisplay::new(path),
));
.hint(format!(" ouch decompress {} --format tar.gz", path.display(),));
}

Err(error.into())
Expand All @@ -181,7 +173,7 @@ pub fn check_missing_formats_when_decompressing(files: &[PathBuf], formats: &[Ve
/// Check if there is a first format when compressing, and returns it.
pub fn check_first_format_when_compressing<'a>(formats: &'a [Extension], output_path: &Path) -> Result<&'a Extension> {
formats.first().ok_or_else(|| {
let output_path = EscapedPathDisplay::new(output_path);
let output_path = output_path.display();
FinalError::with_title(format!("Cannot compress to '{output_path}'."))
.detail("You shall supply the compression format")
.hint("Try adding supported extensions (see --help):")
Expand Down Expand Up @@ -235,11 +227,11 @@ pub fn check_invalid_compression_with_non_archive_format(
.expect("output path should contain a compression format");

(
format!("From: {}", EscapedPathDisplay::new(output_path)),
format!("From: {}", output_path.display()),
format!("To: {suggested_output_path}"),
)
};
let output_path = EscapedPathDisplay::new(output_path);
let output_path = output_path.display();

let error = FinalError::with_title(format!("Cannot compress to '{output_path}'."))
.detail(first_detail_message)
Expand Down
1 change: 0 additions & 1 deletion src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ mod args;
use std::{
io,
path::{Path, PathBuf},
vec::Vec,
};

use clap::Parser;
Expand Down
19 changes: 8 additions & 11 deletions src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ use crate::{
cli::Subcommand,
commands::{compress::compress_files, decompress::decompress_file, list::list_archive_contents},
error::{Error, FinalError},
extension::{self, parse_format},
extension::{self, parse_format_flag},
info,
list::ListOptions,
utils::{self, to_utf, EscapedPathDisplay, FileVisibilityPolicy},
utils::{self, path_to_str, FileVisibilityPolicy},
warning, CliArgs, QuestionPolicy,
};

Expand Down Expand Up @@ -66,7 +66,7 @@ pub fn run(
// Formats from path extension, like "file.tar.gz.xz" -> vec![Tar, Gzip, Lzma]
let (formats_from_flag, formats) = match args.format {
Some(formats) => {
let parsed_formats = parse_format(&formats)?;
let parsed_formats = parse_format_flag(&formats)?;
(Some(formats), parsed_formats)
}
None => (None, extension::extensions_from_path(&output_path)),
Expand Down Expand Up @@ -109,18 +109,15 @@ pub fn run(
// having a final status message is important especially in an accessibility context
// as screen readers may not read a commands exit code, making it hard to reason
// about whether the command succeeded without such a message
info!(accessible, "Successfully compressed '{}'.", to_utf(&output_path));
info!(accessible, "Successfully compressed '{}'.", path_to_str(&output_path));
} else {
// If Ok(false) or Err() occurred, delete incomplete file at `output_path`
//
// if deleting fails, print an extra alert message pointing
// out that we left a possibly CORRUPTED file at `output_path`
if utils::remove_file_or_dir(&output_path).is_err() {
eprintln!("{red}FATAL ERROR:\n", red = *colors::RED);
eprintln!(
" Ouch failed to delete the file '{}'.",
EscapedPathDisplay::new(&output_path)
);
eprintln!(" Ouch failed to delete the file '{}'.", &output_path.display());
eprintln!(" Please delete it manually.");
eprintln!(" This file is corrupted if compression didn't finished.");

Expand All @@ -137,10 +134,10 @@ pub fn run(
let mut formats = vec![];

if let Some(format) = args.format {
let format = parse_format(&format)?;
let format = parse_format_flag(&format)?;
for path in files.iter() {
let file_name = path.file_name().ok_or_else(|| Error::NotFound {
error_title: format!("{} does not have a file name", EscapedPathDisplay::new(path)),
error_title: format!("{} does not have a file name", path.display()),
})?;
output_paths.push(file_name.as_ref());
formats.push(format.clone());
Expand Down Expand Up @@ -189,7 +186,7 @@ pub fn run(
let mut formats = vec![];

if let Some(format) = args.format {
let format = parse_format(&format)?;
let format = parse_format_flag(&format)?;
for _ in 0..files.len() {
formats.push(format.clone());
}
Expand Down
96 changes: 61 additions & 35 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,21 @@

use std::{
borrow::Cow,
ffi::OsString,
fmt::{self, Display},
io,
};

use crate::{accessible::is_running_in_accessible_mode, utils::colors::*};
use crate::{
accessible::is_running_in_accessible_mode,
extension::{PRETTY_SUPPORTED_ALIASES, PRETTY_SUPPORTED_EXTENSIONS},
utils::os_str_to_str,
};

/// All errors that can be generated by `ouch`
#[derive(Debug)]
#[derive(Debug, Clone)]
pub enum Error {
/// Not every IoError, some of them get filtered by `From<io::Error>` into other variants
/// An IoError that doesn't have a dedicated error variant
IoError { reason: String },
/// From lzzzz::lz4f::Error
Lz4Error { reason: String },
Expand All @@ -33,9 +39,9 @@ pub enum Error {
/// Custom and unique errors are reported in this variant
Custom { reason: FinalError },
/// Invalid format passed to `--format`
InvalidFormat { reason: String },
InvalidFormatFlag { text: OsString, reason: String },
/// From sevenz_rust::Error
SevenzipError(sevenz_rust::Error),
SevenzipError { reason: String },
/// Recognised but unsupported format
// currently only RAR when built without the `unrar` feature
UnsupportedFormat { reason: String },
Expand All @@ -60,6 +66,8 @@ pub struct FinalError {

impl Display for FinalError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use crate::utils::colors::*;

// Title
//
// When in ACCESSIBLE mode, the square brackets are suppressed
Expand Down Expand Up @@ -120,55 +128,71 @@ impl FinalError {
self.hints.push(hint.into());
self
}

/// Adds all supported formats as hints.
///
/// 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
/// ```
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))
}
}

impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let err = match self {
Error::WalkdirError { reason } => FinalError::with_title(reason.to_string()),
Error::NotFound { error_title } => FinalError::with_title(error_title.to_string()).detail("File not found"),
impl From<Error> for FinalError {
fn from(err: Error) -> Self {
match err {
Error::WalkdirError { reason } => FinalError::with_title(reason),
Error::NotFound { error_title } => FinalError::with_title(error_title).detail("File not found"),
Error::CompressingRootFolder => {
FinalError::with_title("It seems you're trying to compress the root folder.")
.detail("This is unadvisable since ouch does compressions in-memory.")
.hint("Use a more appropriate tool for this, such as rsync.")
}
Error::IoError { reason } => FinalError::with_title(reason.to_string()),
Error::Lz4Error { reason } => FinalError::with_title(reason.to_string()),
Error::AlreadyExists { error_title } => {
FinalError::with_title(error_title.to_string()).detail("File already exists")
}
Error::InvalidZipArchive(reason) => FinalError::with_title("Invalid zip archive").detail(*reason),
Error::PermissionDenied { error_title } => {
FinalError::with_title(error_title.to_string()).detail("Permission denied")
Error::IoError { reason } => FinalError::with_title(reason),
Error::Lz4Error { reason } => FinalError::with_title(reason),
Error::AlreadyExists { error_title } => FinalError::with_title(error_title).detail("File already exists"),
Error::InvalidZipArchive(reason) => FinalError::with_title("Invalid zip archive").detail(reason),
Error::PermissionDenied { error_title } => FinalError::with_title(error_title).detail("Permission denied"),
Error::UnsupportedZipArchive(reason) => FinalError::with_title("Unsupported zip archive").detail(reason),
Error::InvalidFormatFlag { reason, text } => {
FinalError::with_title(format!("Failed to parse `--format {}`", os_str_to_str(&text)))
.detail(reason)
.hint_all_supported_formats()
.hint("")
.hint("Examples:")
.hint(" --format tar")
.hint(" --format gz")
.hint(" --format tar.gz")
}
Error::UnsupportedZipArchive(reason) => FinalError::with_title("Unsupported zip archive").detail(*reason),
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::SevenzipError { reason } => FinalError::with_title("7z error").detail(reason),
Error::UnsupportedFormat { reason } => {
FinalError::with_title("Recognised but unsupported format").detail(reason.clone())
}
};
}
}
}

impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let err = FinalError::from(self.clone());
write!(f, "{err}")
}
}

impl From<std::io::Error> for Error {
fn from(err: std::io::Error) -> Self {
let error_title = err.to_string();

match err.kind() {
std::io::ErrorKind::NotFound => Self::NotFound {
error_title: err.to_string(),
},
std::io::ErrorKind::PermissionDenied => Self::PermissionDenied {
error_title: err.to_string(),
},
std::io::ErrorKind::AlreadyExists => Self::AlreadyExists {
error_title: err.to_string(),
},
_other => Self::IoError {
reason: err.to_string(),
},
io::ErrorKind::NotFound => Self::NotFound { error_title },
io::ErrorKind::PermissionDenied => Self::PermissionDenied { error_title },
io::ErrorKind::AlreadyExists => Self::AlreadyExists { error_title },
_other => Self::IoError { reason: error_title },
}
}
}
Expand Down Expand Up @@ -198,7 +222,9 @@ impl From<unrar::error::UnrarError> for Error {

impl From<sevenz_rust::Error> for Error {
fn from(err: sevenz_rust::Error) -> Self {
Self::SevenzipError(err)
Self::SevenzipError {
reason: err.to_string(),
}
}
}

Expand Down
Loading
Loading