diff --git a/src/archive/tar.rs b/src/archive/tar.rs index 96f93e691..bb52d7e2a 100644 --- a/src/archive/tar.rs +++ b/src/archive/tar.rs @@ -11,13 +11,13 @@ use walkdir::WalkDir; use crate::{ info, - utils::{self, Bytes}, + utils::{self, Bytes, QuestionPolicy}, }; pub fn unpack_archive( reader: Box, output_folder: &Path, - skip_questions_positively: Option, + question_policy: QuestionPolicy, ) -> crate::Result> { let mut archive = tar::Archive::new(reader); @@ -26,7 +26,7 @@ pub fn unpack_archive( let mut file = file?; let file_path = output_folder.join(file.path()?); - if file_path.exists() && !utils::user_wants_to_overwrite(&file_path, skip_questions_positively)? { + if file_path.exists() && !utils::user_wants_to_overwrite(&file_path, question_policy)? { continue; } diff --git a/src/archive/zip.rs b/src/archive/zip.rs index db357274e..87d1a178a 100644 --- a/src/archive/zip.rs +++ b/src/archive/zip.rs @@ -11,7 +11,7 @@ use zip::{self, read::ZipFile, ZipArchive}; use crate::{ info, - utils::{self, dir_is_empty,strip_cur_dir, Bytes}, + utils::{self, dir_is_empty, strip_cur_dir, Bytes, QuestionPolicy}, }; use self::utf8::get_invalid_utf8_paths; @@ -20,7 +20,7 @@ use self::utf8::get_invalid_utf8_paths; pub fn unpack_archive( mut archive: ZipArchive, into: &Path, - skip_questions_positively: Option, + question_policy: QuestionPolicy, ) -> crate::Result> where R: Read + Seek, @@ -34,7 +34,7 @@ where }; let file_path = into.join(file_path); - if file_path.exists() && !utils::user_wants_to_overwrite(&file_path, skip_questions_positively)? { + if file_path.exists() && !utils::user_wants_to_overwrite(&file_path, question_policy)? { continue; } diff --git a/src/cli.rs b/src/cli.rs index 7a13aab3f..1ec281b71 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -7,6 +7,7 @@ use std::{ use clap::{Parser, ValueHint}; +pub use crate::utils::QuestionPolicy; use crate::Error; #[derive(Parser, Debug)] @@ -53,18 +54,18 @@ pub enum Subcommand { impl Opts { /// A helper method that calls `clap::Parser::parse` and then translates relative paths to absolute. /// Also determines if the user wants to skip questions or not - pub fn parse_args() -> crate::Result<(Self, Option)> { + pub fn parse_args() -> crate::Result<(Self, QuestionPolicy)> { let mut opts: Self = Self::parse(); let (Subcommand::Compress { files, .. } | Subcommand::Decompress { files, .. }) = &mut opts.cmd; *files = canonicalize_files(files)?; let skip_questions_positively = if opts.yes { - Some(true) + QuestionPolicy::AlwaysYes } else if opts.no { - Some(false) + QuestionPolicy::AlwaysNo } else { - None + QuestionPolicy::Ask }; Ok((opts, skip_questions_positively)) diff --git a/src/commands.rs b/src/commands.rs index 6d4134752..2543c6770 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -21,7 +21,7 @@ use crate::{ info, utils::nice_directory_display, utils::to_utf, - utils::{self, dir_is_empty}, + utils::{self, dir_is_empty, QuestionPolicy}, Error, }; @@ -38,7 +38,7 @@ fn represents_several_files(files: &[PathBuf]) -> bool { files.iter().any(is_non_empty_dir) || files.len() > 1 } -pub fn run(args: Opts, skip_questions_positively: Option) -> crate::Result<()> { +pub fn run(args: Opts, question_policy: QuestionPolicy) -> crate::Result<()> { match args.cmd { Subcommand::Compress { files, output: output_path } => { // Formats from path extension, like "file.tar.gz.xz" -> vec![Tar, Gzip, Lzma] @@ -94,7 +94,7 @@ pub fn run(args: Opts, skip_questions_positively: Option) -> crate::Result return Err(Error::with_reason(reason)); } - if output_path.exists() && !utils::user_wants_to_overwrite(&output_path, skip_questions_positively)? { + if output_path.exists() && !utils::user_wants_to_overwrite(&output_path, question_policy)? { // User does not want to overwrite this file return Ok(()); } @@ -176,7 +176,7 @@ pub fn run(args: Opts, skip_questions_positively: Option) -> crate::Result let output_folder = output_folder.as_ref().map(|path| path.as_ref()); for ((input_path, formats), file_name) in files.iter().zip(formats).zip(output_paths) { - decompress_file(input_path, formats, output_folder, file_name, skip_questions_positively)?; + decompress_file(input_path, formats, output_folder, file_name, question_policy)?; } } } @@ -289,7 +289,7 @@ fn decompress_file( formats: Vec, output_folder: Option<&Path>, file_name: &Path, - skip_questions_positively: Option, + question_policy: QuestionPolicy, ) -> crate::Result<()> { // TODO: improve error message let reader = fs::File::open(&input_file_path)?; @@ -311,7 +311,7 @@ fn decompress_file( if let [Zip] = *formats.as_slice() { utils::create_dir_if_non_existent(output_folder)?; let zip_archive = zip::ZipArchive::new(reader)?; - let _files = crate::archive::zip::unpack_archive(zip_archive, output_folder, skip_questions_positively)?; + let _files = crate::archive::zip::unpack_archive(zip_archive, output_folder, question_policy)?; info!("Successfully decompressed archive in {}.", nice_directory_display(output_folder)); return Ok(()); } @@ -349,27 +349,27 @@ fn decompress_file( info!("Successfully decompressed archive in {}.", nice_directory_display(output_path)); } Tar => { - let _ = crate::archive::tar::unpack_archive(reader, output_folder, skip_questions_positively)?; + let _ = crate::archive::tar::unpack_archive(reader, output_folder, question_policy)?; info!("Successfully decompressed archive in {}.", nice_directory_display(output_folder)); } Tgz => { let reader = chain_reader_decoder(&Gzip, reader)?; - let _ = crate::archive::tar::unpack_archive(reader, output_folder, skip_questions_positively)?; + let _ = crate::archive::tar::unpack_archive(reader, output_folder, question_policy)?; info!("Successfully decompressed archive in {}.", nice_directory_display(output_folder)); } Tbz => { let reader = chain_reader_decoder(&Bzip, reader)?; - let _ = crate::archive::tar::unpack_archive(reader, output_folder, skip_questions_positively)?; + let _ = crate::archive::tar::unpack_archive(reader, output_folder, question_policy)?; info!("Successfully decompressed archive in {}.", nice_directory_display(output_folder)); } Tlzma => { let reader = chain_reader_decoder(&Lzma, reader)?; - let _ = crate::archive::tar::unpack_archive(reader, output_folder, skip_questions_positively)?; + let _ = crate::archive::tar::unpack_archive(reader, output_folder, question_policy)?; info!("Successfully decompressed archive in {}.", nice_directory_display(output_folder)); } Tzst => { let reader = chain_reader_decoder(&Zstd, reader)?; - let _ = crate::archive::tar::unpack_archive(reader, output_folder, skip_questions_positively)?; + let _ = crate::archive::tar::unpack_archive(reader, output_folder, question_policy)?; info!("Successfully decompressed archive in {}.", nice_directory_display(output_folder)); } Zip => { @@ -384,7 +384,7 @@ fn decompress_file( io::copy(&mut reader, &mut vec)?; let zip_archive = zip::ZipArchive::new(io::Cursor::new(vec))?; - let _ = crate::archive::zip::unpack_archive(zip_archive, output_folder, skip_questions_positively)?; + let _ = crate::archive::zip::unpack_archive(zip_archive, output_folder, question_policy)?; info!("Successfully decompressed archive in {}.", nice_directory_display(output_folder)); } diff --git a/src/utils.rs b/src/utils.rs index 4e69ab0ba..7c3a0e725 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -43,11 +43,11 @@ pub fn cd_into_same_dir_as(filename: &Path) -> crate::Result { Ok(previous_location) } -pub fn user_wants_to_overwrite(path: &Path, skip_questions_positively: Option) -> crate::Result { - match skip_questions_positively { - Some(true) => Ok(true), - Some(false) => Ok(false), - None => { +pub fn user_wants_to_overwrite(path: &Path, question_policy: QuestionPolicy) -> crate::Result { + match question_policy { + QuestionPolicy::AlwaysYes => Ok(true), + QuestionPolicy::AlwaysNo => Ok(false), + QuestionPolicy::Ask => { let path = to_utf(strip_cur_dir(path)); let path = Some(path.as_str()); let placeholder = Some("FILE"); @@ -126,6 +126,17 @@ impl std::fmt::Display for Bytes { } } +#[derive(Debug, PartialEq, Clone, Copy)] +/// How overwrite questions should be handled +pub enum QuestionPolicy { + /// Ask ever time + Ask, + /// Skip overwrite questions positively + AlwaysYes, + /// Skip overwrite questions negatively + AlwaysNo, +} + #[cfg(test)] mod tests { use super::*; diff --git a/tests/compress_and_decompress.rs b/tests/compress_and_decompress.rs index 77d929d4c..45518d09c 100644 --- a/tests/compress_and_decompress.rs +++ b/tests/compress_and_decompress.rs @@ -8,7 +8,7 @@ use std::{ }; use ouch::{ - cli::{Opts, Subcommand}, + cli::{Opts, QuestionPolicy, Subcommand}, commands::run, }; use rand::{rngs::SmallRng, RngCore, SeedableRng}; @@ -183,7 +183,7 @@ fn extract_files(archive_path: &Path) -> Vec { output: Some(extraction_output_folder.clone()), }, }; - run(command, None).expect("Failed to extract"); + run(command, QuestionPolicy::Ask).expect("Failed to extract"); fs::read_dir(extraction_output_folder).unwrap().map(Result::unwrap).map(|entry| entry.path()).collect() } diff --git a/tests/utils.rs b/tests/utils.rs index 10cc7490b..e55aac26c 100644 --- a/tests/utils.rs +++ b/tests/utils.rs @@ -8,7 +8,7 @@ use std::{ }; use ouch::{ - cli::{Opts, Subcommand}, + cli::{Opts, QuestionPolicy, Subcommand}, commands::run, }; @@ -30,7 +30,7 @@ pub fn compress_files(at: &Path, paths_to_compress: &[PathBuf], format: &str) -> no: false, cmd: Subcommand::Compress { files: paths_to_compress.to_vec(), output: archive_path.clone() }, }; - run(command, None).expect("Failed to compress test dummy files"); + run(command, QuestionPolicy::Ask).expect("Failed to compress test dummy files"); archive_path } @@ -55,7 +55,7 @@ pub fn extract_files(archive_path: &Path) -> Vec { output: Some(extraction_output_folder.clone()), }, }; - run(command, None).expect("Failed to extract"); + run(command, QuestionPolicy::Ask).expect("Failed to extract"); fs::read_dir(extraction_output_folder).unwrap().map(Result::unwrap).map(|entry| entry.path()).collect() }