Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix wrong filename suggestions when compressing folders into non-archives #269

Merged
merged 1 commit into from
Aug 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 66 additions & 10 deletions src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down Expand Up @@ -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<String> {
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.
///
Expand Down Expand Up @@ -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.")
Expand Down Expand Up @@ -386,3 +414,31 @@ fn deduplicate_input_files(files: &mut Vec<PathBuf>, 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"
);
}
}
6 changes: 4 additions & 2 deletions src/extension.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down