Binary size decreased by 35% (-400KB)

Huge refactor, removed totally `clap` to use our argparsing instead.
500+ modified.
This commit is contained in:
João M. Bezerra 2021-04-04 23:23:51 -03:00
parent b45f38f5fa
commit 28901ec44e
15 changed files with 487 additions and 531 deletions

View File

@ -15,7 +15,6 @@ description = "A command-line utility for easily compressing and decompressing f
[dependencies] [dependencies]
colored = "2.0.0" colored = "2.0.0"
walkdir = "2.3.2" walkdir = "2.3.2"
clap = "2.33.3"
tar = "0.4.33" tar = "0.4.33"
xz2 = "0.1.6" xz2 = "0.1.6"
bzip2 = "0.4.2" bzip2 = "0.4.2"

View File

@ -1,260 +1,199 @@
use std::{ use std::{env, ffi::OsString, io, path::PathBuf, vec::Vec};
convert::TryFrom,
env,
ffi::OsString,
fs,
path::{Path, PathBuf},
vec::Vec,
};
use oof::{arg_flag, flag}; use oof::{arg_flag, flag};
// use clap::{Arg, Values};
// use colored::Colorize;
// use crate::{extension::Extension, file::File};
use crate::file::File;
#[derive(PartialEq, Eq, Debug)] #[derive(PartialEq, Eq, Debug)]
pub enum Command { pub enum Command {
/// Files to be compressed /// Files to be compressed
Compress { Compress {
files: Vec<PathBuf>, files: Vec<PathBuf>,
flags: oof::Flags, compressed_output_path: PathBuf,
}, },
/// Files to be decompressed and their extensions /// Files to be decompressed and their extensions
Decompress { Decompress {
files: Vec<PathBuf>, files: Vec<PathBuf>,
output_folder: Option<PathBuf>, output_folder: Option<PathBuf>,
flags: oof::Flags,
}, },
ShowHelp, ShowHelp,
ShowVersion, ShowVersion,
} }
// #[derive(PartialEq, Eq, Debug)] #[derive(PartialEq, Eq, Debug)]
// pub struct Command { pub struct CommandInfo {
// pub kind: CommandKind, pub command: Command,
// pub output: Option<File>, pub flags: oof::Flags,
// } // pub config: Config, // From .TOML, maybe, in the future
}
// // pub fn clap_app<'a, 'b>() -> clap::App<'a, 'b> { /// Calls parse_args_and_flags_from using std::env::args_os ( argv )
// // clap::App::new("ouch") pub fn parse_args() -> crate::Result<ParsedArgs> {
// // .version("0.1.4") let args = env::args_os().skip(1).collect();
// // .about("ouch is a unified compression & decompression utility") parse_args_from(args)
// // .after_help( }
// // "ouch infers what to based on the extensions of the input files and output file received.
// // Examples: `ouch -i movies.tar.gz classes.zip -o Videos/` in order to decompress files into a folder.
// // `ouch -i headers/ sources/ Makefile -o my-project.tar.gz`
// // `ouch -i image{1..50}.jpeg -o images.zip`
// // Please relate any issues or contribute at https://github.com/vrmiguel/ouch")
// // .author("Vinícius R. Miguel")
// // .help_message("Displays this message and exits")
// // .settings(&[
// // clap::AppSettings::ColoredHelp,
// // clap::AppSettings::ArgRequiredElseHelp,
// // ])
// // .arg(
// // Arg::with_name("input")
// // .required(true)
// // .multiple(true)
// // .long("input")
// // .short("i")
// // .help("The input files or directories.")
// // .takes_value(true),
// // )
// // .arg(
// // Arg::with_name("output")
// // // --output/-o not required when output can be inferred from the input files
// // .required(false)
// // .multiple(false)
// // .long("output")
// // .short("o")
// // .help("The output directory or compressed file.")
// // .takes_value(true),
// // )
// // .arg(
// // Arg::with_name("yes")
// // .required(false)
// // .multiple(false)
// // .long("yes")
// // .short("y")
// // .help("Says yes to all confirmation dialogs.")
// // .conflicts_with("no")
// // .takes_value(false),
// // )
// // .arg(
// // Arg::with_name("no")
// // .required(false)
// // .multiple(false)
// // .long("no")
// // .short("n")
// // .help("Says no to all confirmation dialogs.")
// // .conflicts_with("yes")
// // .takes_value(false),
// // )
// // }
// // pub fn get_matches() -> clap::ArgMatches<'static> { pub struct ParsedArgs {
// // clap_app().get_matches() pub command: Command,
// // } pub flags: oof::Flags,
// pub program_called: OsString, // Useful?
}
// pub fn parse_matches(matches: clap::ArgMatches<'static>) -> { fn canonicalize_files(files: Vec<PathBuf>) -> io::Result<Vec<PathBuf>> {
// let flag = match (matches.is_present("yes"), matches.is_present("no")) { files.into_iter().map(|path| path.canonicalize()).collect()
// (true, true) => unreachable!(), }
// (true, _) => Flags::AlwaysYes,
// (_, true) => Flags::AlwaysNo,
// (_, _) => Flags::None,
// };
// Ok((Command::try_from(matches)?, flag)) pub fn parse_args_from(mut args: Vec<OsString>) -> crate::Result<ParsedArgs> {
// } if oof::matches_any_arg(&args, &["--help", "-h"]) || args.is_empty() {
return Ok(ParsedArgs {
// impl TryFrom<clap::ArgMatches<'static>> for Command { command: Command::ShowHelp,
// type Error = crate::Error; flags: oof::Flags::default(),
});
// fn try_from(matches: clap::ArgMatches<'static>) -> crate::Result<Command> {
// let process_decompressible_input = |input_files: Values| {
// let input_files =
// input_files.map(|filename| (Path::new(filename), Extension::new(filename)));
// for file in input_files.clone() {
// match file {
// (filename, Ok(_)) => {
// let path = Path::new(filename);
// if !path.exists() {
// return Err(crate::Error::FileNotFound(filename.into()));
// }
// }
// (filename, Err(_)) => {
// return Err(crate::Error::InputsMustHaveBeenDecompressible(
// filename.into(),
// ));
// }
// }
// }
// Ok(input_files
// .map(|(filename, extension)| {
// (fs::canonicalize(filename).unwrap(), extension.unwrap())
// })
// .map(File::from)
// .collect::<Vec<_>>())
// };
// // Possibilities:
// // * Case 1: output not supplied, therefore try to infer output by checking if all input files are decompressible
// // * Case 2: output supplied
// let output_was_supplied = matches.is_present("output");
// let input_files = matches.values_of("input").unwrap(); // Safe to unwrap since input is an obligatory argument
// if output_was_supplied {
// let output_file = matches.value_of("output").unwrap(); // Safe unwrap since we've established that output was supplied
// let output_file_extension = Extension::new(output_file);
// let output_is_compressible = output_file_extension.is_ok();
// if output_is_compressible {
// // The supplied output is compressible, so we'll compress our inputs to it
// 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(crate::Error::FileNotFound(path));
// }
// eprintln!("{} {}", "[ERROR]".red(), err);
// return Err(crate::Error::IoError);
// }
// }
// let input_files = canonical_paths.map(Result::unwrap).collect();
// Ok(Command {
// kind: CommandKind::Compression(input_files),
// output: Some(File {
// path: output_file.into(),
// contents_in_memory: None,
// extension: Some(output_file_extension.unwrap()),
// }),
// })
// } else {
// // Output not supplied
// // Checking if input files are decompressible
// let input_files = process_decompressible_input(input_files)?;
// Ok(Command {
// kind: CommandKind::Decompression(input_files),
// output: Some(File {
// path: output_file.into(),
// contents_in_memory: None,
// extension: None,
// }),
// })
// }
// } else {
// // else: output file not supplied
// // Case 1: all input files are decompressible
// // Case 2: error
// let input_files = process_decompressible_input(input_files)?;
// Ok(Command {
// kind: CommandKind::Decompression(input_files),
// output: None,
// })
// }
// }
// }
pub fn parse_args_and_flags() -> crate::Result<Command> {
let args: Vec<OsString> = env::args_os().skip(1).collect();
if oof::matches_any_arg(&args, &["--help", "-h"]) {
return Ok(Command::ShowHelp);
} }
if oof::matches_any_arg(&args, &["--version"]) { if oof::matches_any_arg(&args, &["--version"]) {
return Ok(Command::ShowHelp); return Ok(ParsedArgs {
command: Command::ShowVersion,
flags: oof::Flags::default(),
});
} }
let subcommands = &["compress"]; let subcommands = &["compress"];
let mut flags_info = vec![ let mut flags_info = vec![flag!('y', "yes"), flag!('n', "no")];
flag!('y', "yes"),
flag!('n', "no"),
// flag!('v', "verbose"),
];
match oof::pop_subcommand(&mut args, subcommands) { let parsed_args = match oof::pop_subcommand(&mut args, subcommands) {
Some(&"compress") => { Some(&"compress") => {
let (args, flags) = oof::filter_flags(args, &flags_info)?; let (args, flags) = oof::filter_flags(args, &flags_info)?;
let files = args.into_iter().map(PathBuf::from).collect(); let mut files: Vec<PathBuf> = args.into_iter().map(PathBuf::from).collect();
todo!("Adicionar output_file, que é o files.pop() do fim"); if files.len() < 2 {
Ok(Command::Compress { files, flags }) panic!("The compress subcommands demands at least 2 arguments, see usage:.......");
}
// Safety: we checked that args.len() >= 2
let compressed_output_path = files.pop().unwrap();
let files = canonicalize_files(files)?;
let command = Command::Compress {
files,
compressed_output_path,
};
ParsedArgs { command, flags }
} }
// Defaults to decompression when there is no subcommand // Defaults to decompression when there is no subcommand
None => { None => {
// Specific flag flags_info.push(arg_flag!('o', "output"));
flags_info.push(arg_flag!('o', "output_file"));
// Parse flags // Parse flags
let (args, flags) = oof::filter_flags(args, &flags_info)?; let (args, mut flags) = oof::filter_flags(args, &flags_info)?;
let files = args.into_iter().map(PathBuf::from).collect(); let files: Vec<_> = args.into_iter().map(PathBuf::from).collect();
let output_folder = flags.take_arg("output_folder").map(PathBuf::from); let output_folder = flags.take_arg("output").map(PathBuf::from);
Ok(Command::Decompress { // Is the output here fully correct?
// With the paths not canonicalized?
let command = Command::Decompress {
files, files,
output_folder, output_folder,
flags, };
}) ParsedArgs { command, flags }
} }
_ => unreachable!("You should match each subcommand passed."), _ => unreachable!("You should match each subcommand passed."),
} };
Ok(parsed_args)
}
#[cfg(test)]
mod tests {
use super::*;
fn gen_args(text: &str) -> Vec<OsString> {
let args = text.split_whitespace();
args.map(OsString::from).collect()
}
// // util for test the argument parsing
// macro_rules! test {
// ($expected_command:expr, $input_text:expr) => {{
// assert_eq!(
// $expected_command,
// oof::try_arg_parsing($input_text.split_whitespace())
// )
// }};
// }
macro_rules! parse {
($input_text:expr) => {{
let args = gen_args($input_text);
parse_args_from(args).unwrap()
}};
}
#[test]
// The absolute flags that ignore all the other argparsing rules are --help and --version
fn test_absolute_flags() {
let expected = Command::ShowHelp;
assert_eq!(expected, parse!("").command);
assert_eq!(expected, parse!("-h").command);
assert_eq!(expected, parse!("--help").command);
assert_eq!(expected, parse!("aaaaaaaa --help -o -e aaa").command);
assert_eq!(expected, parse!("aaaaaaaa -h").command);
assert_eq!(expected, parse!("--help compress aaaaaaaa").command);
assert_eq!(expected, parse!("compress --help").command);
assert_eq!(expected, parse!("--version --help").command);
assert_eq!(expected, parse!("aaaaaaaa -v aaaa -h").command);
let expected = Command::ShowVersion;
assert_eq!(expected, parse!("ouch --version").command);
assert_eq!(expected, parse!("ouch a --version b").command);
}
#[test]
fn test_arg_parsing_compress_subcommand() {
let files = ["a", "b", "c"].iter().map(PathBuf::from).collect();
let expected = Command::Compress {
files,
compressed_output_path: "d".into(),
};
assert_eq!(expected, parse!("compress a b c d").command);
}
#[test]
fn test_arg_parsing_decompress_subcommand() {
let files: Vec<_> = ["a", "b", "c"].iter().map(PathBuf::from).collect();
let expected = Command::Decompress {
files: files.clone(),
output_folder: None,
};
assert_eq!(expected, parse!("a b c").command);
let expected = Command::Decompress {
files,
output_folder: Some("folder".into()),
};
assert_eq!(expected, parse!("a b c --output folder").command);
assert_eq!(expected, parse!("a b --output folder c").command);
assert_eq!(expected, parse!("a --output folder b c").command);
assert_eq!(expected, parse!("--output folder a b c").command);
}
// #[test]
// fn test_arg_parsing_decompress_subcommand() {
// let files: Vec<PathBuf> = ["a", "b", "c"].iter().map(PathBuf::from).collect();
// let expected = Ok(Command::Decompress {
// files: files.clone(),
// });
// test!(expected, "ouch a b c");
// let files: Vec<PathBuf> = ["a", "b", "c", "d"].iter().map(PathBuf::from).collect();
// let expected = Ok(Command::Decompress {
// files: files.clone(),
// });
// test!(expected, "ouch a b c d");
// }
} }

View File

@ -8,9 +8,9 @@ use crate::file::File;
// FileInMemory(Vec<u8>) // FileInMemory(Vec<u8>)
// } // }
pub enum Entry { pub enum Entry<'a> {
Files(Vec<PathBuf>), Files(Vec<PathBuf>),
InMemory(File), InMemory(File<'a>),
} }
pub trait Compressor { pub trait Compressor {

View File

@ -5,8 +5,7 @@ use tar::Builder;
use walkdir::WalkDir; use walkdir::WalkDir;
use super::compressor::Entry; use super::compressor::Entry;
use crate::utils; use crate::{compressors::Compressor, file::File, utils};
use crate::{compressors::Compressor, file::File};
pub struct TarCompressor {} pub struct TarCompressor {}

View File

@ -1,6 +1,6 @@
use std::path::PathBuf; use std::path::PathBuf;
use crate::{cli::Flags, file::File}; use crate::file::File;
pub enum DecompressionResult { pub enum DecompressionResult {
FilesUnpacked(Vec<PathBuf>), FilesUnpacked(Vec<PathBuf>),
@ -12,6 +12,6 @@ pub trait Decompressor {
&self, &self,
from: File, from: File,
into: &Option<File>, into: &Option<File>,
flags: Flags, flags: &oof::Flags,
) -> crate::Result<DecompressionResult>; ) -> crate::Result<DecompressionResult>;
} }

View File

@ -8,13 +8,13 @@ use colored::Colorize;
use tar::{self, Archive}; use tar::{self, Archive};
use super::decompressor::{DecompressionResult, Decompressor}; use super::decompressor::{DecompressionResult, Decompressor};
use crate::{cli::Flags, dialogs::Confirmation, file::File, utils}; use crate::{dialogs::Confirmation, file::File, utils};
#[derive(Debug)] #[derive(Debug)]
pub struct TarDecompressor {} pub struct TarDecompressor {}
impl TarDecompressor { impl TarDecompressor {
fn unpack_files(from: File, into: &Path, flags: Flags) -> crate::Result<Vec<PathBuf>> { fn unpack_files(from: File, into: &Path, flags: &oof::Flags) -> crate::Result<Vec<PathBuf>> {
println!( println!(
"{}: attempting to decompress {:?}", "{}: attempting to decompress {:?}",
"ouch".bright_blue(), "ouch".bright_blue(),
@ -64,7 +64,7 @@ impl Decompressor for TarDecompressor {
&self, &self,
from: File, from: File,
into: &Option<File>, into: &Option<File>,
flags: Flags, flags: &oof::Flags,
) -> crate::Result<DecompressionResult> { ) -> crate::Result<DecompressionResult> {
let destination_path = utils::get_destination_path(into); let destination_path = utils::get_destination_path(into);

View File

@ -6,7 +6,7 @@ use std::{
use colored::Colorize; use colored::Colorize;
use super::decompressor::{DecompressionResult, Decompressor}; use super::decompressor::{DecompressionResult, Decompressor};
use crate::{cli::Flags, utils}; use crate::utils;
// use niffler; // use niffler;
use crate::{extension::CompressionFormat, file::File}; use crate::{extension::CompressionFormat, file::File};
@ -28,8 +28,8 @@ fn get_decoder<'a>(
} }
impl DecompressorToMemory { impl DecompressorToMemory {
fn unpack_file(from: &Path, format: CompressionFormat) -> crate::Result<Vec<u8>> { fn unpack_file(path: &Path, format: CompressionFormat) -> crate::Result<Vec<u8>> {
let file = std::fs::read(from)?; let file = std::fs::read(path)?;
let mut reader = get_decoder(format, Box::new(&file[..])); let mut reader = get_decoder(format, Box::new(&file[..]));
@ -39,7 +39,7 @@ impl DecompressorToMemory {
println!( println!(
"{}: {:?} extracted into memory ({} bytes).", "{}: {:?} extracted into memory ({} bytes).",
"info".yellow(), "info".yellow(),
from, path,
bytes_read bytes_read
); );
@ -66,7 +66,7 @@ impl Decompressor for GzipDecompressor {
&self, &self,
from: File, from: File,
into: &Option<File>, into: &Option<File>,
_: Flags, _: &oof::Flags,
) -> crate::Result<DecompressionResult> { ) -> crate::Result<DecompressionResult> {
DecompressorToMemory::decompress(from, CompressionFormat::Gzip, into) DecompressorToMemory::decompress(from, CompressionFormat::Gzip, into)
} }
@ -77,7 +77,7 @@ impl Decompressor for BzipDecompressor {
&self, &self,
from: File, from: File,
into: &Option<File>, into: &Option<File>,
_: Flags, _: &oof::Flags,
) -> crate::Result<DecompressionResult> { ) -> crate::Result<DecompressionResult> {
DecompressorToMemory::decompress(from, CompressionFormat::Bzip, into) DecompressorToMemory::decompress(from, CompressionFormat::Bzip, into)
} }
@ -88,7 +88,7 @@ impl Decompressor for LzmaDecompressor {
&self, &self,
from: File, from: File,
into: &Option<File>, into: &Option<File>,
_: Flags, _: &oof::Flags,
) -> crate::Result<DecompressionResult> { ) -> crate::Result<DecompressionResult> {
DecompressorToMemory::decompress(from, CompressionFormat::Lzma, into) DecompressorToMemory::decompress(from, CompressionFormat::Lzma, into)
} }

View File

@ -8,7 +8,7 @@ use colored::Colorize;
use zip::{self, read::ZipFile, ZipArchive}; use zip::{self, read::ZipFile, ZipArchive};
use super::decompressor::{DecompressionResult, Decompressor}; use super::decompressor::{DecompressionResult, Decompressor};
use crate::{cli::Flags, dialogs::Confirmation, file::File, utils}; use crate::{dialogs::Confirmation, file::File, utils};
#[cfg(unix)] #[cfg(unix)]
fn __unix_set_permissions(file_path: &Path, file: &ZipFile) { fn __unix_set_permissions(file_path: &Path, file: &ZipFile) {
@ -34,13 +34,13 @@ impl ZipDecompressor {
} }
} }
pub fn zip_decompress<T>( pub fn zip_decompress<R>(
archive: &mut ZipArchive<T>, archive: &mut ZipArchive<R>,
into: &Path, into: &Path,
flags: Flags, flags: &oof::Flags,
) -> crate::Result<Vec<PathBuf>> ) -> crate::Result<Vec<PathBuf>>
where where
T: Read + Seek, R: Read + Seek,
{ {
let confirm = Confirmation::new("Do you want to overwrite 'FILE'?", Some("FILE")); let confirm = Confirmation::new("Do you want to overwrite 'FILE'?", Some("FILE"));
let mut unpacked_files = vec![]; let mut unpacked_files = vec![];
@ -94,7 +94,7 @@ impl ZipDecompressor {
Ok(unpacked_files) Ok(unpacked_files)
} }
fn unpack_files(from: File, into: &Path, flags: Flags) -> crate::Result<Vec<PathBuf>> { fn unpack_files(from: File, into: &Path, flags: &oof::Flags) -> crate::Result<Vec<PathBuf>> {
println!("{} decompressing {:?}", "[OUCH]".bright_blue(), &from.path); println!("{} decompressing {:?}", "[OUCH]".bright_blue(), &from.path);
match from.contents_in_memory { match from.contents_in_memory {
@ -119,7 +119,7 @@ impl Decompressor for ZipDecompressor {
&self, &self,
from: File, from: File,
into: &Option<File>, into: &Option<File>,
flags: Flags, flags: &oof::Flags,
) -> crate::Result<DecompressionResult> { ) -> crate::Result<DecompressionResult> {
let destination_path = utils::get_destination_path(into); let destination_path = utils::get_destination_path(into);

View File

@ -15,7 +15,7 @@ pub enum Error {
InvalidZipArchive(&'static str), InvalidZipArchive(&'static str),
PermissionDenied, PermissionDenied,
UnsupportedZipArchive(&'static str), UnsupportedZipArchive(&'static str),
InputsMustHaveBeenDecompressible(PathBuf), // InputsMustBeDecompressible(PathBuf),
InternalError, InternalError,
CompressingRootFolder, CompressingRootFolder,
WalkdirError, WalkdirError,
@ -44,10 +44,10 @@ impl fmt::Display for Error {
write!(f, "{} ", "[ERROR]".red())?; write!(f, "{} ", "[ERROR]".red())?;
write!(f, "cannot compress to \'{}\', likely because it has an unsupported (or missing) extension.", filename) write!(f, "cannot compress to \'{}\', likely because it has an unsupported (or missing) extension.", filename)
} }
Error::InputsMustHaveBeenDecompressible(file) => { // Error::InputsMustBeDecompressible(file) => {
write!(f, "{} ", "[ERROR]".red())?; // write!(f, "{} ", "[ERROR]".red())?;
write!(f, "file '{:?}' is not decompressible", file) // write!(f, "file '{:?}' is not decompressible", file)
} // }
Error::WalkdirError => { Error::WalkdirError => {
// Already printed in the From block // Already printed in the From block
write!(f, "") write!(f, "")
@ -88,7 +88,7 @@ impl fmt::Display for Error {
impl From<std::io::Error> for Error { impl From<std::io::Error> for Error {
fn from(err: std::io::Error) -> Self { fn from(err: std::io::Error) -> Self {
match err.kind() { match err.kind() {
std::io::ErrorKind::NotFound => Self::FileNotFound("".into()), std::io::ErrorKind::NotFound => panic!("{}", err),
std::io::ErrorKind::PermissionDenied => Self::PermissionDenied, std::io::ErrorKind::PermissionDenied => Self::PermissionDenied,
std::io::ErrorKind::AlreadyExists => Self::AlreadyExists, std::io::ErrorKind::AlreadyExists => Self::AlreadyExists,
_other => { _other => {
@ -119,7 +119,7 @@ impl From<walkdir::Error> for Error {
} }
impl From<oof::OofError> for Error { impl From<oof::OofError> for Error {
fn from(err: oof::OofError) -> Self { fn from(_err: oof::OofError) -> Self {
todo!("We need to implement this properly"); todo!("We need to implement this properly");
} }
} }

View File

@ -1,4 +1,8 @@
use std::{ffi::OsStr, fs, io::Write, path::PathBuf}; use std::{
fs,
io::Write,
path::{Path, PathBuf},
};
use colored::Colorize; use colored::Colorize;
@ -20,10 +24,13 @@ use crate::{
pub struct Evaluator {} pub struct Evaluator {}
type BoxedCompressor = Box<dyn Compressor>;
type BoxedDecompressor = Box<dyn Decompressor>;
impl Evaluator { impl Evaluator {
pub fn get_compressor( pub fn get_compressor(
file: &File, file: &File,
) -> crate::Result<(Option<Box<dyn Compressor>>, Box<dyn Compressor>)> { ) -> crate::Result<(Option<BoxedCompressor>, BoxedCompressor)> {
let extension = match &file.extension { let extension = match &file.extension {
Some(extension) => extension.clone(), Some(extension) => extension.clone(),
None => { None => {
@ -65,7 +72,7 @@ impl Evaluator {
pub fn get_decompressor( pub fn get_decompressor(
file: &File, file: &File,
) -> crate::Result<(Option<Box<dyn Decompressor>>, Box<dyn Decompressor>)> { ) -> crate::Result<(Option<BoxedDecompressor>, BoxedDecompressor)> {
let extension = match &file.extension { let extension = match &file.extension {
Some(extension) => extension.clone(), Some(extension) => extension.clone(),
None => { None => {
@ -100,24 +107,25 @@ impl Evaluator {
fn decompress_file_in_memory( fn decompress_file_in_memory(
bytes: Vec<u8>, bytes: Vec<u8>,
file_path: PathBuf, file_path: &Path,
decompressor: Option<Box<dyn Decompressor>>, decompressor: Option<Box<dyn Decompressor>>,
output_file: &Option<File>, output_file: Option<File>,
extension: Option<Extension>, extension: Option<Extension>,
flags: Flags, flags: &oof::Flags,
) -> crate::Result<()> { ) -> crate::Result<()> {
let output_file_path = utils::get_destination_path(output_file); let output_file_path = utils::get_destination_path(&output_file);
let mut filename = file_path let file_name = file_path
.file_stem() .file_stem()
.unwrap_or_else(|| output_file_path.as_os_str()); .map(Path::new)
.unwrap_or(output_file_path);
if filename == "." { if "." == file_name.as_os_str() {
// I believe this is only possible when the supplied input has a name // I believe this is only possible when the supplied input has a name
// of the sort `.tar` or `.zip' and no output has been supplied. // of the sort `.tar` or `.zip' and no output has been supplied.
filename = OsStr::new("ouch-output"); // file_name = OsStr::new("ouch-output");
todo!("Pending review, what is this supposed to do??");
} }
let filename = PathBuf::from(filename);
// If there is a decompressor to use, we'll create a file in-memory and decompress it // If there is a decompressor to use, we'll create a file in-memory and decompress it
let decompressor = match decompressor { let decompressor = match decompressor {
@ -125,43 +133,47 @@ impl Evaluator {
None => { None => {
// There is no more processing to be done on the input file (or there is but currently unsupported) // 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. // Therefore, we'll save what we have in memory into a file.
println!("{}: saving to {:?}.", "info".yellow(), filename); println!("{}: saving to {:?}.", "info".yellow(), file_name);
if filename.exists() { if file_name.exists() {
let confirm = let confirm =
Confirmation::new("Do you want to overwrite 'FILE'?", Some("FILE")); Confirmation::new("Do you want to overwrite 'FILE'?", Some("FILE"));
if !utils::permission_for_overwriting(&filename, flags, &confirm)? { if !utils::permission_for_overwriting(&file_name, flags, &confirm)? {
return Ok(()); return Ok(());
} }
} }
let mut f = fs::File::create(output_file_path.join(filename))?; let mut f = fs::File::create(output_file_path.join(file_name))?;
f.write_all(&bytes)?; f.write_all(&bytes)?;
return Ok(()); return Ok(());
} }
}; };
let file = File { let file = File {
path: filename, path: file_name,
contents_in_memory: Some(bytes), contents_in_memory: Some(bytes),
extension, extension,
}; };
let decompression_result = decompressor.decompress(file, output_file, flags)?; let decompression_result = decompressor.decompress(file, &output_file, flags)?;
if let DecompressionResult::FileInMemory(_) = decompression_result { if let DecompressionResult::FileInMemory(_) = decompression_result {
// Should not be reachable. unreachable!("Shouldn't");
unreachable!();
} }
Ok(()) Ok(())
} }
fn compress_files(files: Vec<PathBuf>, mut output: File, flags: Flags) -> crate::Result<()> { fn compress_files(
files: Vec<PathBuf>,
output_path: &Path,
flags: &oof::Flags,
) -> crate::Result<()> {
let mut output = File::from(output_path)?;
let confirm = Confirmation::new("Do you want to overwrite 'FILE'?", Some("FILE")); let confirm = Confirmation::new("Do you want to overwrite 'FILE'?", Some("FILE"));
let (first_compressor, second_compressor) = Self::get_compressor(&output)?; let (first_compressor, second_compressor) = Self::get_compressor(&output)?;
// TODO: use -y and -n here // TODO: use -y and -n here
let output_path = output.path.clone();
if output_path.exists() if output_path.exists()
&& !utils::permission_for_overwriting(&output_path, flags, &confirm)? && !utils::permission_for_overwriting(&output_path, flags, &confirm)?
{ {
@ -187,7 +199,7 @@ impl Evaluator {
println!( println!(
"{}: writing to {:?}. ({} bytes)", "{}: writing to {:?}. ({} bytes)",
"info".yellow(), "info".yellow(),
&output_path, output_path,
bytes.len() bytes.len()
); );
fs::write(output_path, bytes)?; fs::write(output_path, bytes)?;
@ -195,13 +207,21 @@ impl Evaluator {
Ok(()) Ok(())
} }
fn decompress_file(file: File, output: &Option<File>, flags: Flags) -> crate::Result<()> { fn decompress_file(
file_path: &Path,
output: Option<&Path>,
flags: &oof::Flags,
) -> crate::Result<()> {
let file = File::from(file_path)?;
let output = match output {
Some(inner) => Some(File::from(inner)?),
None => None,
};
let (first_decompressor, second_decompressor) = Self::get_decompressor(&file)?; let (first_decompressor, second_decompressor) = Self::get_decompressor(&file)?;
let file_path = file.path.clone();
let extension = file.extension.clone(); let extension = file.extension.clone();
let decompression_result = second_decompressor.decompress(file, output, flags)?; let decompression_result = second_decompressor.decompress(file, &output, &flags)?;
match decompression_result { match decompression_result {
DecompressionResult::FileInMemory(bytes) => { DecompressionResult::FileInMemory(bytes) => {
@ -230,43 +250,24 @@ impl Evaluator {
Ok(()) Ok(())
} }
pub fn evaluate(command: Command) -> crate::Result<()> { pub fn evaluate(command: Command, flags: &oof::Flags) -> crate::Result<()> {
// Compress {
// files: Vec<PathBuf>,
// flags: oof::Flags,
// },
// /// Files to be decompressed and their extensions
// Decompress {
// files: Vec<PathBuf>,
// output_folder: Option<PathBuf>,
// flags: oof::Flags,
// },
// ShowHelp,
// ShowVersion,
match command { match command {
Command::Compress { files, flags } => {} Command::Compress {
files,
compressed_output_path,
} => Self::compress_files(files, &compressed_output_path, flags)?,
Command::Decompress { Command::Decompress {
files, files,
output_folder, output_folder,
flags,
} => { } => {
// for file in files { decompress } // From Option<PathBuf> to Option<&Path>
} let output_folder = output_folder.as_ref().map(|path| Path::new(path));
Command::ShowHelp => todo!(), for file in files.iter() {
Command::ShowVersion => todo!(), Self::decompress_file(file, output_folder, flags)?;
}
match command.kind {
CommandKind::Compression(files_to_compress) => {
// Safe to unwrap since output is mandatory for compression
let output = output.unwrap();
Self::compress_files(files_to_compress, output, flags)?;
}
CommandKind::Decompression(files_to_decompress) => {
for file in files_to_decompress {
Self::decompress_file(file, &output, flags)?;
} }
} }
Command::ShowHelp => todo!("call help function"),
Command::ShowVersion => todo!("call version function"),
} }
Ok(()) Ok(())
} }

View File

@ -7,6 +7,8 @@ use std::{
use CompressionFormat::*; use CompressionFormat::*;
use crate::utils::to_utf;
/// Represents the extension of a file, but only really caring about /// Represents the extension of a file, but only really caring about
/// compression formats (and .tar). /// compression formats (and .tar).
/// Ex.: Extension::new("file.tar.gz") == Extension { first_ext: Some(Tar), second_ext: Gzip } /// Ex.: Extension::new("file.tar.gz") == Extension { first_ext: Some(Tar), second_ext: Gzip }
@ -16,20 +18,17 @@ pub struct Extension {
pub second_ext: CompressionFormat, pub second_ext: CompressionFormat,
} }
pub fn get_extension_from_filename(filename: &str) -> Option<(&str, &str)> { pub fn get_extension_from_filename(file_name: &OsStr) -> Option<(&OsStr, &OsStr)> {
let path = Path::new(filename); let path = Path::new(file_name);
let ext = path.extension().and_then(OsStr::to_str)?; let ext = path.extension()?;
let previous_extension = path let previous_extension = path.file_stem().and_then(get_extension_from_filename);
.file_stem()
.and_then(OsStr::to_str)
.and_then(get_extension_from_filename);
if let Some((_, prev)) = previous_extension { if let Some((_, prev)) = previous_extension {
Some((prev, ext)) Some((prev, ext))
} else { } else {
Some(("", ext)) Some((OsStr::new(""), ext))
} }
} }
@ -43,32 +42,32 @@ impl From<CompressionFormat> for Extension {
} }
impl Extension { impl Extension {
pub fn new(filename: &str) -> crate::Result<Self> { pub fn from(file_name: &OsStr) -> crate::Result<Self> {
let ext_from_str = |ext| match ext { let compression_format_from = |ext: &OsStr| match ext {
"zip" => Ok(Zip), _ if ext == "zip" => Ok(Zip),
"tar" => Ok(Tar), _ if ext == "tar" => Ok(Tar),
"gz" => Ok(Gzip), _ if ext == "gz" => Ok(Gzip),
"bz" | "bz2" => Ok(Bzip), _ if ext == "bz" || ext == "bz2" => Ok(Bzip),
"xz" | "lz" | "lzma" => Ok(Lzma), _ if ext == "xz" || ext == "lz" || ext == "lzma" => Ok(Lzma),
other => Err(crate::Error::UnknownExtensionError(other.into())), other => Err(crate::Error::UnknownExtensionError(to_utf(other))),
}; };
let (first_ext, second_ext) = match get_extension_from_filename(filename) { let (first_ext, second_ext) = match get_extension_from_filename(&file_name) {
Some(extension_tuple) => match extension_tuple { Some(extension_tuple) => match extension_tuple {
("", snd) => (None, snd), (os_str, snd) if os_str.is_empty() => (None, snd),
(fst, snd) => (Some(fst), snd), (fst, snd) => (Some(fst), snd),
}, },
None => return Err(crate::Error::MissingExtensionError(filename.into())), None => return Err(crate::Error::MissingExtensionError(to_utf(file_name))),
}; };
let (first_ext, second_ext) = match (first_ext, second_ext) { let (first_ext, second_ext) = match (first_ext, second_ext) {
(None, snd) => { (None, snd) => {
let ext = ext_from_str(snd)?; let ext = compression_format_from(snd)?;
(None, ext) (None, ext)
} }
(Some(fst), snd) => { (Some(fst), snd) => {
let snd = ext_from_str(snd)?; let snd = compression_format_from(snd)?;
let fst = ext_from_str(fst).ok(); let fst = compression_format_from(fst).ok();
(fst, snd) (fst, snd)
} }
}; };
@ -130,9 +129,9 @@ impl TryFrom<&PathBuf> for CompressionFormat {
impl TryFrom<&str> for CompressionFormat { impl TryFrom<&str> for CompressionFormat {
type Error = crate::Error; type Error = crate::Error;
fn try_from(filename: &str) -> Result<Self, Self::Error> { fn try_from(file_name: &str) -> Result<Self, Self::Error> {
let filename = Path::new(filename); let file_name = Path::new(file_name);
let ext = match filename.extension() { let ext = match file_name.extension() {
Some(ext) => ext, Some(ext) => ext,
None => return Err(crate::Error::MissingExtensionError(String::new())), None => return Err(crate::Error::MissingExtensionError(String::new())),
}; };

View File

@ -1,11 +1,11 @@
use std::path::PathBuf; use std::path::Path;
use crate::extension::Extension; use crate::extension::Extension;
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct File { pub struct File<'a> {
/// File's (relative) path /// File's (relative) path
pub path: PathBuf, pub path: &'a Path,
/// The bytes that compose the file. /// The bytes that compose the file.
/// Only used when the whole file is kept in-memory /// Only used when the whole file is kept in-memory
pub contents_in_memory: Option<Vec<u8>>, pub contents_in_memory: Option<Vec<u8>>,
@ -17,12 +17,15 @@ pub struct File {
pub extension: Option<Extension>, pub extension: Option<Extension>,
} }
impl From<(PathBuf, Extension)> for File { impl<'a> File<'a> {
fn from((path, format): (PathBuf, Extension)) -> Self { pub fn from(path: &'a Path) -> crate::Result<Self> {
Self { let extension = Extension::from(path.as_ref())?;
eprintln!("dev warning: Should we really ignore the errors from the convertion above?");
Ok(File {
path, path,
contents_in_memory: None, contents_in_memory: None,
extension: Some(format), extension: Some(extension),
} })
} }
} }

View File

@ -12,6 +12,8 @@ mod utils;
use error::{Error, Result}; use error::{Error, Result};
use evaluator::Evaluator; use evaluator::Evaluator;
use crate::cli::ParsedArgs;
fn main() { fn main() {
if let Err(err) = run() { if let Err(err) = run() {
println!("{}", err); println!("{}", err);
@ -20,6 +22,7 @@ fn main() {
} }
fn run() -> crate::Result<()> { fn run() -> crate::Result<()> {
let command = cli::parse_args_and_flags()?; let ParsedArgs { command, flags } = cli::parse_args()?;
Evaluator::evaluate(command) dbg!(&command);
Evaluator::evaluate(command, &flags)
} }

View File

@ -1,211 +1,219 @@
// TODO: remove tests of CompressionFormat::try_from since that's no longer used anywhere // TODO: remove tests of CompressionFormat::try_from since that's no longer used anywhere
#[cfg(test)] // use std::{
mod cli { // convert::TryFrom,
use std::{convert::TryFrom, fs, path::Path}; // ffi::{OsStr, OsString},
// fs,
// path::Path,
// };
use crate::{ // use crate::{
cli::{clap_app, Command, CommandKind::*}, // cli::Command,
extension::{CompressionFormat::*, Extension}, // extension::{CompressionFormat, CompressionFormat::*, Extension},
file::File, // file::File,
}; // };
// ouch's command-line logic uses fs::canonicalize on its inputs so we cannot // // Helper
// use made-up files for testing. // fn gen_args(text: &str) -> Vec<OsString> {
// make_dummy_file therefores creates a small temporary file to bypass fs::canonicalize errors // let args = text.split_whitespace();
fn make_dummy_file<'a, P>(path: P) -> crate::Result<()> // args.map(OsString::from).collect()
where // }
P: AsRef<Path> + 'a,
{
fs::write(path.as_ref(), &[2, 3, 4, 5, 6, 7, 8, 9, 10])?;
Ok(())
}
#[test] // #[cfg(test)]
fn decompress_files_into_folder() -> crate::Result<()> { // mod cli {
make_dummy_file("file.zip")?; // use super::*;
let matches = clap_app().get_matches_from(vec!["ouch", "-i", "file.zip", "-o", "folder/"]);
let command_from_matches = Command::try_from(matches)?;
assert_eq!( // // ouch's command-line logic uses fs::canonicalize on its inputs so we cannot
command_from_matches, // // use made-up files for testing.
Command { // // make_dummy_file therefores creates a small temporary file to bypass fs::canonicalize errors
kind: Decompression(vec![File { // fn make_dummy_file<'a, P>(path: P) -> crate::Result<()>
path: fs::canonicalize("file.zip")?, // where
contents_in_memory: None, // P: AsRef<Path> + 'a,
extension: Some(Extension::from(Zip)) // {
}]), // fs::write(path.as_ref(), &[2, 3, 4, 5, 6, 7, 8, 9, 10])?;
output: Some(File { // Ok(())
path: "folder".into(), // }
contents_in_memory: None,
extension: None
}),
}
);
fs::remove_file("file.zip")?; // #[test]
// fn decompress_files_into_folder() -> crate::Result<()> {
// make_dummy_file("file.zip")?;
// let args = gen_args("ouch -i file.zip -o folder/");
// let (command, flags) = cli::parse_args_and_flags_from(args)?;
Ok(()) // assert_eq!(
} // command,
// Command::Decompress {
// files: args,
// compressed_output_path: PathBuf,
// } // kind: Decompress(vec![File {
// // path: fs::canonicalize("file.zip")?,
// // contents_in_memory: None,
// // extension: Some(Extension::from(Zip))
// // }]),
// // output: Some(File {
// // path: "folder".into(),
// // contents_in_memory: None,
// // extension: None
// // }),
// // }
// );
#[test] // fs::remove_file("file.zip")?;
fn decompress_files() -> crate::Result<()> {
make_dummy_file("my-cool-file.zip")?;
make_dummy_file("file.tar")?;
let matches =
clap_app().get_matches_from(vec!["ouch", "-i", "my-cool-file.zip", "file.tar"]);
let command_from_matches = Command::try_from(matches)?;
assert_eq!( // Ok(())
command_from_matches, // }
Command {
kind: Decompression(vec![
File {
path: fs::canonicalize("my-cool-file.zip")?,
contents_in_memory: None,
extension: Some(Extension::from(Zip))
},
File {
path: fs::canonicalize("file.tar")?,
contents_in_memory: None,
extension: Some(Extension::from(Tar))
}
],),
output: None,
}
);
fs::remove_file("my-cool-file.zip")?; // #[test]
fs::remove_file("file.tar")?; // fn decompress_files() -> crate::Result<()> {
// make_dummy_file("my-cool-file.zip")?;
// make_dummy_file("file.tar")?;
// let matches =
// clap_app().get_matches_from(vec!["ouch", "-i", "my-cool-file.zip", "file.tar"]);
// let command_from_matches = Command::try_from(matches)?;
Ok(()) // assert_eq!(
} // command_from_matches,
// Command {
// kind: Decompress(vec![
// File {
// path: fs::canonicalize("my-cool-file.zip")?,
// contents_in_memory: None,
// extension: Some(Extension::from(Zip))
// },
// File {
// path: fs::canonicalize("file.tar")?,
// contents_in_memory: None,
// extension: Some(Extension::from(Tar))
// }
// ],),
// output: None,
// }
// );
#[test] // fs::remove_file("my-cool-file.zip")?;
fn compress_files() -> crate::Result<()> { // fs::remove_file("file.tar")?;
make_dummy_file("file")?;
make_dummy_file("file2.jpeg")?;
make_dummy_file("file3.ok")?;
let matches = clap_app().get_matches_from(vec![ // Ok(())
"ouch", // }
"-i",
"file",
"file2.jpeg",
"file3.ok",
"-o",
"file.tar",
]);
let command_from_matches = Command::try_from(matches)?;
assert_eq!( // #[test]
command_from_matches, // fn compress_files() -> crate::Result<()> {
Command { // make_dummy_file("file")?;
kind: Compression(vec![ // make_dummy_file("file2.jpeg")?;
fs::canonicalize("file")?, // make_dummy_file("file3.ok")?;
fs::canonicalize("file2.jpeg")?,
fs::canonicalize("file3.ok")?
]),
output: Some(File {
path: "file.tar".into(),
contents_in_memory: None,
extension: Some(Extension::from(Tar))
}),
}
);
fs::remove_file("file")?; // let matches = clap_app().get_matches_from(vec![
fs::remove_file("file2.jpeg")?; // "ouch",
fs::remove_file("file3.ok")?; // "-i",
// "file",
// "file2.jpeg",
// "file3.ok",
// "-o",
// "file.tar",
// ]);
// let command_from_matches = Command::try_from(matches)?;
Ok(()) // assert_eq!(
} // command_from_matches,
} // Command {
// kind: Compress(vec![
// fs::canonicalize("file")?,
// fs::canonicalize("file2.jpeg")?,
// fs::canonicalize("file3.ok")?
// ]),
// output: Some(File {
// path: "file.tar".into(),
// contents_in_memory: None,
// extension: Some(Extension::from(Tar))
// }),
// }
// );
#[cfg(test)] // fs::remove_file("file")?;
mod cli_errors { // fs::remove_file("file2.jpeg")?;
// fs::remove_file("file3.ok")?;
use std::convert::TryFrom; // Ok(())
// }
// }
use crate::cli::{clap_app, Command}; // #[cfg(test)]
// mod cli_errors {
#[test] // #[test]
fn compress_files() -> crate::Result<()> { // fn compress_files() -> crate::Result<()> {
let matches = // let matches =
clap_app().get_matches_from(vec!["ouch", "-i", "a_file", "file2.jpeg", "file3.ok"]); // clap_app().get_matches_from(vec!["ouch", "-i", "a_file", "file2.jpeg", "file3.ok"]);
let res = Command::try_from(matches); // let res = Command::try_from(matches);
assert_eq!( // assert_eq!(
res, // res,
Err(crate::Error::InputsMustHaveBeenDecompressible( // Err(crate::Error::InputsMustHaveBeenDecompressible(
"a_file".into() // "a_file".into()
)) // ))
); // );
Ok(()) // Ok(())
} // }
} // }
#[cfg(test)] // #[cfg(test)]
mod extension_extraction { // mod extension_extraction {
use std::convert::TryFrom;
use crate::extension::{CompressionFormat, Extension}; // #[test]
// fn test_extension_zip() {
// let path = "filename.tar.zip";
// assert_eq!(
// CompressionFormat::try_from(path),
// Ok(CompressionFormat::Zip)
// );
// }
#[test] // #[test]
fn test_extension_zip() { // fn test_extension_tar_gz() {
let path = "filename.tar.zip"; // let extension = Extension::from(OsStr::new("folder.tar.gz")).unwrap();
assert_eq!( // assert_eq!(
CompressionFormat::try_from(path), // extension,
Ok(CompressionFormat::Zip) // Extension {
); // first_ext: Some(CompressionFormat::Tar),
} // second_ext: CompressionFormat::Gzip
// }
// );
// }
#[test] // #[test]
fn test_extension_tar_gz() { // fn test_extension_tar() {
let extension = Extension::new("folder.tar.gz").unwrap(); // let path = "pictures.tar";
assert_eq!( // assert_eq!(
extension, // CompressionFormat::try_from(path),
Extension { // Ok(CompressionFormat::Tar)
first_ext: Some(CompressionFormat::Tar), // );
second_ext: CompressionFormat::Gzip // }
}
);
}
#[test] // #[test]
fn test_extension_tar() { // fn test_extension_gz() {
let path = "pictures.tar"; // let path = "passwords.tar.gz";
assert_eq!( // assert_eq!(
CompressionFormat::try_from(path), // CompressionFormat::try_from(path),
Ok(CompressionFormat::Tar) // Ok(CompressionFormat::Gzip)
); // );
} // }
#[test] // #[test]
fn test_extension_gz() { // fn test_extension_lzma() {
let path = "passwords.tar.gz"; // let path = "mygame.tar.lzma";
assert_eq!( // assert_eq!(
CompressionFormat::try_from(path), // CompressionFormat::try_from(path),
Ok(CompressionFormat::Gzip) // Ok(CompressionFormat::Lzma)
); // );
} // }
#[test] // #[test]
fn test_extension_lzma() { // fn test_extension_bz() {
let path = "mygame.tar.lzma"; // let path = "songs.tar.bz";
assert_eq!( // assert_eq!(
CompressionFormat::try_from(path), // CompressionFormat::try_from(path),
Ok(CompressionFormat::Lzma) // Ok(CompressionFormat::Bzip)
); // );
} // }
// }
#[test]
fn test_extension_bz() {
let path = "songs.tar.bz";
assert_eq!(
CompressionFormat::try_from(path),
Ok(CompressionFormat::Bzip)
);
}
}

View File

@ -1,11 +1,13 @@
use std::{ use std::{
env, fs, env,
ffi::OsStr,
fs,
path::{Path, PathBuf}, path::{Path, PathBuf},
}; };
use colored::Colorize; use colored::Colorize;
use crate::{cli::Flags, dialogs::Confirmation, extension::CompressionFormat, file::File}; use crate::{dialogs::Confirmation, extension::CompressionFormat, file::File};
pub(crate) fn ensure_exists<'a, P>(path: P) -> crate::Result<()> pub(crate) fn ensure_exists<'a, P>(path: P) -> crate::Result<()>
where where
@ -47,13 +49,12 @@ pub(crate) fn create_path_if_non_existent(path: &Path) -> crate::Result<()> {
Ok(()) Ok(())
} }
pub(crate) fn get_destination_path(dest: &Option<File>) -> &Path { pub(crate) fn get_destination_path<'a>(dest: &'a Option<File>) -> &'a Path {
match dest { match dest {
Some(output) => { Some(output_file) => {
// Must be None according to the way command-line arg. parsing in Ouch works // Must be None according to the way command-line arg. parsing in Ouch works
assert_eq!(output.extension, None); assert_eq!(output_file.extension, None);
Path::new(&output_file.path)
Path::new(&output.path)
} }
None => Path::new("."), None => Path::new("."),
} }
@ -68,19 +69,23 @@ pub(crate) fn change_dir_and_return_parent(filename: &Path) -> crate::Result<Pat
return Err(crate::Error::CompressingRootFolder); return Err(crate::Error::CompressingRootFolder);
}; };
env::set_current_dir(parent)?; env::set_current_dir(parent).unwrap();
Ok(previous_location) Ok(previous_location)
} }
pub fn permission_for_overwriting( pub fn permission_for_overwriting(
path: &Path, path: &Path,
flags: Flags, flags: &oof::Flags,
confirm: &Confirmation, confirm: &Confirmation,
) -> crate::Result<bool> { ) -> crate::Result<bool> {
match flags { match (flags.is_present("yes"), flags.is_present("false")) {
Flags::AlwaysYes => return Ok(true), (true, true) => {
Flags::AlwaysNo => return Ok(false), unreachable!("This shoul've been cutted out in the ~/src/cli.rs filter flags function.")
Flags::None => {} }
(true, _) => return Ok(true),
(_, true) => return Ok(false),
_ => {}
} }
let file_path_str = to_utf(path); let file_path_str = to_utf(path);