From 7954eb07fdcd13a14416aa9e8dc3179e59429a08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Rodrigues=20Miguel?= Date: Sun, 28 Mar 2021 13:56:00 -0300 Subject: [PATCH 1/3] decompressors/zip: Add confirmation dialog for file overwriting --- src/decompressors/zip.rs | 47 ++++++++++++++++++++++++---------------- src/dialogs.rs | 47 ++++++++++++++++++++++++++++++++++++++++ src/error.rs | 1 + src/main.rs | 12 ++++++++++ 4 files changed, 88 insertions(+), 19 deletions(-) create mode 100644 src/dialogs.rs diff --git a/src/decompressors/zip.rs b/src/decompressors/zip.rs index 01f8a65..6e414d2 100644 --- a/src/decompressors/zip.rs +++ b/src/decompressors/zip.rs @@ -8,7 +8,7 @@ use colored::Colorize; use zip::{self, read::ZipFile, ZipArchive}; use super::decompressor::{DecompressionResult, Decompressor}; -use crate::{file::File, utils}; +use crate::{dialogs::Confirmation, file::File, utils}; #[cfg(unix)] fn __unix_set_permissions(file_path: &PathBuf, file: &ZipFile) { @@ -41,6 +41,7 @@ impl ZipDecompressor { where T: Read + Seek, { + let confirm = Confirmation::new("Do you want to overwrite 'FILE'?", Some("FILE")); let mut unpacked_files = vec![]; for idx in 0..archive.len() { let mut file = archive.by_index(idx)?; @@ -50,28 +51,36 @@ 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))? { + continue; + } + } Self::check_for_comments(&file); - let is_dir = (&*file.name()).ends_with('/'); - - if is_dir { - println!("File {} extracted to \"{}\"", idx, file_path.display()); - fs::create_dir_all(&file_path)?; - } else { - if let Some(p) = file_path.parent() { - if !p.exists() { - fs::create_dir_all(&p)?; - } + match (&*file.name()).ends_with('/') { + _is_dir @ true => { + println!("File {} extracted to \"{}\"", idx, file_path.display()); + fs::create_dir_all(&file_path)?; + } + _is_file @ false => { + if let Some(path) = file_path.parent() { + if !path.exists() { + fs::create_dir_all(&path)?; + } + } + println!( + "{}: \"{}\" extracted. ({} bytes)", + "info".yellow(), + file_path.display(), + file.size() + ); + + let mut output_file = fs::File::create(&file_path)?; + io::copy(&mut file, &mut output_file)?; } - println!( - "{}: \"{}\" extracted. ({} bytes)", - "info".yellow(), - file_path.display(), - file.size() - ); - let mut outfile = fs::File::create(&file_path)?; - io::copy(&mut file, &mut outfile)?; } #[cfg(unix)] diff --git a/src/dialogs.rs b/src/dialogs.rs new file mode 100644 index 0000000..b603b9a --- /dev/null +++ b/src/dialogs.rs @@ -0,0 +1,47 @@ +use std::io::{self, Write}; + +use colored::Colorize; + +pub struct Confirmation<'a> { + pub prompt: &'a str, + pub placeholder: Option<&'a str>, +} + +#[derive(Debug)] +pub struct Error; + +impl<'a> Confirmation<'a> { + pub fn new(prompt: &'a str, pattern: Option<&'a str>) -> Self { + Self { + prompt, + placeholder: pattern, + } + } + + pub fn ask(&self, substitute: Option<&'a str>) -> crate::Result { + let message = match (self.placeholder, substitute) { + (None, _) => self.prompt.into(), + (Some(_), None) => return Err(crate::Error::InternalError), + (Some(placeholder), Some(subs)) => self.prompt.replace(placeholder, subs), + }; + + loop { + print!("{} [{}/{}] ", message, "Y".bright_green(), "n".bright_red()); + io::stdout().flush()?; + + let mut answer = String::new(); + io::stdin().read_line(&mut answer)?; + let trimmed_answer = answer.trim().to_ascii_lowercase(); + + if trimmed_answer.is_empty() { + return Ok(true); + } + + match trimmed_answer.to_ascii_lowercase().as_ref() { + "y" | "yes" => return Ok(true), + "n" | "no" => return Ok(false), + _ => {} + } + } + } +} diff --git a/src/error.rs b/src/error.rs index 6680600..2b58a5c 100644 --- a/src/error.rs +++ b/src/error.rs @@ -16,6 +16,7 @@ pub enum Error { PermissionDenied, UnsupportedZipArchive(&'static str), InputsMustHaveBeenDecompressible(PathBuf), + InternalError, } pub type Result = std::result::Result; diff --git a/src/main.rs b/src/main.rs index 6902876..a13504f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,7 @@ mod cli; mod compressors; mod decompressors; +mod dialogs; mod error; mod evaluator; mod extension; @@ -18,3 +19,14 @@ fn main() -> crate::Result<()> { let command = cli::Command::try_from(matches)?; Evaluator::evaluate(command) } + +// 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(()) +// } From 03d6fc1e605920f870ebc4e3dc721d27163edd4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Rodrigues=20Miguel?= Date: Sun, 28 Mar 2021 14:28:07 -0300 Subject: [PATCH 2/3] decompressors/tar: Add confirmation dialog for file overwriting --- src/decompressors/tar.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/decompressors/tar.rs b/src/decompressors/tar.rs index a613b0c..c384ada 100644 --- a/src/decompressors/tar.rs +++ b/src/decompressors/tar.rs @@ -8,7 +8,7 @@ use colored::Colorize; use tar::{self, Archive}; use super::decompressor::{DecompressionResult, Decompressor}; -use crate::{file::File, utils}; +use crate::{file::File, utils, dialogs::Confirmation}; #[derive(Debug)] pub struct TarDecompressor {} @@ -21,6 +21,7 @@ impl TarDecompressor { &from.path ); let mut files_unpacked = vec![]; + let confirm = Confirmation::new("Do you want to overwrite 'FILE'?", Some("FILE")); let mut archive: Archive> = match from.contents_in_memory { Some(bytes) => tar::Archive::new(Box::new(Cursor::new(bytes))), @@ -32,8 +33,15 @@ impl TarDecompressor { for file in archive.entries()? { let mut file = file?; - - // TODO: check if file/folder already exists and ask user's permission for overwriting + + 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))? { + continue; + } + } + file.unpack_in(into)?; println!( From 40fb926d8081cecb9c856e39ee880086eb91887d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Rodrigues=20Miguel?= Date: Sun, 28 Mar 2021 14:52:09 -0300 Subject: [PATCH 3/3] evaluator: Add confirmation dialog for file overwriting --- src/decompressors/tar.rs | 5 +++-- src/decompressors/zip.rs | 3 ++- src/evaluator.rs | 10 ++++++++++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/decompressors/tar.rs b/src/decompressors/tar.rs index c384ada..13ef979 100644 --- a/src/decompressors/tar.rs +++ b/src/decompressors/tar.rs @@ -37,7 +37,8 @@ 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 !confirm.ask(Some(file_path_str))? { + // The user does not want to overwrite the file continue; } } @@ -51,7 +52,7 @@ impl TarDecompressor { file.size() ); - let file_path = fs::canonicalize(into.join(file.path()?))?; + let file_path = fs::canonicalize(file_path)?; files_unpacked.push(file_path); } diff --git a/src/decompressors/zip.rs b/src/decompressors/zip.rs index 6e414d2..40e8441 100644 --- a/src/decompressors/zip.rs +++ b/src/decompressors/zip.rs @@ -53,7 +53,8 @@ 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 !confirm.ask(Some(file_path_str))? { + // The user does not want to overwrite the file continue; } } diff --git a/src/evaluator.rs b/src/evaluator.rs index 8e923b1..d913bea 100644 --- a/src/evaluator.rs +++ b/src/evaluator.rs @@ -13,6 +13,7 @@ use crate::{ TarDecompressor, ZipDecompressor, }, extension::{CompressionFormat, Extension}, + dialogs::Confirmation, file::File, utils, }; @@ -148,9 +149,18 @@ impl Evaluator { } fn compress_files(files: Vec, mut output: File) -> crate::Result<()> { + let confirm = Confirmation::new("Do you want to overwrite 'FILE'?", Some("FILE")); let (first_compressor, second_compressor) = Self::get_compressor(&output)?; 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))? { + // The user does not want to overwrite the file + return Ok(()); + } + } + let bytes = match first_compressor { Some(first_compressor) => {