clap: improve the clap support + add tests

This commit is contained in:
Sylvestre Ledru 2025-08-09 23:45:01 +02:00
parent c7342a939b
commit 2063aa1009
2 changed files with 70 additions and 8 deletions

View file

@ -116,7 +116,7 @@ pub fn handle_clap_error_with_exit_code(err: Error, util_name: &str, exit_code:
ErrorKind::UnknownArgument => {
// Force localization initialization - ignore any previous failures
crate::locale::setup_localization_with_common(util_name).ok();
// UnknownArgument gets special handling for suggestions, but should still show simple help
if let Some(invalid_arg) = err.get(ContextKind::InvalidArg) {
let arg_str = invalid_arg.to_string();
@ -124,11 +124,19 @@ pub fn handle_clap_error_with_exit_code(err: Error, util_name: &str, exit_code:
// Get the uncolored words from common strings with fallbacks
let error_word = {
let translated = translate!("common-error");
if translated == "common-error" { "error".to_string() } else { translated }
if translated == "common-error" {
"error".to_string()
} else {
translated
}
};
let tip_word = {
let translated = translate!("common-tip");
if translated == "common-tip" { "tip".to_string() } else { translated }
if translated == "common-tip" {
"tip".to_string()
} else {
translated
}
};
let colored_arg = maybe_colorize(&arg_str, Color::Yellow);
@ -143,7 +151,10 @@ pub fn handle_clap_error_with_exit_code(err: Error, util_name: &str, exit_code:
"error_word" => colored_error_word.clone()
);
if translated.starts_with("clap-error-unexpected-argument") {
format!("{}: unexpected argument '{}' found", colored_error_word, colored_arg)
format!(
"{}: unexpected argument '{}' found",
colored_error_word, colored_arg
)
} else {
translated
}
@ -151,7 +162,7 @@ pub fn handle_clap_error_with_exit_code(err: Error, util_name: &str, exit_code:
eprintln!("{error_msg}");
eprintln!();
// Show suggestion if available
// Show suggestion if available
let suggestion = err.get(ContextKind::SuggestedArg);
if let Some(suggested_arg) = suggestion {
let colored_suggestion =
@ -163,7 +174,10 @@ pub fn handle_clap_error_with_exit_code(err: Error, util_name: &str, exit_code:
"suggestion" => colored_suggestion.clone()
);
if translated.starts_with("clap-error-similar-argument") {
format!(" {}: a similar argument exists: '{}'", colored_tip_word, colored_suggestion)
format!(
" {}: a similar argument exists: '{}'",
colored_tip_word, colored_suggestion
)
} else {
format!(" {}", translated)
}
@ -188,7 +202,11 @@ pub fn handle_clap_error_with_exit_code(err: Error, util_name: &str, exit_code:
let formatted_usage = crate::format_usage(&usage_text);
let usage_label = {
let translated = translate!("common-usage");
if translated == "common-usage" { "Usage".to_string() } else { translated }
if translated == "common-usage" {
"Usage".to_string()
} else {
translated
}
};
eprintln!("{}: {}", usage_label, formatted_usage);
eprintln!();
@ -200,7 +218,11 @@ pub fn handle_clap_error_with_exit_code(err: Error, util_name: &str, exit_code:
// Generic fallback case
let error_word = {
let translated = translate!("common-error");
if translated == "common-error" { "error".to_string() } else { translated }
if translated == "common-error" {
"error".to_string()
} else {
translated
}
};
let colored_error_word = maybe_colorize(&error_word, Color::Red);
eprintln!("{colored_error_word}: unexpected argument");

View file

@ -1402,6 +1402,46 @@ invalid-syntax = This is { $missing
panic!("Expected LocalizationError::ParseResource with snippet");
}
}
#[test]
fn test_clap_localization_fallbacks() {
std::thread::spawn(|| {
// Test the scenario where localization isn't properly initialized
// and we need fallbacks for clap error handling
// First, test when localizer is not initialized
let error_msg = get_message("common-error");
assert_eq!(error_msg, "common-error"); // Should return key when not initialized
let tip_msg = get_message("common-tip");
assert_eq!(tip_msg, "common-tip"); // Should return key when not initialized
// Now initialize with setup_localization_with_common
let result = setup_localization_with_common("comm");
if result.is_err() {
// If setup fails (e.g., no embedded locales for comm), try with a known utility
let _ = setup_localization_with_common("test");
}
// Test that common strings are available after initialization
let error_after_init = get_message("common-error");
// Should either be translated or return the key (but not panic)
assert!(!error_after_init.is_empty());
let tip_after_init = get_message("common-tip");
assert!(!tip_after_init.is_empty());
// Test that clap error keys work with fallbacks
let unknown_arg_key = get_message("clap-error-unexpected-argument");
assert!(!unknown_arg_key.is_empty());
// Test usage key fallback
let usage_key = get_message("common-usage");
assert!(!usage_key.is_empty());
})
.join()
.unwrap();
}
}
#[cfg(all(test, not(debug_assertions)))]