mirror of
https://github.com/ouch-org/ouch.git
synced 2025-06-05 02:55:31 +00:00
Make ouch support paths with dot-dot (..) for input files/directories
This commit is contained in:
parent
b343e7872c
commit
965041310c
45
src/cli.rs
45
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<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),
|
||||
|
@ -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()?)
|
||||
|
37
src/error.rs
37
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<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);
|
||||
|
@ -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) {
|
||||
|
@ -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)?
|
||||
);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user