diff --git a/Cargo.toml b/Cargo.toml index 7441402..0e70d94 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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 diff --git a/oof/src/error.rs b/oof/src/error.rs index abc8671..86ead8c 100644 --- a/oof/src/error.rs +++ b/oof/src/error.rs @@ -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), } } } diff --git a/oof/src/flags.rs b/oof/src/flags.rs index b31a8d6..043896e 100644 --- a/oof/src/flags.rs +++ b/oof/src/flags.rs @@ -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 { diff --git a/oof/src/lib.rs b/oof/src/lib.rs index ccd4364..87445bd 100644 --- a/oof/src/lib.rs +++ b/oof/src/lib.rs @@ -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); diff --git a/src/cli.rs b/src/cli.rs index 0eb4cc5..1fdcaa3 100644 --- a/src/cli.rs +++ b/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 { 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 + '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 where P: AsRef + '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

) -> crate::Result> where P: AsRef + 'a, @@ -85,6 +100,7 @@ pub fn parse_args_from(mut args: Vec) -> crate::Result { 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 = args.into_iter().map(PathBuf::from).collect(); @@ -106,6 +122,13 @@ pub fn parse_args_from(mut args: Vec) -> crate::Result { // 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)?; diff --git a/src/commands.rs b/src/commands.rs index 2547226..ad5dee8 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -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, 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) ); diff --git a/src/compressors/bzip.rs b/src/compressors/bzip.rs index a08c7c9..102de37 100644 --- a/src/compressors/bzip.rs +++ b/src/compressors/bzip.rs @@ -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) ); diff --git a/src/compressors/gzip.rs b/src/compressors/gzip.rs index 9399469..8e16970 100644 --- a/src/compressors/gzip.rs +++ b/src/compressors/gzip.rs @@ -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) ); diff --git a/src/compressors/lzma.rs b/src/compressors/lzma.rs index d18fc0d..e8f1855 100644 --- a/src/compressors/lzma.rs +++ b/src/compressors/lzma.rs @@ -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) ); diff --git a/src/compressors/tar.rs b/src/compressors/tar.rs index 0e0c417..60d891d 100644 --- a/src/compressors/tar.rs +++ b/src/compressors/tar.rs @@ -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> { 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("")) } diff --git a/src/decompressors/tar.rs b/src/decompressors/tar.rs index 7981750..4d91358 100644 --- a/src/decompressors/tar.rs +++ b/src/decompressors/tar.rs @@ -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> { 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()) ); diff --git a/src/decompressors/to_memory.rs b/src/decompressors/to_memory.rs index a76e060..d7aaf63 100644 --- a/src/decompressors/to_memory.rs +++ b/src/decompressors/to_memory.rs @@ -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) ); diff --git a/src/decompressors/zip.rs b/src/decompressors/zip.rs index ac2120c..501f25d 100644 --- a/src/decompressors/zip.rs +++ b/src/decompressors/zip.rs @@ -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> { - println!("{} decompressing {:?}", "[OUCH]".bright_blue(), &from.path); + println!("{}[INFO]{} decompressing {:?}", colors::blue(), colors::reset(), &from.path); match from.contents_in_memory { Some(bytes) => { diff --git a/src/dialogs.rs b/src/dialogs.rs index 2902a41..645a432 100644 --- a/src/dialogs.rs +++ b/src/dialogs.rs @@ -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(); diff --git a/src/error.rs b/src/error.rs index 3b7c3e0..2c869c0 100644 --- a/src/error.rs +++ b/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 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 for Error { impl From for Error { fn from(err: walkdir::Error) -> Self { - eprintln!("{} {}", "[ERROR]".red(), err); + eprintln!("{}[ERROR]{} {}", colors::red(), colors::reset(), err); Self::WalkdirError } } -impl From for Error { - fn from(_err: oof::OofError) -> Self { - todo!("We need to implement this properly"); +impl<'t> From> 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 } } diff --git a/src/lib.rs b/src/lib.rs index fa9bad7..70fc347 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,6 +10,7 @@ mod error; mod extension; mod file; mod utils; +mod test; pub use error::{Error, Result}; diff --git a/src/test.rs b/src/test.rs index 4d61540..95700a0 100644 --- a/src/test.rs +++ b/src/test.rs @@ -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"); diff --git a/src/utils.rs b/src/utils.rs index a607180..99020aa 100644 --- a/src/utils.rs +++ b/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 { 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 {