mirror of
https://github.com/ouch-org/ouch.git
synced 2025-06-06 11:35:45 +00:00
Merge pull request #21 from vrmiguel/dev
Add checking for typos on the compression subcommand and switch panics to errors
This commit is contained in:
commit
dedf7c0eca
@ -13,19 +13,18 @@ description = "A command-line utility for easily compressing and decompressing f
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
colored = "2.0.0"
|
||||
walkdir = "2.3.2"
|
||||
strsim = "0.10.0"
|
||||
flate2 = "1.0.14"
|
||||
bzip2 = "0.4.2"
|
||||
tar = "0.4.33"
|
||||
xz2 = "0.1.6"
|
||||
bzip2 = "0.4.2"
|
||||
flate2 = "1.0.14"
|
||||
zip = "0.5.11"
|
||||
|
||||
|
||||
# Dependency from workspace
|
||||
oof = { path = "./oof" }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
termion = "1.5.6"
|
||||
[profile.release]
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
|
@ -3,21 +3,29 @@ use std::{error, ffi::OsString, fmt};
|
||||
use crate::Flag;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum OofError {
|
||||
pub enum OofError<'t> {
|
||||
FlagValueConflict {
|
||||
flag: Flag,
|
||||
previous_value: OsString,
|
||||
new_value: OsString,
|
||||
},
|
||||
/// User supplied a flag containing invalid Unicode
|
||||
InvalidUnicode(OsString),
|
||||
/// User supplied an unrecognized short flag
|
||||
UnknownShortFlag(char),
|
||||
UnknownLongFlag(String),
|
||||
MisplacedShortArgFlagError(char),
|
||||
MissingValueToFlag(&'t Flag),
|
||||
DuplicatedFlag(&'t Flag)
|
||||
}
|
||||
|
||||
impl error::Error for OofError {
|
||||
impl<'t> error::Error for OofError<'t> {
|
||||
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for OofError {
|
||||
impl<'t> fmt::Display for OofError<'t> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
// TODO: implement proper debug messages
|
||||
match self {
|
||||
@ -30,6 +38,12 @@ impl fmt::Display for OofError {
|
||||
"CLI flag value conflicted for flag '--{}', previous: {:?}, new: {:?}.",
|
||||
flag.long, previous_value, new_value
|
||||
),
|
||||
OofError::InvalidUnicode(flag) => write!(f, "{:?} is not valid Unicode.", flag),
|
||||
OofError::UnknownShortFlag(ch) => write!(f, "Unknown argument '-{}'", ch),
|
||||
OofError::MisplacedShortArgFlagError(ch) => write!(f, "Invalid placement of `-{}`.\nOnly the last letter in a sequence of short flags can take values.", ch),
|
||||
OofError::MissingValueToFlag(flag) => write!(f, "Flag {} takes value but none was supplied.", flag),
|
||||
OofError::DuplicatedFlag(flag) => write!(f, "Duplicated usage of {}.", flag),
|
||||
OofError::UnknownLongFlag(flag) => write!(f, "Unknown argument '--{}'", flag),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,6 +29,15 @@ pub struct Flag {
|
||||
pub takes_value: bool,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Flag {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self.short {
|
||||
Some(short_flag) => write!(f, "-{}/--{}", short_flag, self.long),
|
||||
None => write!(f, "--{}", self.long),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Flag {
|
||||
pub fn long(name: &'static str) -> Self {
|
||||
Self {
|
||||
|
@ -7,10 +7,7 @@ mod error;
|
||||
mod flags;
|
||||
pub mod util;
|
||||
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
ffi::{OsStr, OsString},
|
||||
};
|
||||
use std::{collections::BTreeMap, ffi::{OsStr, OsString}};
|
||||
|
||||
pub use error::OofError;
|
||||
pub use flags::{ArgFlag, Flag, FlagType, Flags};
|
||||
@ -105,10 +102,11 @@ pub fn filter_flags(
|
||||
continue;
|
||||
}
|
||||
|
||||
// If it is a flag, now we try to interpret as valid utf-8
|
||||
let flag: &str = arg
|
||||
.to_str()
|
||||
.unwrap_or_else(|| panic!("User error: The flag needs to be valid utf8"));
|
||||
// If it is a flag, now we try to interpret it as valid utf-8
|
||||
let flag= match arg.to_str() {
|
||||
Some(arg) => arg,
|
||||
None => return Err(OofError::InvalidUnicode(arg))
|
||||
};
|
||||
|
||||
// Only one hyphen in the flag
|
||||
// A short flag can be of form "-", "-abcd", "-h", "-v", etc
|
||||
@ -129,14 +127,14 @@ pub fn filter_flags(
|
||||
// Safety: this loop only runs when len >= 1, so this subtraction is safe
|
||||
let is_last_letter = i == letters.len() - 1;
|
||||
|
||||
let flag_info = short_flags_info.get(&letter).unwrap_or_else(|| {
|
||||
panic!("User error: Unexpected/UNKNOWN flag `letter`, error")
|
||||
});
|
||||
let flag_info = short_flags_info.get(&letter).ok_or(
|
||||
OofError::UnknownShortFlag(letter)
|
||||
)?;
|
||||
|
||||
if !is_last_letter && flag_info.takes_value {
|
||||
panic!("User error: Only the last letter can refer to flag that takes values");
|
||||
return Err(OofError::MisplacedShortArgFlagError(letter))
|
||||
// Because "-AB argument" only works if B takes values, not A.
|
||||
// That is, the short flag that takes values need to come at the end
|
||||
// That is, the short flag that takes values needs to come at the end
|
||||
// of this piece of text
|
||||
}
|
||||
|
||||
@ -145,23 +143,20 @@ pub fn filter_flags(
|
||||
if flag_info.takes_value {
|
||||
// If it was already inserted
|
||||
if result_flags.argument_flags.contains_key(flag_name) {
|
||||
panic!("User error: duplicated, found this flag TWICE!");
|
||||
return Err(OofError::DuplicatedFlag(flag_info));
|
||||
}
|
||||
|
||||
// pop the next one
|
||||
let flag_argument = iter.next().unwrap_or_else(|| {
|
||||
panic!(
|
||||
"USer errror: argument flag `argument_flag` came at last, but it \
|
||||
requires an argument"
|
||||
)
|
||||
});
|
||||
let flag_argument = iter.next().ok_or(
|
||||
OofError::MissingValueToFlag(flag_info)
|
||||
)?;
|
||||
|
||||
// Otherwise, insert it.
|
||||
result_flags.argument_flags.insert(flag_name, flag_argument);
|
||||
} else {
|
||||
// If it was already inserted
|
||||
if result_flags.boolean_flags.contains(flag_name) {
|
||||
panic!("User error: duplicated, found this flag TWICE!");
|
||||
return Err(OofError::DuplicatedFlag(flag_info));
|
||||
}
|
||||
// Otherwise, insert it
|
||||
result_flags.boolean_flags.insert(flag_name);
|
||||
@ -172,29 +167,26 @@ pub fn filter_flags(
|
||||
if let FlagType::Long = flag_type {
|
||||
let flag = trim_double_hyphen(flag);
|
||||
|
||||
let flag_info = long_flags_info.get(flag).unwrap_or_else(|| {
|
||||
panic!("User error: Unexpected/UNKNOWN flag '{}'", flag);
|
||||
});
|
||||
let flag_info = long_flags_info.get(flag).ok_or_else(|| {
|
||||
OofError::UnknownLongFlag(String::from(flag))
|
||||
})?;
|
||||
|
||||
let flag_name = flag_info.long;
|
||||
|
||||
if flag_info.takes_value {
|
||||
// If it was already inserted
|
||||
if result_flags.argument_flags.contains_key(&flag_name) {
|
||||
panic!("User error: duplicated, found this flag TWICE!");
|
||||
return Err(OofError::DuplicatedFlag(flag_info));
|
||||
}
|
||||
|
||||
let flag_argument = iter.next().unwrap_or_else(|| {
|
||||
panic!(
|
||||
"USer errror: argument flag `argument_flag` came at last, but it requires \
|
||||
an argument"
|
||||
)
|
||||
});
|
||||
let flag_argument = iter.next().ok_or(
|
||||
OofError::MissingValueToFlag(flag_info)
|
||||
)?;
|
||||
result_flags.argument_flags.insert(flag_name, flag_argument);
|
||||
} else {
|
||||
// If it was already inserted
|
||||
if result_flags.boolean_flags.contains(&flag_name) {
|
||||
panic!("User error: duplicated, found this flag TWICE!");
|
||||
return Err(OofError::DuplicatedFlag(flag_info));
|
||||
}
|
||||
// Otherwise, insert it
|
||||
result_flags.boolean_flags.insert(&flag_name);
|
||||
|
39
src/cli.rs
39
src/cli.rs
@ -5,8 +5,10 @@ use std::{
|
||||
vec::Vec,
|
||||
};
|
||||
|
||||
use strsim::normalized_damerau_levenshtein;
|
||||
use oof::{arg_flag, flag};
|
||||
|
||||
|
||||
#[derive(PartialEq, Eq, Debug)]
|
||||
pub enum Command {
|
||||
/// Files to be compressed
|
||||
@ -23,12 +25,6 @@ pub enum Command {
|
||||
ShowVersion,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug)]
|
||||
pub struct CommandInfo {
|
||||
pub command: Command,
|
||||
pub flags: oof::Flags,
|
||||
}
|
||||
|
||||
/// Calls parse_args_and_flags_from using std::env::args_os ( argv )
|
||||
pub fn parse_args() -> crate::Result<ParsedArgs> {
|
||||
let args = env::args_os().skip(1).collect();
|
||||
@ -40,6 +36,24 @@ pub struct ParsedArgs {
|
||||
pub flags: oof::Flags,
|
||||
}
|
||||
|
||||
|
||||
/// check_for_typo checks if the first argument is
|
||||
/// a typo for the compress subcommand.
|
||||
/// Returns true if the arg is probably a typo or false otherwise.
|
||||
fn is_typo<'a, P>(path: P) -> bool
|
||||
where
|
||||
P: AsRef<Path> + 'a,
|
||||
{
|
||||
if path.as_ref().exists() {
|
||||
// If the file exists then we won't check for a typo
|
||||
return false;
|
||||
}
|
||||
|
||||
let path = path.as_ref().to_string_lossy();
|
||||
// We'll consider it a typo if the word is somewhat 'close' to "compress"
|
||||
normalized_damerau_levenshtein("compress", &path) > 0.625
|
||||
}
|
||||
|
||||
fn canonicalize<'a, P>(path: P) -> crate::Result<PathBuf>
|
||||
where
|
||||
P: AsRef<Path> + 'a,
|
||||
@ -50,13 +64,14 @@ where
|
||||
if !path.as_ref().exists() {
|
||||
Err(crate::Error::FileNotFound(PathBuf::from(path.as_ref())))
|
||||
} else {
|
||||
eprintln!("[ERROR] {}", io_err);
|
||||
Err(crate::Error::IoError)
|
||||
Err(crate::Error::IoError(io_err))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
fn canonicalize_files<'a, P>(files: Vec<P>) -> crate::Result<Vec<PathBuf>>
|
||||
where
|
||||
P: AsRef<Path> + 'a,
|
||||
@ -85,6 +100,7 @@ pub fn parse_args_from(mut args: Vec<OsString>) -> crate::Result<ParsedArgs> {
|
||||
|
||||
let parsed_args = match oof::pop_subcommand(&mut args, subcommands) {
|
||||
Some(&"compress") => {
|
||||
// `ouch compress` subcommand
|
||||
let (args, flags) = oof::filter_flags(args, &flags_info)?;
|
||||
let mut files: Vec<PathBuf> = args.into_iter().map(PathBuf::from).collect();
|
||||
|
||||
@ -106,6 +122,13 @@ pub fn parse_args_from(mut args: Vec<OsString>) -> crate::Result<ParsedArgs> {
|
||||
// Defaults to decompression when there is no subcommand
|
||||
None => {
|
||||
flags_info.push(arg_flag!('o', "output"));
|
||||
{
|
||||
let first_arg = args.first().unwrap();
|
||||
if is_typo(first_arg) {
|
||||
return Err(crate::Error::CompressionTypo);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Parse flags
|
||||
let (args, mut flags) = oof::filter_flags(args, &flags_info)?;
|
||||
|
@ -4,7 +4,7 @@ use std::{
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use colored::Colorize;
|
||||
use utils::colors;
|
||||
|
||||
use crate::{
|
||||
cli::Command,
|
||||
@ -88,8 +88,9 @@ fn get_decompressor(file: &File) -> crate::Result<(Option<BoxedDecompressor>, Bo
|
||||
None => {
|
||||
// This block *should* be unreachable
|
||||
eprintln!(
|
||||
"{} reached Evaluator::get_decompressor without known extension.",
|
||||
"[internal error]".red()
|
||||
"{}[internal error]{} reached Evaluator::get_decompressor without known extension.",
|
||||
colors::red(),
|
||||
colors::reset()
|
||||
);
|
||||
return Err(crate::Error::InvalidInput);
|
||||
}
|
||||
@ -143,7 +144,7 @@ fn decompress_file_in_memory(
|
||||
None => {
|
||||
// There is no more processing to be done on the input file (or there is but currently unsupported)
|
||||
// Therefore, we'll save what we have in memory into a file.
|
||||
println!("{}: saving to {:?}.", "info".yellow(), file_name);
|
||||
println!("{}[INFO]{} saving to {:?}.", colors::yellow(), colors::reset(), file_name);
|
||||
|
||||
if file_name.exists() {
|
||||
let confirm = Confirmation::new("Do you want to overwrite 'FILE'?", Some("FILE"));
|
||||
@ -203,8 +204,9 @@ fn compress_files(
|
||||
};
|
||||
|
||||
println!(
|
||||
"{}: writing to {:?}. ({})",
|
||||
"info".yellow(),
|
||||
"{}[INFO]{} writing to {:?}. ({})",
|
||||
colors::yellow(),
|
||||
colors::reset(),
|
||||
output_path,
|
||||
utils::Bytes::new(bytes.len() as u64)
|
||||
);
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::{fs, io::Write, path::PathBuf};
|
||||
|
||||
use colored::Colorize;
|
||||
use utils::colors;
|
||||
|
||||
use super::{Compressor, Entry};
|
||||
use crate::{extension::CompressionFormat, file::File, utils};
|
||||
@ -18,8 +18,9 @@ impl BzipCompressor {
|
||||
};
|
||||
|
||||
println!(
|
||||
"{}: compressed {:?} into memory ({})",
|
||||
"info".yellow(),
|
||||
"{}[INFO]{} compressed {:?} into memory ({})",
|
||||
colors::yellow(),
|
||||
colors::reset(),
|
||||
&path,
|
||||
utils::Bytes::new(contents.len() as u64)
|
||||
);
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::{fs, io::Write, path::PathBuf};
|
||||
|
||||
use colored::Colorize;
|
||||
use utils::colors;
|
||||
|
||||
use super::{Compressor, Entry};
|
||||
use crate::{extension::CompressionFormat, file::File, utils};
|
||||
@ -23,8 +23,9 @@ impl GzipCompressor {
|
||||
};
|
||||
|
||||
println!(
|
||||
"{}: compressed {:?} into memory ({})",
|
||||
"info".yellow(),
|
||||
"{}[INFO]{} compressed {:?} into memory ({})",
|
||||
colors::yellow(),
|
||||
colors::reset(),
|
||||
&path,
|
||||
utils::Bytes::new(bytes.len() as u64)
|
||||
);
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::{fs, io::Write, path::PathBuf};
|
||||
|
||||
use colored::Colorize;
|
||||
use utils::colors;
|
||||
|
||||
use super::{Compressor, Entry};
|
||||
use crate::{extension::CompressionFormat, file::File, utils};
|
||||
@ -23,8 +23,9 @@ impl LzmaCompressor {
|
||||
};
|
||||
|
||||
println!(
|
||||
"{}: compressed {:?} into memory ({})",
|
||||
"info".yellow(),
|
||||
"{}[INFO]{} compressed {:?} into memory ({})",
|
||||
colors::yellow(),
|
||||
colors::reset(),
|
||||
&path,
|
||||
utils::Bytes::new(bytes.len() as u64)
|
||||
);
|
||||
|
@ -1,7 +1,7 @@
|
||||
use std::{env, fs, path::PathBuf};
|
||||
|
||||
use colored::Colorize;
|
||||
use tar::Builder;
|
||||
use utils::colors;
|
||||
use walkdir::WalkDir;
|
||||
|
||||
use super::compressor::Entry;
|
||||
@ -13,8 +13,9 @@ impl TarCompressor {
|
||||
// TODO: implement this
|
||||
fn make_archive_from_memory(_input: File) -> crate::Result<Vec<u8>> {
|
||||
println!(
|
||||
"{} .tar.tar and .zip.tar is currently unimplemented.",
|
||||
"[ERROR]".red()
|
||||
"{}[ERROR]{} .tar.tar and .zip.tar is currently unimplemented.",
|
||||
colors::red(),
|
||||
colors::reset()
|
||||
);
|
||||
Err(crate::Error::InvalidZipArchive(""))
|
||||
}
|
||||
|
@ -4,8 +4,9 @@ use std::{
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use colored::Colorize;
|
||||
|
||||
use tar::{self, Archive};
|
||||
use utils::colors;
|
||||
|
||||
use super::decompressor::{DecompressionResult, Decompressor};
|
||||
use crate::{dialogs::Confirmation, file::File, utils};
|
||||
@ -16,8 +17,9 @@ pub struct TarDecompressor;
|
||||
impl TarDecompressor {
|
||||
fn unpack_files(from: File, into: &Path, flags: &oof::Flags) -> crate::Result<Vec<PathBuf>> {
|
||||
println!(
|
||||
"{}: attempting to decompress {:?}",
|
||||
"ouch".bright_blue(),
|
||||
"{}[INFO]{} attempting to decompress {:?}",
|
||||
colors::blue(),
|
||||
colors::reset(),
|
||||
&from.path
|
||||
);
|
||||
let mut files_unpacked = vec![];
|
||||
@ -45,8 +47,9 @@ impl TarDecompressor {
|
||||
file.unpack_in(into)?;
|
||||
|
||||
println!(
|
||||
"{}: {:?} extracted. ({})",
|
||||
"info".yellow(),
|
||||
"{}[INFO]{} {:?} extracted. ({})",
|
||||
colors::yellow(),
|
||||
colors::reset(),
|
||||
into.join(file.path()?),
|
||||
utils::Bytes::new(file.size())
|
||||
);
|
||||
|
@ -3,7 +3,8 @@ use std::{
|
||||
path::Path,
|
||||
};
|
||||
|
||||
use colored::Colorize;
|
||||
|
||||
use utils::colors;
|
||||
|
||||
use super::decompressor::{DecompressionResult, Decompressor};
|
||||
use crate::{extension::CompressionFormat, file::File, utils};
|
||||
@ -35,8 +36,9 @@ impl DecompressorToMemory {
|
||||
let bytes_read = reader.read_to_end(&mut buffer)?;
|
||||
|
||||
println!(
|
||||
"{}: {:?} extracted into memory ({}).",
|
||||
"info".yellow(),
|
||||
"{}[INFO]{} {:?} extracted into memory ({}).",
|
||||
colors::yellow(),
|
||||
colors::reset(),
|
||||
path,
|
||||
utils::Bytes::new(bytes_read as u64)
|
||||
);
|
||||
|
@ -4,7 +4,7 @@ use std::{
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use colored::Colorize;
|
||||
use utils::colors;
|
||||
use zip::{self, read::ZipFile, ZipArchive};
|
||||
|
||||
use super::decompressor::{DecompressionResult, Decompressor};
|
||||
@ -26,8 +26,9 @@ impl ZipDecompressor {
|
||||
let comment = file.comment();
|
||||
if !comment.is_empty() {
|
||||
println!(
|
||||
"{}: Comment in {}: {}",
|
||||
"info".yellow(),
|
||||
"{}[INFO]{} Comment in {}: {}",
|
||||
colors::yellow(),
|
||||
colors::reset(),
|
||||
file.name(),
|
||||
comment
|
||||
);
|
||||
@ -73,8 +74,9 @@ impl ZipDecompressor {
|
||||
}
|
||||
}
|
||||
println!(
|
||||
"{}: \"{}\" extracted. ({})",
|
||||
"info".yellow(),
|
||||
"{}[INFO]{} \"{}\" extracted. ({})",
|
||||
colors::yellow(),
|
||||
colors::reset(),
|
||||
file_path.display(),
|
||||
utils::Bytes::new(file.size())
|
||||
);
|
||||
@ -95,7 +97,7 @@ impl ZipDecompressor {
|
||||
}
|
||||
|
||||
fn unpack_files(from: File, into: &Path, flags: &oof::Flags) -> crate::Result<Vec<PathBuf>> {
|
||||
println!("{} decompressing {:?}", "[OUCH]".bright_blue(), &from.path);
|
||||
println!("{}[INFO]{} decompressing {:?}", colors::blue(), colors::reset(), &from.path);
|
||||
|
||||
match from.contents_in_memory {
|
||||
Some(bytes) => {
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::io::{self, Write};
|
||||
|
||||
use colored::Colorize;
|
||||
use crate::utils::colors;
|
||||
|
||||
pub struct Confirmation<'a> {
|
||||
pub prompt: &'a str,
|
||||
@ -26,7 +26,7 @@ impl<'a> Confirmation<'a> {
|
||||
};
|
||||
|
||||
loop {
|
||||
print!("{} [{}/{}] ", message, "Y".bright_green(), "n".bright_red());
|
||||
print!("{} [{}Y{}/{}n{}] ", message, colors::green(), colors::reset(), colors::red(), colors::reset());
|
||||
io::stdout().flush()?;
|
||||
|
||||
let mut answer = String::new();
|
||||
|
45
src/error.rs
45
src/error.rs
@ -1,23 +1,23 @@
|
||||
use std::{fmt, path::PathBuf};
|
||||
use crate::utils::colors;
|
||||
|
||||
use colored::Colorize;
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
pub enum Error {
|
||||
UnknownExtensionError(String),
|
||||
MissingExtensionError(PathBuf),
|
||||
// TODO: get rid of this error variant
|
||||
InvalidUnicode,
|
||||
InvalidInput,
|
||||
IoError,
|
||||
IoError(std::io::Error),
|
||||
FileNotFound(PathBuf),
|
||||
AlreadyExists,
|
||||
InvalidZipArchive(&'static str),
|
||||
PermissionDenied,
|
||||
UnsupportedZipArchive(&'static str),
|
||||
InternalError,
|
||||
OofError,
|
||||
CompressingRootFolder,
|
||||
MissingArgumentsForCompression,
|
||||
CompressionTypo,
|
||||
WalkdirError,
|
||||
}
|
||||
|
||||
@ -33,7 +33,7 @@ impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Error::MissingExtensionError(filename) => {
|
||||
write!(f, "{} ", "[ERROR]".red())?;
|
||||
write!(f, "{}[ERROR]{} ", colors::red(), colors::reset())?;
|
||||
// TODO: show MIME type of the unsupported file
|
||||
write!(f, "cannot compress to {:?}, likely because it has an unsupported (or missing) extension.", filename)
|
||||
}
|
||||
@ -42,14 +42,14 @@ impl fmt::Display for Error {
|
||||
write!(f, "")
|
||||
}
|
||||
Error::FileNotFound(file) => {
|
||||
write!(f, "{} ", "[ERROR]".red())?;
|
||||
write!(f, "{}[ERROR]{} ", colors::red(), colors::reset())?;
|
||||
if file == &PathBuf::from("") {
|
||||
return write!(f, "file not found!");
|
||||
}
|
||||
write!(f, "file {:?} not found!", file)
|
||||
}
|
||||
Error::CompressingRootFolder => {
|
||||
write!(f, "{} ", "[ERROR]".red())?;
|
||||
write!(f, "{}[ERROR]{} ", colors::red(), colors::reset())?;
|
||||
let spacing = " ";
|
||||
writeln!(f, "It seems you're trying to compress the root folder.")?;
|
||||
writeln!(
|
||||
@ -59,21 +59,28 @@ impl fmt::Display for Error {
|
||||
)?;
|
||||
write!(
|
||||
f,
|
||||
"{}Use a more appropriate tool for this, such as {}.",
|
||||
"{}Use a more appropriate tool for this, such as {}rsync{}.",
|
||||
spacing,
|
||||
"rsync".green()
|
||||
colors::green(),
|
||||
colors::reset()
|
||||
)
|
||||
}
|
||||
Error::MissingArgumentsForCompression => {
|
||||
write!(f, "{} ", "[ERROR]".red())?;
|
||||
write!(f, "{}[ERROR]{} ", colors::red(), colors::reset())?;
|
||||
let spacing = " ";
|
||||
writeln!(f,"The compress subcommands demands at least 2 arguments, an input file and an output file.")?;
|
||||
writeln!(f,"{}Example: `ouch compress img.jpeg img.zip", spacing)?;
|
||||
write!(f,"{}For more information, run `ouch --help`", spacing)
|
||||
}
|
||||
Error::InternalError => {
|
||||
write!(f, "{} ", "[ERROR]".red())?;
|
||||
write!(f, "You've reached an internal error! This really should not have happened.\nPlease file an issue at {}", "https://github.com/vrmiguel/ouch".green())
|
||||
write!(f, "{}[ERROR]{} ", colors::red(), colors::reset())?;
|
||||
write!(f, "You've reached an internal error! This really should not have happened.\nPlease file an issue at {}https://github.com/vrmiguel/ouch{}", colors::green(), colors::reset())
|
||||
}
|
||||
Error::IoError(io_err) => {
|
||||
write!(f, "{}[ERROR]{} {}", colors::red(), colors::reset(), io_err)
|
||||
}
|
||||
Error::CompressionTypo =>{
|
||||
write!(f, "Did you mean {}ouch compress{}?", colors::magenta(), colors::reset())
|
||||
}
|
||||
_err => {
|
||||
// TODO
|
||||
@ -90,8 +97,7 @@ impl From<std::io::Error> for Error {
|
||||
std::io::ErrorKind::PermissionDenied => Self::PermissionDenied,
|
||||
std::io::ErrorKind::AlreadyExists => Self::AlreadyExists,
|
||||
_other => {
|
||||
println!("{} {}", "[IO error]".red(), err);
|
||||
Self::IoError
|
||||
Self::IoError(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -111,13 +117,16 @@ impl From<zip::result::ZipError> for Error {
|
||||
|
||||
impl From<walkdir::Error> for Error {
|
||||
fn from(err: walkdir::Error) -> Self {
|
||||
eprintln!("{} {}", "[ERROR]".red(), err);
|
||||
eprintln!("{}[ERROR]{} {}", colors::red(), colors::reset(), err);
|
||||
Self::WalkdirError
|
||||
}
|
||||
}
|
||||
|
||||
impl From<oof::OofError> for Error {
|
||||
fn from(_err: oof::OofError) -> Self {
|
||||
todo!("We need to implement this properly");
|
||||
impl<'t> From<oof::OofError<'t>> for Error {
|
||||
fn from(err: oof::OofError) -> Self {
|
||||
// To avoid entering a lifetime hell, we'll just print the Oof error here
|
||||
// and skip saving it into a variant of Self
|
||||
println!("{}[ERROR]{} {}", colors::red(), colors::reset(), err);
|
||||
Self::OofError
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ mod error;
|
||||
mod extension;
|
||||
mod file;
|
||||
mod utils;
|
||||
mod test;
|
||||
|
||||
pub use error::{Error, Result};
|
||||
|
||||
|
@ -126,7 +126,7 @@ mod argparsing {
|
||||
|
||||
#[cfg(test)]
|
||||
mod byte_pretty_printing {
|
||||
use crate::bytes::Bytes;
|
||||
use crate::utils::Bytes;
|
||||
#[test]
|
||||
fn bytes() {
|
||||
assert_eq!(&format!("{}", Bytes::new(234)), "234.00 B");
|
||||
|
59
src/utils.rs
59
src/utils.rs
@ -5,8 +5,6 @@ use std::{
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use colored::Colorize;
|
||||
|
||||
use crate::{dialogs::Confirmation, extension::CompressionFormat, file::File};
|
||||
|
||||
#[macro_export]
|
||||
@ -42,10 +40,11 @@ pub fn check_for_multiple_files(
|
||||
) -> crate::Result<()> {
|
||||
if files.len() != 1 {
|
||||
eprintln!(
|
||||
"{}: cannot compress multiple files directly to {:#?}.\n\
|
||||
"{}[ERROR]{} cannot compress multiple files directly to {:#?}.\n\
|
||||
Try using an intermediate archival method such as Tar.\n\
|
||||
Example: filename.tar{}",
|
||||
"[ERROR]".red(),
|
||||
colors::red(),
|
||||
colors::reset(),
|
||||
format,
|
||||
format
|
||||
);
|
||||
@ -58,14 +57,16 @@ pub fn check_for_multiple_files(
|
||||
pub fn create_path_if_non_existent(path: &Path) -> crate::Result<()> {
|
||||
if !path.exists() {
|
||||
println!(
|
||||
"{}: attempting to create folder {:?}.",
|
||||
"[INFO]".yellow(),
|
||||
"{}[INFO]{} attempting to create folder {:?}.",
|
||||
colors::yellow(),
|
||||
colors::reset(),
|
||||
&path
|
||||
);
|
||||
std::fs::create_dir_all(path)?;
|
||||
println!(
|
||||
"{}: directory {:#?} created.",
|
||||
"[INFO]".yellow(),
|
||||
"{}[INFO]{} directory {:#?} created.",
|
||||
colors::yellow(),
|
||||
colors::reset(),
|
||||
fs::canonicalize(&path)?
|
||||
);
|
||||
}
|
||||
@ -106,7 +107,7 @@ pub fn permission_for_overwriting(
|
||||
) -> crate::Result<bool> {
|
||||
match (flags.is_present("yes"), flags.is_present("false")) {
|
||||
(true, true) => {
|
||||
unreachable!("This shoul've been cutted out in the ~/src/cli.rs filter flags function.")
|
||||
unreachable!("This should've been cutted out in the ~/src/cli.rs filter flags function.")
|
||||
}
|
||||
(true, _) => return Ok(true),
|
||||
(_, true) => return Ok(false),
|
||||
@ -130,37 +131,35 @@ pub struct Bytes {
|
||||
#[allow(dead_code)]
|
||||
#[cfg(target_family = "unix")]
|
||||
pub mod colors {
|
||||
use termion::color::*;
|
||||
|
||||
pub fn reset() -> &'static str {
|
||||
Reset.fg_str()
|
||||
pub const fn reset() -> &'static str {
|
||||
"\u{1b}[39m"
|
||||
}
|
||||
pub fn black() -> &'static str {
|
||||
LightBlack.fg_str()
|
||||
pub const fn black() -> &'static str {
|
||||
"\u{1b}[38;5;8m"
|
||||
}
|
||||
pub fn blue() -> &'static str {
|
||||
LightBlue.fg_str()
|
||||
pub const fn blue() -> &'static str {
|
||||
"\u{1b}[38;5;12m"
|
||||
}
|
||||
pub fn cyan() -> &'static str {
|
||||
LightCyan.fg_str()
|
||||
pub const fn cyan() -> &'static str {
|
||||
"\u{1b}[38;5;14m"
|
||||
}
|
||||
pub fn green() -> &'static str {
|
||||
LightGreen.fg_str()
|
||||
pub const fn green() -> &'static str {
|
||||
"\u{1b}[38;5;10m"
|
||||
}
|
||||
pub fn magenta() -> &'static str {
|
||||
LightMagenta.fg_str()
|
||||
pub const fn magenta() -> &'static str {
|
||||
"\u{1b}[38;5;13m"
|
||||
}
|
||||
pub fn red() -> &'static str {
|
||||
LightRed.fg_str()
|
||||
pub const fn red() -> &'static str {
|
||||
"\u{1b}[38;5;9m"
|
||||
}
|
||||
pub fn white() -> &'static str {
|
||||
LightWhite.fg_str()
|
||||
pub const fn white() -> &'static str {
|
||||
"\u{1b}[38;5;15m"
|
||||
}
|
||||
pub fn yellow() -> &'static str {
|
||||
LightYellow.fg_str()
|
||||
pub const fn yellow() -> &'static str {
|
||||
"\u{1b}[38;5;11m"
|
||||
}
|
||||
}
|
||||
// Termion does not support Windows
|
||||
// Windows does not support ANSI escape codes
|
||||
#[allow(dead_code, non_upper_case_globals)]
|
||||
#[cfg(not(target_family = "unix"))]
|
||||
pub mod colors {
|
||||
|
Loading…
x
Reference in New Issue
Block a user