Adding more checks before compression

This commit is contained in:
João M. Bezerra 2021-08-03 21:43:56 -03:00
parent bb93e46535
commit 33382d06c3
5 changed files with 126 additions and 9 deletions

View File

@ -25,21 +25,27 @@ pub enum Command {
ShowVersion,
}
/// Calls parse_args_and_flags_from using std::env::args_os ( argv )
/// Calls parse_args_and_flags_from using argv (std::env::args_os)
///
/// This function is also responsible for treating and checking the cli input
/// Like calling canonicale, checking if it exists.
pub fn parse_args() -> crate::Result<ParsedArgs> {
let args = env::args_os().skip(1).collect();
// From argv, but ignoring empty arguments
let args = env::args_os().skip(1).filter(|arg| !arg.is_empty()).collect();
let mut parsed_args = parse_args_from(args)?;
// If has a list of files, canonicalize them, reporting error if they do now exist
// If has a list of files, canonicalize them, reporting error if they do not exist
match &mut parsed_args.command {
Command::Compress { files, .. } | Command::Decompress { files, .. } => {
*files = canonicalize_files(files)?;
},
_ => {},
}
if parsed_args.flags.is_present("yes") && parsed_args.flags.is_present("no") {
todo!("conflicting flags, better error message.");
}
Ok(parsed_args)
}

View File

@ -9,6 +9,7 @@ use utils::colors;
use crate::{
archive,
cli::Command,
error::FinalError,
extension::{
self,
CompressionFormat::{self, *},
@ -20,12 +21,115 @@ use crate::{
pub fn run(command: Command, flags: &oof::Flags) -> crate::Result<()> {
match command {
Command::Compress { files, output_path } => {
// Formats from path extension, like "file.tar.gz.xz" -> vec![Tar, Gzip, Lzma]
let formats = extension::extensions_from_path(&output_path);
let output_file = fs::File::create(&output_path)?;
let compression_result = compress_files(files, formats, output_file, flags);
if let Err(_err) = compression_result {
fs::remove_file(&output_path).unwrap();
if formats.is_empty() {
FinalError::with_title(format!("Cannot compress to '{}'.", to_utf(&output_path)))
.detail("You shall supply the compression format via the extension.")
.hint("Try adding something like .tar.gz or .zip to the output file.")
.hint("")
.hint("Examples:")
.hint(format!(" ouch compress ... {}.tar.gz", to_utf(&output_path)))
.hint(format!(" ouch compress ... {}.zip", to_utf(&output_path)))
.display_and_crash();
}
if matches!(&formats[0], Bzip | Gzip | Lzma) && files.len() > 1 {
// This piece of code creates a sugestion for compressing multiple files
// It says:
// Change from file.bz.xz
// To file.tar.bz.xz
let extensions_text: String =
formats.iter().map(|format| format.to_string()).collect();
let output_path = to_utf(output_path);
// Breaks if Lzma is .lz or .lzma and not .xz
// Or if Bzip is .bz2 and not .bz
let extensions_start_position = output_path.rfind(&extensions_text).unwrap();
let pos = extensions_start_position;
let empty_range = pos..pos;
let mut suggested_output_path = output_path.clone();
suggested_output_path.replace_range(empty_range, ".tar");
FinalError::with_title(format!(
"Cannot compress to '{}'.",
to_utf(&output_path)
))
.detail("You are trying to compress multiple files.")
.detail(format!(
"The compression format '{}' cannot receive multiple files.",
&formats[0]
))
.detail("The only supported formats that bundle files into an archive are .tar and .zip.")
.hint(format!(
"Try inserting '.tar' or '.zip' before '{}'.",
&formats[0]
))
.hint(format!("From: {}", output_path))
.hint(format!(" To : {}", suggested_output_path))
.display_and_crash();
}
if let Some(format) =
formats.iter().skip(1).position(|format| matches!(format, Tar | Zip))
{
FinalError::with_title(format!("Cannot compress to '{}'.", to_utf(&output_path)))
.detail(format!("Found the format '{}' in an incorrect position.", format))
.detail(format!(
"{} can only be used at the start of the file extension.",
format
))
.hint(format!(
"If you wish to compress multiple files, start the extension with {}.",
format
))
.hint(format!("Otherwise, remove {} from '{}'.", format, to_utf(&output_path)))
.display_and_crash();
}
if output_path.exists() && !utils::permission_for_overwriting(&output_path, flags)? {
// The user does not want to overwrite the file
return Ok(());
}
let output_file = fs::File::create(&output_path).unwrap_or_else(|err| {
FinalError::with_title(format!("Cannot compress to '{}'.", to_utf(&output_path)))
.detail(format!("Could not open file '{}' for writing.", to_utf(&output_path)))
.detail(format!("Error: {}.", err))
.display_and_crash()
});
let compress_result = compress_files(files, formats, output_file, flags);
// If any error occurred, delete incomplete file
if compress_result.is_err() {
// Print an extra alert message pointing out that we left a possibly
// CORRUPTED FILE at `output_path`
if let Err(err) = fs::remove_file(&output_path) {
eprintln!("{red}FATAL ERROR:\n", red = colors::red());
eprintln!(" Please manually delete '{}'.", to_utf(&output_path));
eprintln!(
" Compression failed and we could not delete '{}'.",
to_utf(&output_path),
);
eprintln!(
" Error:{reset} {}{red}.{reset}\n",
err,
reset = colors::reset(),
red = colors::red()
);
}
} else {
println!(
"{}[INFO]{} Successfully compressed '{}'.",
colors::yellow(),
colors::reset(),
to_utf(output_path),
);
}
compress_result?;
},
Command::Decompress { files, output_folder } => {
let mut output_paths = vec![];

View File

@ -28,7 +28,7 @@ pub enum Error {
pub type Result<T> = std::result::Result<T, Error>;
struct FinalError {
pub struct FinalError {
title: String,
details: Vec<String>,
hints: Vec<String>,
@ -70,6 +70,11 @@ impl FinalError {
// Make sure to fix colors
eprint!("{}", reset());
}
pub fn display_and_crash(&self) -> ! {
self.display();
std::process::exit(crate::EXIT_FAILURE)
}
}
impl fmt::Display for Error {

View File

@ -13,6 +13,8 @@ mod utils;
use dialogs::Confirmation;
pub use error::{Error, Result};
pub const EXIT_FAILURE: i32 = 127;
const VERSION: &str = "0.1.5";
const OVERWRITE_CONFIRMATION: Confirmation =
Confirmation::new("Do you want to overwrite 'FILE'?", Some("FILE"));

View File

@ -6,7 +6,7 @@ use ouch::{
fn main() {
if let Err(err) = run() {
println!("{}", err);
std::process::exit(127);
std::process::exit(ouch::EXIT_FAILURE);
}
}