From 2c4b53c976e21ce27c081136a091b05185ecdb7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20R=2E=20Miguel?= Date: Wed, 17 Aug 2022 22:01:10 -0300 Subject: [PATCH] Fix wrong filename suggestions when compressing folders into non-archives --- src/commands/mod.rs | 76 +++++++++++++++++++++++++++++++++++++++------ src/extension.rs | 6 ++-- 2 files changed, 70 insertions(+), 12 deletions(-) diff --git a/src/commands/mod.rs b/src/commands/mod.rs index c684cf7ff..133d14e40 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -16,7 +16,7 @@ use utils::colors; use crate::{ commands::{compress::compress_files, decompress::decompress_file, list::list_archive_contents}, error::FinalError, - extension::{self, flatten_compression_formats, Extension}, + extension::{self, flatten_compression_formats, Extension, SUPPORTED_EXTENSIONS}, info, list::ListOptions, utils::{ @@ -46,6 +46,40 @@ fn represents_several_files(files: &[PathBuf]) -> bool { files.iter().any(is_non_empty_dir) || files.len() > 1 } +/// Builds a suggested output file in scenarios where the user tried to compress +/// a folder into a non-archive compression format, for error message purposes +/// +/// E.g.: `build_suggestion("file.bz.xz", ".tar")` results in `Some("file.tar.bz.xz")` +fn build_archive_file_suggestion(path: &Path, suggested_extension: &str) -> Option { + let path = path.to_string_lossy(); + let mut rest = &*path; + let mut position_to_insert = 0; + + // Walk through the path to find the first supported compression extension + while let Some(pos) = rest.find('.') { + // Use just the text located after the dot we found + rest = &rest[pos + 1..]; + position_to_insert += pos + 1; + + // If the string contains more chained extensions, clip to the immediate one + let maybe_extension = { + let idx = rest.find('.').unwrap_or(rest.len()); + &rest[..idx] + }; + + // If the extension we got is a supported extension, generate the suggestion + // at the position we found + if SUPPORTED_EXTENSIONS.contains(&maybe_extension) { + let mut path = path.to_string(); + path.insert_str(position_to_insert - 1, suggested_extension); + + return Some(path); + } + } + + None +} + /// This function checks what command needs to be run and performs A LOT of ahead-of-time checks /// to assume everything is OK. /// @@ -93,16 +127,10 @@ pub fn run( // It says: // Change from file.bz.xz // To file.tar.bz.xz - let extensions_text: String = formats.iter().map(|format| format.to_string()).collect(); - - let output_path = to_utf(&output_path).to_string(); + let suggested_output_path = build_archive_file_suggestion(&output_path, ".tar") + .expect("output path did not contain a compression format"); - // Breaks if Lzma is .lz or .lzma and not .xz - // Or if Bzip is .bz2 and not .bz - let extensions_start_position = output_path.rfind(&extensions_text).unwrap(); - let pos = extensions_start_position - 1; - let mut suggested_output_path = output_path.to_string(); - suggested_output_path.insert_str(pos, ".tar"); + let output_path = to_utf(&output_path); let error = FinalError::with_title(format!("Cannot compress to '{}'.", output_path)) .detail("You are trying to compress multiple files.") @@ -386,3 +414,31 @@ fn deduplicate_input_files(files: &mut Vec, output_path: &Path) { } } } + +#[cfg(test)] +mod tests { + use std::path::Path; + + use super::build_archive_file_suggestion; + + #[test] + fn builds_suggestion_correctly() { + assert_eq!(build_archive_file_suggestion(Path::new("linux.png"), ".tar"), None); + assert_eq!( + build_archive_file_suggestion(Path::new("linux.xz.gz.zst"), ".tar").unwrap(), + "linux.tar.xz.gz.zst" + ); + assert_eq!( + build_archive_file_suggestion(Path::new("linux.pkg.xz.gz.zst"), ".tar").unwrap(), + "linux.pkg.tar.xz.gz.zst" + ); + assert_eq!( + build_archive_file_suggestion(Path::new("linux.pkg.zst"), ".tar").unwrap(), + "linux.pkg.tar.zst" + ); + assert_eq!( + build_archive_file_suggestion(Path::new("linux.pkg.info.zst"), ".tar").unwrap(), + "linux.pkg.info.tar.zst" + ); + } +} diff --git a/src/extension.rs b/src/extension.rs index a70c60d21..006f73a69 100644 --- a/src/extension.rs +++ b/src/extension.rs @@ -98,8 +98,10 @@ impl fmt::Display for CompressionFormat { } } -// use crate::extension::CompressionFormat::*; -// +pub const SUPPORTED_EXTENSIONS: &[&str] = &[ + "tar", "tgz", "tbz", "tlz4", "txz", "tzlma", "tsz", "tzst", "zip", "bz", "bz2", "gz", "lz4", "xz", "lzma", "sz", + "zst", +]; /// Extracts extensions from a path, /// return both the remaining path and the list of extension objects