Make ouch support paths with dot-dot (..) for input files/directories

This commit is contained in:
Vinícius Rodrigues Miguel 2021-03-25 03:20:20 -03:00
parent b343e7872c
commit 965041310c
5 changed files with 60 additions and 50 deletions

View File

@ -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<PathBuf>,
),
Decompression(
// Files to be decompressed and their extensions
/// Files to be decompressed and their extensions
Vec<File>,
),
}
@ -66,23 +67,30 @@ pub fn get_matches() -> clap::ArgMatches<'static> {
clap_app().get_matches()
}
// holy spaghetti code
impl TryFrom<clap::ArgMatches<'static>> for Command {
type Error = error::Error;
fn try_from(matches: clap::ArgMatches<'static>) -> error::OuchResult<Command> {
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::<Vec<_>>())
};
@ -104,13 +112,20 @@ impl TryFrom<clap::ArgMatches<'static>> 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),

View File

@ -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<PathBuf>) -> OuchResult<Vec<u8>> {
let change_dir_and_return_parent = |filename: &PathBuf| -> OuchResult<PathBuf> {
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()?)

View File

@ -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<T> = Result<T, Error>;
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<std::io::Error> 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<zip::result::ZipError> 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<niffler::error::Error> 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<walkdir::Error> for Error {
fn from(err: walkdir::Error) -> Self {
eprintln!("{}: {}", "error".red(), err);

View File

@ -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) {

View File

@ -8,7 +8,8 @@ where
P: AsRef<Path> + '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<PathBuf>, 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)?
);
}