diff --git a/src/cli.rs b/src/cli.rs index fd0fdf6..8d355e2 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,7 +1,8 @@ -use std::{convert::TryFrom, path::PathBuf, vec::Vec}; +use std::{convert::TryFrom, fs, path::{Path, PathBuf}, vec::Vec}; use clap::{Arg, Values}; -// use colored::Colorize; +use colored::Colorize; +use error::Error; use crate::error; use crate::extension::Extension; @@ -10,11 +11,11 @@ use crate::file::File; #[derive(PartialEq, Eq, Debug)] pub enum CommandKind { Compression( - // Files to be compressed + /// Files to be compressed Vec, ), Decompression( - // Files to be decompressed and their extensions + /// Files to be decompressed and their extensions Vec, ), } @@ -66,23 +67,30 @@ pub fn get_matches() -> clap::ArgMatches<'static> { clap_app().get_matches() } -// holy spaghetti code impl TryFrom> for Command { type Error = error::Error; fn try_from(matches: clap::ArgMatches<'static>) -> error::OuchResult { let process_decompressible_input = |input_files: Values| { let input_files = - input_files.map(|filename| (filename, Extension::new(filename))); + input_files.map(|filename| (Path::new(filename), Extension::new(filename))); for file in input_files.clone() { - if let (file, Err(_)) = file { - return Err(error::Error::InputsMustHaveBeenDecompressible(file.into())); + match file { + (filename, Ok(_)) => { + let path = Path::new(filename); + if !path.exists() { + return Err(error::Error::FileNotFound(filename.into())) + } + }, + (filename, Err(_)) => { + return Err(error::Error::InputsMustHaveBeenDecompressible(filename.into())); + } } } Ok(input_files - .map(|(filename, extension)| (PathBuf::from(filename), extension.unwrap())) + .map(|(filename, extension)| (fs::canonicalize(filename).unwrap(), extension.unwrap())) .map(File::from) .collect::>()) }; @@ -104,13 +112,20 @@ impl TryFrom> for Command { if output_is_compressible { // The supplied output is compressible, so we'll compress our inputs to it - // println!( - // "{}: trying to compress input files into '{}'", - // "info".yellow(), - // output_file - // ); + let canonical_paths = input_files.clone().map(Path::new).map(fs::canonicalize); + for (filename, canonical_path) in input_files.zip(canonical_paths.clone()) { + if let Err(err) = canonical_path { + let path = PathBuf::from(filename); + if !path.exists() { + return Err(Error::FileNotFound(path)) + } - let input_files = input_files.map(PathBuf::from).collect(); + eprintln!("{} {}", "[ERROR]".red(), err); + return Err(Error::IOError); + } + } + + let input_files = canonical_paths.map(Result::unwrap).collect(); return Ok(Command { kind: CommandKind::Compression(input_files), diff --git a/src/compressors/tar.rs b/src/compressors/tar.rs index b2ed02f..945f3d3 100644 --- a/src/compressors/tar.rs +++ b/src/compressors/tar.rs @@ -1,4 +1,4 @@ -use std::{fs, path::PathBuf}; +use std::{env, fs, path::PathBuf}; use colored::Colorize; use tar::Builder; @@ -19,13 +19,21 @@ impl TarCompressor { } fn make_archive_from_files(input_filenames: Vec) -> OuchResult> { - + + let change_dir_and_return_parent = |filename: &PathBuf| -> OuchResult { + let previous_location = env::current_dir()?; + let parent = filename.parent().unwrap(); + env::set_current_dir(parent)?; + Ok(previous_location) + }; + let buf = Vec::new(); let mut b = Builder::new(buf); for filename in input_filenames { - // TODO: check if file exists - + let previous_location = change_dir_and_return_parent(&filename)?; + // Safe unwrap since this filename came from `fs::canonicalize`. + let filename = filename.file_name().unwrap(); for entry in WalkDir::new(&filename) { let entry = entry?; let path = entry.path(); @@ -34,6 +42,7 @@ impl TarCompressor { } b.append_file(path, &mut fs::File::open(path)?)?; } + env::set_current_dir(previous_location)?; } Ok(b.into_inner()?) diff --git a/src/error.rs b/src/error.rs index 2d6e9d0..f1b99ed 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,4 +1,4 @@ -use std::fmt; +use std::{fmt, path::PathBuf}; use colored::Colorize; @@ -10,29 +10,29 @@ pub enum Error { InvalidUnicode, InvalidInput, IOError, - FileNotFound, + FileNotFound(PathBuf), AlreadyExists, InvalidZipArchive(&'static str), PermissionDenied, UnsupportedZipArchive(&'static str), - InputsMustHaveBeenDecompressible(String), + InputsMustHaveBeenDecompressible(PathBuf), } pub type OuchResult = Result; impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - + write!(f, "{} ", "[ERROR]".red())?; match self { Error::MissingExtensionError(filename) => { write!(f, "cannot compress to \'{}\', likely because it has an unsupported (or missing) extension.", filename) }, Error::InputsMustHaveBeenDecompressible(file) => { - write!(f, "file '{}' is not decompressible", file.red()) + write!(f, "file '{:?}' is not decompressible", file) }, - // TODO: find out a way to attach the missing file in question here - Error::FileNotFound => { - write!(f, "file not found!") + Error::FileNotFound(file) => { + // TODO: check if file == "" + write!(f, "file {:?} not found!", file) } _err => { // TODO @@ -45,11 +45,11 @@ impl fmt::Display for Error { impl From for Error { fn from(err: std::io::Error) -> Self { match err.kind() { - std::io::ErrorKind::NotFound => Self::FileNotFound, + std::io::ErrorKind::NotFound => Self::FileNotFound("".into()), std::io::ErrorKind::PermissionDenied => Self::PermissionDenied, std::io::ErrorKind::AlreadyExists => Self::AlreadyExists, _other => { - println!("{}: {:#?}", "IO error".red(), err); + println!("{}: {}", "IO error".red(), err); Self::IOError } } @@ -62,27 +62,12 @@ impl From for Error { match err { Io(io_err) => Self::from(io_err), InvalidArchive(filename) => Self::InvalidZipArchive(filename), - FileNotFound => Self::FileNotFound, + FileNotFound => Self::FileNotFound("".into()), UnsupportedArchive(filename) => Self::UnsupportedZipArchive(filename) } } } -// impl From for Error { -// fn from(err: niffler::error::Error) -> Self { -// use niffler::error::Error as NifErr; -// match err { -// NifErr::FeatureDisabled => { -// // Ouch is using Niffler with all its features so -// // this should be unreachable. -// unreachable!(); -// }, -// NifErr::FileTooShort => Self::FileTooShort, -// NifErr::IOError(io_err) => Self::from(io_err) -// } -// } -// } - impl From for Error { fn from(err: walkdir::Error) -> Self { eprintln!("{}: {}", "error".red(), err); diff --git a/src/main.rs b/src/main.rs index a00387c..29408f0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,7 +15,7 @@ mod decompressors; fn main() -> error::OuchResult<()>{ let print_error = |err| { - println!("{}: {}", "error".red(), err); + println!("{}", err); }; let matches = cli::get_matches(); match cli::Command::try_from(matches) { diff --git a/src/utils.rs b/src/utils.rs index 9412e6c..5c7225f 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -8,7 +8,8 @@ where P: AsRef + 'a { let exists = path.as_ref().exists(); if !exists { - eprintln!("{}: could not find file {:?}", "error".red(), path.as_ref()); + eprintln!("{}: could not find file {:?}", "[ERROR]".red(), path.as_ref()); + return Err(Error::FileNotFound(PathBuf::from(path.as_ref()))); } Ok(()) } @@ -16,7 +17,7 @@ where pub (crate) fn check_for_multiple_files(files: &Vec, format: &CompressionFormat) -> OuchResult<()> { if files.len() != 1 { - eprintln!("{}: cannot compress multiple files directly to {:#?}.\n Try using an intermediate archival method such as Tar.\n Example: filename.tar{}", "error".red(), format, format); + eprintln!("{}: cannot compress multiple files directly to {:#?}.\n Try using an intermediate archival method such as Tar.\n Example: filename.tar{}", "[ERROR]".red(), format, format); return Err(Error::InvalidInput); } @@ -27,13 +28,13 @@ pub (crate) fn create_path_if_non_existent(path: &Path) -> OuchResult<()> { if !path.exists() { println!( "{}: attempting to create folder {:?}.", - "info".yellow(), + "[INFO]".yellow(), &path ); std::fs::create_dir_all(path)?; println!( "{}: directory {:#?} created.", - "info".yellow(), + "[INFO]".yellow(), fs::canonicalize(&path)? ); }