Merge pull request #8 from vrmiguel/yes-and-no

Add `-y, --yes` and `-n, --no` flags
This commit is contained in:
Vinícius Miguel 2021-03-29 02:54:12 -03:00 committed by GitHub
commit 70d8e37cea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 112 additions and 55 deletions

View File

@ -22,6 +22,16 @@ pub enum CommandKind {
),
}
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub enum Flags {
// No flags supplied
None,
// Flag -y, --yes supplied
AlwaysYes,
// Flag -n, --no supplied
AlwaysNo
}
#[derive(PartialEq, Eq, Debug)]
pub struct Command {
pub kind: CommandKind,
@ -63,12 +73,43 @@ Please relate any issues or contribute at https://github.com/vrmiguel/ouch")
.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> {
clap_app().get_matches()
}
pub fn parse_matches(matches: clap::ArgMatches<'static>) -> crate::Result<(Command, Flags)> {
let flag = match (matches.is_present("yes"), matches.is_present("no")) {
(true, true) => unreachable!(),
(true, _) => Flags::AlwaysYes,
(_, true) => Flags::AlwaysNo,
(_, _) => Flags::None
};
Ok((Command::try_from(matches)?, flag))
}
impl TryFrom<clap::ArgMatches<'static>> for Command {
type Error = crate::Error;

View File

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

View File

@ -8,13 +8,13 @@ use colored::Colorize;
use tar::{self, Archive};
use super::decompressor::{DecompressionResult, Decompressor};
use crate::{file::File, utils, dialogs::Confirmation};
use crate::{cli::Flags, dialogs::Confirmation, file::File, utils};
#[derive(Debug)]
pub struct TarDecompressor {}
impl TarDecompressor {
fn unpack_files(from: File, into: &Path) -> crate::Result<Vec<PathBuf>> {
fn unpack_files(from: File, into: &Path, flags: Flags) -> crate::Result<Vec<PathBuf>> {
println!(
"{}: attempting to decompress {:?}",
"ouch".bright_blue(),
@ -24,7 +24,9 @@ impl TarDecompressor {
let confirm = Confirmation::new("Do you want to overwrite 'FILE'?", Some("FILE"));
let mut archive: Archive<Box<dyn Read>> = match from.contents_in_memory {
Some(bytes) => tar::Archive::new(Box::new(Cursor::new(bytes))),
Some(bytes) => {
tar::Archive::new(Box::new(Cursor::new(bytes)))
},
None => {
let file = fs::File::open(&from.path)?;
tar::Archive::new(Box::new(file))
@ -36,8 +38,7 @@ impl TarDecompressor {
let file_path = PathBuf::from(into).join(file.path()?);
if file_path.exists() {
let file_path_str = &*file_path.to_string_lossy();
if !confirm.ask(Some(file_path_str))? {
if !utils::permission_for_overwriting(&file_path, flags, &confirm)? {
// The user does not want to overwrite the file
continue;
}
@ -61,12 +62,12 @@ impl TarDecompressor {
}
impl Decompressor for TarDecompressor {
fn decompress(&self, from: File, into: &Option<File>) -> crate::Result<DecompressionResult> {
fn decompress(&self, from: File, into: &Option<File>, flags: Flags) -> crate::Result<DecompressionResult> {
let destination_path = utils::get_destination_path(into);
utils::create_path_if_non_existent(destination_path)?;
let files_unpacked = Self::unpack_files(from, destination_path)?;
let files_unpacked = Self::unpack_files(from, destination_path, flags)?;
Ok(DecompressionResult::FilesUnpacked(files_unpacked))
}

View File

@ -6,7 +6,7 @@ use std::{
use colored::Colorize;
use super::decompressor::{DecompressionResult, Decompressor};
use crate::utils;
use crate::{cli::Flags, utils};
// use niffler;
use crate::{extension::CompressionFormat, file::File};
@ -62,19 +62,19 @@ impl DecompressorToMemory {
}
impl Decompressor for GzipDecompressor {
fn decompress(&self, from: File, into: &Option<File>) -> crate::Result<DecompressionResult> {
fn decompress(&self, from: File, into: &Option<File>, _: Flags) -> crate::Result<DecompressionResult> {
DecompressorToMemory::decompress(from, CompressionFormat::Gzip, into)
}
}
impl Decompressor for BzipDecompressor {
fn decompress(&self, from: File, into: &Option<File>) -> crate::Result<DecompressionResult> {
fn decompress(&self, from: File, into: &Option<File>, _: Flags) -> crate::Result<DecompressionResult> {
DecompressorToMemory::decompress(from, CompressionFormat::Bzip, into)
}
}
impl Decompressor for LzmaDecompressor {
fn decompress(&self, from: File, into: &Option<File>) -> crate::Result<DecompressionResult> {
fn decompress(&self, from: File, into: &Option<File>, _: Flags) -> crate::Result<DecompressionResult> {
DecompressorToMemory::decompress(from, CompressionFormat::Lzma, into)
}
}

View File

@ -8,7 +8,7 @@ use colored::Colorize;
use zip::{self, read::ZipFile, ZipArchive};
use super::decompressor::{DecompressionResult, Decompressor};
use crate::{dialogs::Confirmation, file::File, utils};
use crate::{cli::Flags, dialogs::Confirmation, file::File, utils};
#[cfg(unix)]
fn __unix_set_permissions(file_path: &PathBuf, file: &ZipFile) {
@ -37,6 +37,7 @@ impl ZipDecompressor {
pub fn zip_decompress<T>(
archive: &mut ZipArchive<T>,
into: &Path,
flags: Flags,
) -> crate::Result<Vec<PathBuf>>
where
T: Read + Seek,
@ -52,8 +53,7 @@ impl ZipDecompressor {
let file_path = into.join(file_path);
if file_path.exists() {
let file_path_str = &*file_path.as_path().to_string_lossy();
if !confirm.ask(Some(file_path_str))? {
if !utils::permission_for_overwriting(&file_path, flags, &confirm)? {
// The user does not want to overwrite the file
continue;
}
@ -94,35 +94,42 @@ impl ZipDecompressor {
Ok(unpacked_files)
}
fn unpack_files(from: File, into: &Path) -> crate::Result<Vec<PathBuf>> {
fn unpack_files(from: File, into: &Path, flags: Flags) -> crate::Result<Vec<PathBuf>> {
println!(
"{}: attempting to decompress {:?}",
"ouch".bright_blue(),
"{} decompressing {:?}",
"[OUCH]".bright_blue(),
&from.path
);
match from.contents_in_memory {
Some(bytes) => {
// Decompressing a .zip archive loaded up in memory
let mut archive = zip::ZipArchive::new(Cursor::new(bytes))?;
Ok(Self::zip_decompress(&mut archive, into)?)
Ok(Self::zip_decompress(&mut archive, into, flags)?)
}
None => {
// Decompressing a .zip archive from the file system
let file = fs::File::open(&from.path)?;
let mut archive = zip::ZipArchive::new(file)?;
Ok(Self::zip_decompress(&mut archive, into)?)
Ok(Self::zip_decompress(&mut archive, into, flags)?)
}
}
}
}
impl Decompressor for ZipDecompressor {
fn decompress(&self, from: File, into: &Option<File>) -> crate::Result<DecompressionResult> {
fn decompress(
&self,
from: File,
into: &Option<File>,
flags: Flags,
) -> crate::Result<DecompressionResult> {
let destination_path = utils::get_destination_path(into);
utils::create_path_if_non_existent(destination_path)?;
let files_unpacked = Self::unpack_files(from, destination_path)?;
let files_unpacked = Self::unpack_files(from, destination_path, flags)?;
Ok(DecompressionResult::FilesUnpacked(files_unpacked))
}

View File

@ -31,7 +31,7 @@ impl<'a> Confirmation<'a> {
let mut answer = String::new();
io::stdin().read_line(&mut answer)?;
let trimmed_answer = answer.trim().to_ascii_lowercase();
let trimmed_answer = answer.trim();
if trimmed_answer.is_empty() {
return Ok(true);

View File

@ -3,7 +3,7 @@ use std::{ffi::OsStr, fs, io::Write, path::PathBuf};
use colored::Colorize;
use crate::{
cli::{Command, CommandKind},
cli::{Command, CommandKind, Flags},
compressors::{
BzipCompressor, Compressor, Entry, GzipCompressor, LzmaCompressor, TarCompressor,
ZipCompressor,
@ -12,8 +12,8 @@ use crate::{
BzipDecompressor, DecompressionResult, Decompressor, GzipDecompressor, LzmaDecompressor,
TarDecompressor, ZipDecompressor,
},
extension::{CompressionFormat, Extension},
dialogs::Confirmation,
extension::{CompressionFormat, Extension},
file::File,
utils,
};
@ -98,13 +98,13 @@ impl Evaluator {
Ok((first_decompressor, second_decompressor))
}
// todo: move this folder into decompressors/ later on
fn decompress_file_in_memory(
bytes: Vec<u8>,
file_path: PathBuf,
decompressor: Option<Box<dyn Decompressor>>,
output_file: &Option<File>,
extension: Option<Extension>,
flags: Flags,
) -> crate::Result<()> {
let output_file_path = utils::get_destination_path(output_file);
@ -113,7 +113,7 @@ impl Evaluator {
.unwrap_or_else(|| output_file_path.as_os_str());
if filename == "." {
// I believe this is only possible when the supplied inout 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.
filename = OsStr::new("ouch-output");
}
@ -127,6 +127,14 @@ impl Evaluator {
// Therefore, we'll save what we have in memory into a file.
println!("{}: saving to {:?}.", "info".yellow(), filename);
if filename.exists() {
let confirm =
Confirmation::new("Do you want to overwrite 'FILE'?", Some("FILE"));
if !utils::permission_for_overwriting(&filename, flags, &confirm)? {
return Ok(());
}
}
let mut f = fs::File::create(output_file_path.join(filename))?;
f.write_all(&bytes)?;
return Ok(());
@ -139,7 +147,7 @@ impl Evaluator {
extension,
};
let decompression_result = decompressor.decompress(file, output_file)?;
let decompression_result = decompressor.decompress(file, output_file, flags)?;
if let DecompressionResult::FileInMemory(_) = decompression_result {
// Should not be reachable.
unreachable!();
@ -148,20 +156,19 @@ impl Evaluator {
Ok(())
}
fn compress_files(files: Vec<PathBuf>, mut output: File) -> crate::Result<()> {
fn compress_files(files: Vec<PathBuf>, mut output: File, flags: Flags) -> crate::Result<()> {
let confirm = Confirmation::new("Do you want to overwrite 'FILE'?", Some("FILE"));
let (first_compressor, second_compressor) = Self::get_compressor(&output)?;
// TODO: use -y and -n here
let output_path = output.path.clone();
if output_path.exists() {
let output_path_str = &*output_path.to_string_lossy();
if !confirm.ask(Some(output_path_str))? {
if !utils::permission_for_overwriting(&output_path, flags, &confirm)? {
// The user does not want to overwrite the file
return Ok(());
}
}
let bytes = match first_compressor {
Some(first_compressor) => {
let mut entry = Entry::Files(files);
@ -188,14 +195,13 @@ impl Evaluator {
Ok(())
}
fn decompress_file(file: File, output: &Option<File>) -> crate::Result<()> {
// let output_file = &command.output;
fn decompress_file(file: File, output: &Option<File>, flags: Flags) -> crate::Result<()> {
let (first_decompressor, second_decompressor) = Self::get_decompressor(&file)?;
let file_path = file.path.clone();
let extension = file.extension.clone();
let decompression_result = second_decompressor.decompress(file, output)?;
let decompression_result = second_decompressor.decompress(file, output, flags)?;
match decompression_result {
DecompressionResult::FileInMemory(bytes) => {
@ -207,6 +213,7 @@ impl Evaluator {
first_decompressor,
output,
extension,
flags,
)?;
}
DecompressionResult::FilesUnpacked(_files) => {
@ -223,18 +230,18 @@ impl Evaluator {
Ok(())
}
pub fn evaluate(command: Command) -> crate::Result<()> {
pub fn evaluate(command: Command, flags: Flags) -> crate::Result<()> {
let output = command.output.clone();
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)?;
Self::compress_files(files_to_compress, output, flags)?;
}
CommandKind::Decompression(files_to_decompress) => {
for file in files_to_decompress {
Self::decompress_file(file, &output)?;
Self::decompress_file(file, &output, flags)?;
}
}
}

View File

@ -9,24 +9,11 @@ mod file;
mod test;
mod utils;
use std::convert::TryFrom;
use error::{Error, Result};
use evaluator::Evaluator;
fn main() -> crate::Result<()> {
let matches = cli::get_matches();
let command = cli::Command::try_from(matches)?;
Evaluator::evaluate(command)
let (command, flags) = cli::parse_matches(matches)?;
Evaluator::evaluate(command, flags)
}
// fn main() -> crate::Result<()> {
// let dialog = dialogs::Confirmation::new("Do you want to overwrite 'FILE'?", Some("FILE"));
// match dialog.ask(Some("file.tar.gz"))? {
// true => println!("deleting"),
// false => println!("keeping")
// };
// Ok(())
// }

View File

@ -5,7 +5,7 @@ use std::{
use colored::Colorize;
use crate::{extension::CompressionFormat, file::File};
use crate::{cli::Flags, dialogs::Confirmation, extension::CompressionFormat, file::File};
pub(crate) fn ensure_exists<'a, P>(path: P) -> crate::Result<()>
where
@ -90,3 +90,14 @@ pub(crate) fn change_dir_and_return_parent(filename: &PathBuf) -> crate::Result<
env::set_current_dir(parent)?;
Ok(previous_location)
}
pub fn permission_for_overwriting(path: &PathBuf, flags: Flags, confirm: &Confirmation) -> crate::Result<bool> {
match flags {
Flags::AlwaysYes => return Ok(true),
Flags::AlwaysNo => return Ok(false),
Flags::None => {}
}
let file_path_str = &*path.as_path().to_string_lossy();
Ok(confirm.ask(Some(file_path_str))?)
}