mirror of
https://github.com/ouch-org/ouch.git
synced 2025-06-07 12:05:46 +00:00
Adding more checks before compression
This commit is contained in:
parent
bb93e46535
commit
33382d06c3
12
src/cli.rs
12
src/cli.rs
@ -25,21 +25,27 @@ pub enum Command {
|
|||||||
ShowVersion,
|
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
|
/// This function is also responsible for treating and checking the cli input
|
||||||
/// Like calling canonicale, checking if it exists.
|
/// Like calling canonicale, checking if it exists.
|
||||||
pub fn parse_args() -> crate::Result<ParsedArgs> {
|
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)?;
|
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 {
|
match &mut parsed_args.command {
|
||||||
Command::Compress { files, .. } | Command::Decompress { files, .. } => {
|
Command::Compress { files, .. } | Command::Decompress { files, .. } => {
|
||||||
*files = canonicalize_files(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)
|
Ok(parsed_args)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
112
src/commands.rs
112
src/commands.rs
@ -9,6 +9,7 @@ use utils::colors;
|
|||||||
use crate::{
|
use crate::{
|
||||||
archive,
|
archive,
|
||||||
cli::Command,
|
cli::Command,
|
||||||
|
error::FinalError,
|
||||||
extension::{
|
extension::{
|
||||||
self,
|
self,
|
||||||
CompressionFormat::{self, *},
|
CompressionFormat::{self, *},
|
||||||
@ -20,12 +21,115 @@ use crate::{
|
|||||||
pub fn run(command: Command, flags: &oof::Flags) -> crate::Result<()> {
|
pub fn run(command: Command, flags: &oof::Flags) -> crate::Result<()> {
|
||||||
match command {
|
match command {
|
||||||
Command::Compress { files, output_path } => {
|
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 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 formats.is_empty() {
|
||||||
if let Err(_err) = compression_result {
|
FinalError::with_title(format!("Cannot compress to '{}'.", to_utf(&output_path)))
|
||||||
fs::remove_file(&output_path).unwrap();
|
.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 } => {
|
Command::Decompress { files, output_folder } => {
|
||||||
let mut output_paths = vec![];
|
let mut output_paths = vec![];
|
||||||
|
@ -28,7 +28,7 @@ pub enum Error {
|
|||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
struct FinalError {
|
pub struct FinalError {
|
||||||
title: String,
|
title: String,
|
||||||
details: Vec<String>,
|
details: Vec<String>,
|
||||||
hints: Vec<String>,
|
hints: Vec<String>,
|
||||||
@ -70,6 +70,11 @@ impl FinalError {
|
|||||||
// Make sure to fix colors
|
// Make sure to fix colors
|
||||||
eprint!("{}", reset());
|
eprint!("{}", reset());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn display_and_crash(&self) -> ! {
|
||||||
|
self.display();
|
||||||
|
std::process::exit(crate::EXIT_FAILURE)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
impl fmt::Display for Error {
|
||||||
|
@ -13,6 +13,8 @@ mod utils;
|
|||||||
use dialogs::Confirmation;
|
use dialogs::Confirmation;
|
||||||
pub use error::{Error, Result};
|
pub use error::{Error, Result};
|
||||||
|
|
||||||
|
pub const EXIT_FAILURE: i32 = 127;
|
||||||
|
|
||||||
const VERSION: &str = "0.1.5";
|
const VERSION: &str = "0.1.5";
|
||||||
const OVERWRITE_CONFIRMATION: Confirmation =
|
const OVERWRITE_CONFIRMATION: Confirmation =
|
||||||
Confirmation::new("Do you want to overwrite 'FILE'?", Some("FILE"));
|
Confirmation::new("Do you want to overwrite 'FILE'?", Some("FILE"));
|
||||||
|
@ -6,7 +6,7 @@ use ouch::{
|
|||||||
fn main() {
|
fn main() {
|
||||||
if let Err(err) = run() {
|
if let Err(err) = run() {
|
||||||
println!("{}", err);
|
println!("{}", err);
|
||||||
std::process::exit(127);
|
std::process::exit(ouch::EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user