diff --git a/src/archive/tar.rs b/src/archive/tar.rs index 96f93e6..bb52d7e 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 f15e650..87d1a17 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 bd9a8de..256424b 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -7,23 +7,24 @@ use std::{ use clap::Parser; +pub use crate::utils::QuestionPolicy; use crate::{Error, Opts, 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 b4e6fab..aa538a9 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -20,7 +20,7 @@ use crate::{ info, utils::nice_directory_display, utils::to_utf, - utils::{self, dir_is_empty}, + utils::{self, dir_is_empty, QuestionPolicy}, Error, Opts, Subcommand, }; @@ -37,7 +37,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] @@ -93,7 +93,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(()); } @@ -175,7 +175,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)?; } } } @@ -288,7 +288,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)?; @@ -310,7 +310,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(()); } @@ -348,27 +348,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 => { @@ -383,7 +383,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 4e69ab0..7c3a0e7 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 71e5d13..d1344e9 100644 --- a/tests/compress_and_decompress.rs +++ b/tests/compress_and_decompress.rs @@ -7,7 +7,7 @@ use std::{ time::Duration, }; -use ouch::{commands::run, Opts, Subcommand}; +use ouch::{cli::QuestionPolicy, commands::run, Opts, Subcommand}; use rand::{rngs::SmallRng, RngCore, SeedableRng}; use tempfile::NamedTempFile; use utils::*; @@ -180,7 +180,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 ad3bdf4..b51de5d 100644 --- a/tests/utils.rs +++ b/tests/utils.rs @@ -7,7 +7,7 @@ use std::{ path::{Path, PathBuf}, }; -use ouch::{commands::run, Opts, Subcommand}; +use ouch::{cli::QuestionPolicy, commands::run, Opts, Subcommand}; pub fn create_empty_dir(at: &Path, filename: &str) -> PathBuf { let dirname = Path::new(filename); @@ -27,7 +27,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 } @@ -52,7 +52,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() }