From 2063aa10090f0fc5db733484dbd180856efff084 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sat, 9 Aug 2025 23:45:01 +0200 Subject: [PATCH] clap: improve the clap support + add tests --- src/uucore/src/lib/mods/clap_localization.rs | 38 +++++++++++++++---- src/uucore/src/lib/mods/locale.rs | 40 ++++++++++++++++++++ 2 files changed, 70 insertions(+), 8 deletions(-) diff --git a/src/uucore/src/lib/mods/clap_localization.rs b/src/uucore/src/lib/mods/clap_localization.rs index 5c47fa14e..96e0681e2 100644 --- a/src/uucore/src/lib/mods/clap_localization.rs +++ b/src/uucore/src/lib/mods/clap_localization.rs @@ -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"); diff --git a/src/uucore/src/lib/mods/locale.rs b/src/uucore/src/lib/mods/locale.rs index aec455cfe..8e1a09a23 100644 --- a/src/uucore/src/lib/mods/locale.rs +++ b/src/uucore/src/lib/mods/locale.rs @@ -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)))]