mirror of
https://github.com/ouch-org/ouch.git
synced 2025-06-06 11:35:45 +00:00
Merge pull request #8 from vrmiguel/yes-and-no
Add `-y, --yes` and `-n, --no` flags
This commit is contained in:
commit
70d8e37cea
41
src/cli.rs
41
src/cli.rs
@ -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;
|
||||
|
||||
|
@ -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>;
|
||||
}
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
17
src/main.rs
17
src/main.rs
@ -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(())
|
||||
// }
|
||||
|
13
src/utils.rs
13
src/utils.rs
@ -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))?)
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user