intro merge action in decompression

Signed-off-by: tommady <tommady@users.noreply.github.com>
This commit is contained in:
tommady 2025-04-18 11:58:00 +00:00
parent 267ce7672e
commit dd81c0c449
No known key found for this signature in database
GPG Key ID: 175B664929DF2F2F
7 changed files with 58 additions and 30 deletions

View File

@ -18,8 +18,6 @@ pub fn unpack_archive(
password: Option<&[u8]>,
quiet: bool,
) -> crate::Result<usize> {
assert!(output_folder.read_dir().expect("dir exists").next().is_none());
let archive = match password {
Some(password) => Archive::with_password(archive_path, password),
None => Archive::new(archive_path),

View File

@ -24,7 +24,6 @@ use crate::{
/// Unpacks the archive given by `archive` into the folder given by `into`.
/// Assumes that output_folder is empty
pub fn unpack_archive(reader: Box<dyn Read>, output_folder: &Path, quiet: bool) -> crate::Result<usize> {
assert!(output_folder.read_dir().expect("dir exists").next().is_none());
let mut archive = tar::Archive::new(reader);
let mut files_unpacked = 0;

View File

@ -37,8 +37,6 @@ pub fn unpack_archive<R>(
where
R: Read + Seek,
{
assert!(output_folder.read_dir().expect("dir exists").next().is_none());
let mut unpacked_files = 0;
for idx in 0..archive.len() {

View File

@ -137,7 +137,11 @@ pub fn decompress_file(options: DecompressOptions) -> crate::Result<()> {
Gzip | Bzip | Bzip3 | Lz4 | Lzma | Snappy | Zstd | Brotli => {
reader = chain_reader_decoder(&first_extension, reader)?;
let mut writer = match utils::ask_to_create_file(&options.output_file_path, options.question_policy)? {
let mut writer = match utils::ask_to_create_file(
&options.output_file_path,
options.question_policy,
QuestionAction::Decompression,
)? {
Some(file) => file,
None => return Ok(()),
};
@ -318,7 +322,7 @@ fn unpack(
let output_dir_cleaned = if is_valid_output_dir {
output_dir.to_owned()
} else {
match utils::resolve_path_conflict(output_dir, question_policy)? {
match utils::resolve_path_conflict(output_dir, question_policy, QuestionAction::Decompression)? {
Some(path) => path,
None => return Ok(ControlFlow::Break(())),
}
@ -374,7 +378,7 @@ fn smart_unpack(
// Before moving, need to check if a file with the same name already exists
// If it does, need to ask the user what to do
new_path = match utils::resolve_path_conflict(&new_path, question_policy)? {
new_path = match utils::resolve_path_conflict(&new_path, question_policy, QuestionAction::Decompression)? {
Some(path) => path,
None => return Ok(ControlFlow::Break(())),
};

View File

@ -20,6 +20,7 @@ use crate::{
list::ListOptions,
utils::{
self, colors::*, is_path_stdin, logger::info_accessible, path_to_str, EscapedPathDisplay, FileVisibilityPolicy,
QuestionAction,
},
CliArgs, QuestionPolicy,
};
@ -91,10 +92,11 @@ pub fn run(
)?;
check::check_archive_formats_position(&formats, &output_path)?;
let output_file = match utils::ask_to_create_file(&output_path, question_policy)? {
Some(writer) => writer,
None => return Ok(()),
};
let output_file =
match utils::ask_to_create_file(&output_path, question_policy, QuestionAction::Compression)? {
Some(writer) => writer,
None => return Ok(()),
};
let level = if fast {
Some(1) // Lowest level of compression

View File

@ -11,7 +11,7 @@ use fs_err as fs;
use super::{question::FileConflitOperation, user_wants_to_overwrite};
use crate::{
extension::Extension,
utils::{logger::info_accessible, EscapedPathDisplay},
utils::{logger::info_accessible, EscapedPathDisplay, QuestionAction},
QuestionPolicy,
};
@ -26,9 +26,13 @@ pub fn is_path_stdin(path: &Path) -> bool {
/// * `Ok(None)` means the user wants to cancel the operation
/// * `Ok(Some(path))` returns a valid PathBuf without any another file or directory with the same name
/// * `Err(_)` is an error
pub fn resolve_path_conflict(path: &Path, question_policy: QuestionPolicy) -> crate::Result<Option<PathBuf>> {
pub fn resolve_path_conflict(
path: &Path,
question_policy: QuestionPolicy,
question_action: QuestionAction,
) -> crate::Result<Option<PathBuf>> {
if path.exists() {
match user_wants_to_overwrite(path, question_policy)? {
match user_wants_to_overwrite(path, question_policy, question_action)? {
FileConflitOperation::Cancel => Ok(None),
FileConflitOperation::Overwrite => {
remove_file_or_dir(path)?;
@ -38,6 +42,7 @@ pub fn resolve_path_conflict(path: &Path, question_policy: QuestionPolicy) -> cr
let renamed_path = rename_for_available_filename(path);
Ok(Some(renamed_path))
}
FileConflitOperation::Merge => Ok(Some(path.to_path_buf())),
}
} else {
Ok(Some(path.to_path_buf()))

View File

@ -48,49 +48,71 @@ pub enum FileConflitOperation {
/// Rename the file
/// It'll be put "_1" at the end of the filename or "_2","_3","_4".. if already exists
Rename,
/// Merge duplicated files
Merge,
}
/// Check if QuestionPolicy flags were set, otherwise, ask user if they want to overwrite.
pub fn user_wants_to_overwrite(path: &Path, question_policy: QuestionPolicy) -> crate::Result<FileConflitOperation> {
pub fn user_wants_to_overwrite(
path: &Path,
question_policy: QuestionPolicy,
question_action: QuestionAction,
) -> crate::Result<FileConflitOperation> {
use FileConflitOperation as Op;
match question_policy {
QuestionPolicy::AlwaysYes => Ok(Op::Overwrite),
QuestionPolicy::AlwaysNo => Ok(Op::Cancel),
QuestionPolicy::Ask => ask_file_conflict_operation(path),
QuestionPolicy::Ask => ask_file_conflict_operation(path, question_action),
}
}
/// Ask the user if they want to overwrite or rename the &Path
pub fn ask_file_conflict_operation(path: &Path) -> Result<FileConflitOperation> {
pub fn ask_file_conflict_operation(path: &Path, question_action: QuestionAction) -> Result<FileConflitOperation> {
use FileConflitOperation as Op;
let path = path_to_str(strip_cur_dir(path));
ChoicePrompt::new(
format!("Do you want to overwrite {path}?"),
[
("yes", Op::Overwrite, *colors::GREEN),
("no", Op::Cancel, *colors::RED),
("rename", Op::Rename, *colors::BLUE),
],
)
.ask()
match question_action {
QuestionAction::Compression => ChoicePrompt::new(
format!("Do you want to overwrite {path}?"),
[
("yes", Op::Overwrite, *colors::GREEN),
("no", Op::Cancel, *colors::RED),
("rename", Op::Rename, *colors::BLUE),
],
)
.ask(),
QuestionAction::Decompression => ChoicePrompt::new(
format!("Do you want to overwrite {path}?"),
[
("yes", Op::Overwrite, *colors::GREEN),
("no", Op::Cancel, *colors::RED),
("rename", Op::Rename, *colors::BLUE),
("merge", Op::Merge, *colors::ORANGE),
],
)
.ask(),
}
}
/// Create the file if it doesn't exist and if it does then ask to overwrite it.
/// If the user doesn't want to overwrite then we return [`Ok(None)`]
pub fn ask_to_create_file(path: &Path, question_policy: QuestionPolicy) -> Result<Option<fs::File>> {
pub fn ask_to_create_file(
path: &Path,
question_policy: QuestionPolicy,
question_action: QuestionAction,
) -> Result<Option<fs::File>> {
match fs::OpenOptions::new().write(true).create_new(true).open(path) {
Ok(w) => Ok(Some(w)),
Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => {
let action = match question_policy {
QuestionPolicy::AlwaysYes => FileConflitOperation::Overwrite,
QuestionPolicy::AlwaysNo => FileConflitOperation::Cancel,
QuestionPolicy::Ask => ask_file_conflict_operation(path)?,
QuestionPolicy::Ask => ask_file_conflict_operation(path, question_action)?,
};
match action {
FileConflitOperation::Merge => Ok(Some(fs::File::create(path)?)),
FileConflitOperation::Overwrite => {
utils::remove_file_or_dir(path)?;
Ok(Some(fs::File::create(path)?))