Introduce new type for policy on how to handle questions

This commit is contained in:
Anton Hermann 2021-10-21 23:16:01 +02:00
parent a46fa1fb38
commit 10f7462b8b
7 changed files with 44 additions and 32 deletions

View File

@ -11,13 +11,13 @@ use walkdir::WalkDir;
use crate::{ use crate::{
info, info,
utils::{self, Bytes}, utils::{self, Bytes, QuestionPolicy},
}; };
pub fn unpack_archive( pub fn unpack_archive(
reader: Box<dyn Read>, reader: Box<dyn Read>,
output_folder: &Path, output_folder: &Path,
skip_questions_positively: Option<bool>, question_policy: QuestionPolicy,
) -> crate::Result<Vec<PathBuf>> { ) -> crate::Result<Vec<PathBuf>> {
let mut archive = tar::Archive::new(reader); let mut archive = tar::Archive::new(reader);
@ -26,7 +26,7 @@ pub fn unpack_archive(
let mut file = file?; let mut file = file?;
let file_path = output_folder.join(file.path()?); 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; continue;
} }

View File

@ -11,7 +11,7 @@ use zip::{self, read::ZipFile, ZipArchive};
use crate::{ use crate::{
info, 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; use self::utf8::get_invalid_utf8_paths;
@ -20,7 +20,7 @@ use self::utf8::get_invalid_utf8_paths;
pub fn unpack_archive<R>( pub fn unpack_archive<R>(
mut archive: ZipArchive<R>, mut archive: ZipArchive<R>,
into: &Path, into: &Path,
skip_questions_positively: Option<bool>, question_policy: QuestionPolicy,
) -> crate::Result<Vec<PathBuf>> ) -> crate::Result<Vec<PathBuf>>
where where
R: Read + Seek, R: Read + Seek,
@ -34,7 +34,7 @@ where
}; };
let file_path = into.join(file_path); 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; continue;
} }

View File

@ -7,6 +7,7 @@ use std::{
use clap::{Parser, ValueHint}; use clap::{Parser, ValueHint};
pub use crate::utils::QuestionPolicy;
use crate::Error; use crate::Error;
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
@ -53,18 +54,18 @@ pub enum Subcommand {
impl Opts { impl Opts {
/// A helper method that calls `clap::Parser::parse` and then translates relative paths to absolute. /// 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 /// Also determines if the user wants to skip questions or not
pub fn parse_args() -> crate::Result<(Self, Option<bool>)> { pub fn parse_args() -> crate::Result<(Self, QuestionPolicy)> {
let mut opts: Self = Self::parse(); let mut opts: Self = Self::parse();
let (Subcommand::Compress { files, .. } | Subcommand::Decompress { files, .. }) = &mut opts.cmd; let (Subcommand::Compress { files, .. } | Subcommand::Decompress { files, .. }) = &mut opts.cmd;
*files = canonicalize_files(files)?; *files = canonicalize_files(files)?;
let skip_questions_positively = if opts.yes { let skip_questions_positively = if opts.yes {
Some(true) QuestionPolicy::AlwaysYes
} else if opts.no { } else if opts.no {
Some(false) QuestionPolicy::AlwaysNo
} else { } else {
None QuestionPolicy::Ask
}; };
Ok((opts, skip_questions_positively)) Ok((opts, skip_questions_positively))

View File

@ -21,7 +21,7 @@ use crate::{
info, info,
utils::nice_directory_display, utils::nice_directory_display,
utils::to_utf, utils::to_utf,
utils::{self, dir_is_empty}, utils::{self, dir_is_empty, QuestionPolicy},
Error, Error,
}; };
@ -38,7 +38,7 @@ fn represents_several_files(files: &[PathBuf]) -> bool {
files.iter().any(is_non_empty_dir) || files.len() > 1 files.iter().any(is_non_empty_dir) || files.len() > 1
} }
pub fn run(args: Opts, skip_questions_positively: Option<bool>) -> crate::Result<()> { pub fn run(args: Opts, question_policy: QuestionPolicy) -> crate::Result<()> {
match args.cmd { match args.cmd {
Subcommand::Compress { files, output: output_path } => { Subcommand::Compress { files, output: output_path } => {
// Formats from path extension, like "file.tar.gz.xz" -> vec![Tar, Gzip, Lzma] // 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<bool>) -> crate::Result
return Err(Error::with_reason(reason)); 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 // User does not want to overwrite this file
return Ok(()); return Ok(());
} }
@ -176,7 +176,7 @@ pub fn run(args: Opts, skip_questions_positively: Option<bool>) -> crate::Result
let output_folder = output_folder.as_ref().map(|path| path.as_ref()); 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) { 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<extension::CompressionFormat>, formats: Vec<extension::CompressionFormat>,
output_folder: Option<&Path>, output_folder: Option<&Path>,
file_name: &Path, file_name: &Path,
skip_questions_positively: Option<bool>, question_policy: QuestionPolicy,
) -> crate::Result<()> { ) -> crate::Result<()> {
// TODO: improve error message // TODO: improve error message
let reader = fs::File::open(&input_file_path)?; let reader = fs::File::open(&input_file_path)?;
@ -311,7 +311,7 @@ fn decompress_file(
if let [Zip] = *formats.as_slice() { if let [Zip] = *formats.as_slice() {
utils::create_dir_if_non_existent(output_folder)?; utils::create_dir_if_non_existent(output_folder)?;
let zip_archive = zip::ZipArchive::new(reader)?; 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)); info!("Successfully decompressed archive in {}.", nice_directory_display(output_folder));
return Ok(()); return Ok(());
} }
@ -349,27 +349,27 @@ fn decompress_file(
info!("Successfully decompressed archive in {}.", nice_directory_display(output_path)); info!("Successfully decompressed archive in {}.", nice_directory_display(output_path));
} }
Tar => { 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)); info!("Successfully decompressed archive in {}.", nice_directory_display(output_folder));
} }
Tgz => { Tgz => {
let reader = chain_reader_decoder(&Gzip, reader)?; 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)); info!("Successfully decompressed archive in {}.", nice_directory_display(output_folder));
} }
Tbz => { Tbz => {
let reader = chain_reader_decoder(&Bzip, reader)?; 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)); info!("Successfully decompressed archive in {}.", nice_directory_display(output_folder));
} }
Tlzma => { Tlzma => {
let reader = chain_reader_decoder(&Lzma, reader)?; 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)); info!("Successfully decompressed archive in {}.", nice_directory_display(output_folder));
} }
Tzst => { Tzst => {
let reader = chain_reader_decoder(&Zstd, reader)?; 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)); info!("Successfully decompressed archive in {}.", nice_directory_display(output_folder));
} }
Zip => { Zip => {
@ -384,7 +384,7 @@ fn decompress_file(
io::copy(&mut reader, &mut vec)?; io::copy(&mut reader, &mut vec)?;
let zip_archive = zip::ZipArchive::new(io::Cursor::new(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)); info!("Successfully decompressed archive in {}.", nice_directory_display(output_folder));
} }

View File

@ -43,11 +43,11 @@ pub fn cd_into_same_dir_as(filename: &Path) -> crate::Result<PathBuf> {
Ok(previous_location) Ok(previous_location)
} }
pub fn user_wants_to_overwrite(path: &Path, skip_questions_positively: Option<bool>) -> crate::Result<bool> { pub fn user_wants_to_overwrite(path: &Path, question_policy: QuestionPolicy) -> crate::Result<bool> {
match skip_questions_positively { match question_policy {
Some(true) => Ok(true), QuestionPolicy::AlwaysYes => Ok(true),
Some(false) => Ok(false), QuestionPolicy::AlwaysNo => Ok(false),
None => { QuestionPolicy::Ask => {
let path = to_utf(strip_cur_dir(path)); let path = to_utf(strip_cur_dir(path));
let path = Some(path.as_str()); let path = Some(path.as_str());
let placeholder = Some("FILE"); 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)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View File

@ -8,7 +8,7 @@ use std::{
}; };
use ouch::{ use ouch::{
cli::{Opts, Subcommand}, cli::{Opts, QuestionPolicy, Subcommand},
commands::run, commands::run,
}; };
use rand::{rngs::SmallRng, RngCore, SeedableRng}; use rand::{rngs::SmallRng, RngCore, SeedableRng};
@ -183,7 +183,7 @@ fn extract_files(archive_path: &Path) -> Vec<PathBuf> {
output: Some(extraction_output_folder.clone()), 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() fs::read_dir(extraction_output_folder).unwrap().map(Result::unwrap).map(|entry| entry.path()).collect()
} }

View File

@ -8,7 +8,7 @@ use std::{
}; };
use ouch::{ use ouch::{
cli::{Opts, Subcommand}, cli::{Opts, QuestionPolicy, Subcommand},
commands::run, commands::run,
}; };
@ -30,7 +30,7 @@ pub fn compress_files(at: &Path, paths_to_compress: &[PathBuf], format: &str) ->
no: false, no: false,
cmd: Subcommand::Compress { files: paths_to_compress.to_vec(), output: archive_path.clone() }, 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 archive_path
} }
@ -55,7 +55,7 @@ pub fn extract_files(archive_path: &Path) -> Vec<PathBuf> {
output: Some(extraction_output_folder.clone()), 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() fs::read_dir(extraction_output_folder).unwrap().map(Result::unwrap).map(|entry| entry.path()).collect()
} }