mirror of
https://github.com/uutils/coreutils.git
synced 2026-06-10 15:48:22 -07:00
clap/local: split the functions
This commit is contained in:
@@ -79,32 +79,14 @@ fn colorize(text: &str, color: Color) -> String {
|
||||
format!("\x1b[{}m{text}\x1b[0m", color.code())
|
||||
}
|
||||
|
||||
pub fn handle_clap_error_with_exit_code(err: Error, util_name: &str, exit_code: i32) -> ! {
|
||||
// Ensure localization is initialized for this utility (always with common strings)
|
||||
let _ = crate::locale::setup_localization(util_name);
|
||||
|
||||
// Check if colors are enabled by examining clap's rendered output
|
||||
let rendered_str = err.render().to_string();
|
||||
let colors_enabled = rendered_str.contains("\x1b[");
|
||||
|
||||
// Helper function to conditionally colorize text
|
||||
let maybe_colorize = |text: &str, color: Color| -> String {
|
||||
if colors_enabled {
|
||||
colorize(text, color)
|
||||
} else {
|
||||
text.to_string()
|
||||
}
|
||||
};
|
||||
|
||||
/// Handle DisplayHelp and DisplayVersion errors
|
||||
fn handle_display_errors(err: Error) -> ! {
|
||||
match err.kind() {
|
||||
ErrorKind::DisplayHelp => {
|
||||
// For help messages, we use the localized help template
|
||||
// The template should already have the localized usage label,
|
||||
// but we also replace any remaining "Usage:" instances for fallback
|
||||
|
||||
// Ensure localization is initialized
|
||||
let _ = crate::locale::setup_localization(util_name);
|
||||
|
||||
let help_text = err.render().to_string();
|
||||
|
||||
// Replace any remaining "Usage:" with localized version as fallback
|
||||
@@ -120,167 +102,185 @@ pub fn handle_clap_error_with_exit_code(err: Error, util_name: &str, exit_code:
|
||||
print!("{}", err.render());
|
||||
std::process::exit(0);
|
||||
}
|
||||
ErrorKind::UnknownArgument => {
|
||||
// Force localization initialization - ignore any previous failures
|
||||
crate::locale::setup_localization(util_name).ok();
|
||||
_ => unreachable!("handle_display_errors called with non-display error"),
|
||||
}
|
||||
}
|
||||
|
||||
// Choose exit code based on utility name
|
||||
let exit_code = match util_name {
|
||||
// These utilities expect exit code 2 for invalid options
|
||||
"ls" | "dir" | "vdir" | "sort" | "tty" | "printenv" => 2,
|
||||
// Most utilities expect exit code 1
|
||||
_ => 1,
|
||||
};
|
||||
/// Handle UnknownArgument errors with localization and suggestions
|
||||
fn handle_unknown_argument_error(
|
||||
err: Error,
|
||||
util_name: &str,
|
||||
maybe_colorize: impl Fn(&str, Color) -> String,
|
||||
) -> ! {
|
||||
if let Some(invalid_arg) = err.get(ContextKind::InvalidArg) {
|
||||
let arg_str = invalid_arg.to_string();
|
||||
// Get localized error word with fallback
|
||||
let error_word = translate!("common-error");
|
||||
|
||||
let colored_arg = maybe_colorize(&arg_str, Color::Yellow);
|
||||
let colored_error_word = maybe_colorize(&error_word, Color::Red);
|
||||
|
||||
// Print main error message with fallback
|
||||
let error_msg = translate!(
|
||||
"clap-error-unexpected-argument",
|
||||
"arg" => colored_arg.clone(),
|
||||
"error_word" => colored_error_word.clone()
|
||||
);
|
||||
eprintln!("{error_msg}");
|
||||
eprintln!();
|
||||
|
||||
// Show suggestion if available
|
||||
if let Some(suggested_arg) = err.get(ContextKind::SuggestedArg) {
|
||||
let tip_word = translate!("common-tip");
|
||||
let colored_tip_word = maybe_colorize(&tip_word, Color::Green);
|
||||
let colored_suggestion = maybe_colorize(&suggested_arg.to_string(), Color::Green);
|
||||
let suggestion_msg = translate!(
|
||||
"clap-error-similar-argument",
|
||||
"tip_word" => colored_tip_word.clone(),
|
||||
"suggestion" => colored_suggestion.clone()
|
||||
);
|
||||
eprintln!("{suggestion_msg}");
|
||||
eprintln!();
|
||||
} else {
|
||||
// For UnknownArgument, we need to preserve clap's built-in tips (like using -- for values)
|
||||
// while still allowing localization of the main error message
|
||||
let rendered_str = err.render().to_string();
|
||||
let _lines: Vec<&str> = rendered_str.lines().collect();
|
||||
|
||||
if let Some(invalid_arg) = err.get(ContextKind::InvalidArg) {
|
||||
let arg_str = invalid_arg.to_string();
|
||||
|
||||
// Get localized error word with fallback
|
||||
let error_word = translate!("common-error");
|
||||
|
||||
let colored_arg = maybe_colorize(&arg_str, Color::Yellow);
|
||||
let colored_error_word = maybe_colorize(&error_word, Color::Red);
|
||||
|
||||
// Print main error message with fallback
|
||||
let error_msg = translate!(
|
||||
"clap-error-unexpected-argument",
|
||||
"arg" => colored_arg.clone(),
|
||||
"error_word" => colored_error_word.clone()
|
||||
);
|
||||
eprintln!("{error_msg}");
|
||||
eprintln!();
|
||||
|
||||
// Show suggestion if available
|
||||
let suggestion = err.get(ContextKind::SuggestedArg);
|
||||
if let Some(suggested_arg) = suggestion {
|
||||
let tip_word = translate!("common-tip");
|
||||
let colored_tip_word = maybe_colorize(&tip_word, Color::Green);
|
||||
let colored_suggestion =
|
||||
maybe_colorize(&suggested_arg.to_string(), Color::Green);
|
||||
let suggestion_msg = translate!(
|
||||
"clap-error-similar-argument",
|
||||
"tip_word" => colored_tip_word.clone(),
|
||||
"suggestion" => colored_suggestion.clone()
|
||||
);
|
||||
eprintln!("{suggestion_msg}");
|
||||
// Look for other clap tips (like "-- --file-with-dash") that aren't suggestions
|
||||
// These usually start with " tip:" and contain useful information
|
||||
for line in rendered_str.lines() {
|
||||
if line.trim_start().starts_with("tip:") && !line.contains("similar argument") {
|
||||
eprintln!("{line}");
|
||||
eprintln!();
|
||||
} else {
|
||||
// Look for other clap tips (like "-- --file-with-dash") that aren't suggestions
|
||||
// These usually start with " tip:" and contain useful information
|
||||
for line in _lines.iter() {
|
||||
if line.trim().starts_with("tip:") && !line.contains("similar argument") {
|
||||
eprintln!("{}", line);
|
||||
eprintln!();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Show usage information for unknown arguments
|
||||
let usage_key = format!("{util_name}-usage");
|
||||
let usage_text = translate!(&usage_key);
|
||||
let formatted_usage = crate::format_usage(&usage_text);
|
||||
let usage_label = translate!("common-usage");
|
||||
eprintln!("{}: {}", usage_label, formatted_usage);
|
||||
eprintln!();
|
||||
eprintln!("{}", translate!("common-help-suggestion"));
|
||||
|
||||
std::process::exit(exit_code);
|
||||
} else {
|
||||
// Generic fallback case
|
||||
let error_word = translate!("common-error");
|
||||
let colored_error_word = maybe_colorize(&error_word, Color::Red);
|
||||
eprintln!("{colored_error_word}: unexpected argument");
|
||||
std::process::exit(exit_code);
|
||||
}
|
||||
}
|
||||
|
||||
// Show usage information for unknown arguments
|
||||
let usage_key = format!("{util_name}-usage");
|
||||
let usage_text = translate!(&usage_key);
|
||||
let formatted_usage = crate::format_usage(&usage_text);
|
||||
let usage_label = translate!("common-usage");
|
||||
eprintln!("{usage_label}: {formatted_usage}");
|
||||
eprintln!();
|
||||
eprintln!("{}", translate!("common-help-suggestion"));
|
||||
} else {
|
||||
// Generic fallback case
|
||||
let error_word = translate!("common-error");
|
||||
let colored_error_word = maybe_colorize(&error_word, Color::Red);
|
||||
eprintln!("{colored_error_word}: unexpected argument");
|
||||
}
|
||||
// Choose exit code based on utility name
|
||||
let exit_code = match util_name {
|
||||
// These utilities expect exit code 2 for invalid options
|
||||
"ls" | "dir" | "vdir" | "sort" | "tty" | "printenv" => 2,
|
||||
// Most utilities expect exit code 1
|
||||
_ => 1,
|
||||
};
|
||||
|
||||
std::process::exit(exit_code);
|
||||
}
|
||||
|
||||
/// Handle InvalidValue and ValueValidation errors with localization
|
||||
fn handle_invalid_value_error(err: Error, maybe_colorize: impl Fn(&str, Color) -> String) -> ! {
|
||||
// Extract value and option from error context using clap's context API
|
||||
// This is much more robust than parsing the error string
|
||||
let invalid_arg = err.get(ContextKind::InvalidArg);
|
||||
let invalid_value = err.get(ContextKind::InvalidValue);
|
||||
|
||||
if let (Some(arg), Some(value)) = (invalid_arg, invalid_value) {
|
||||
let option = arg.to_string();
|
||||
let value = value.to_string();
|
||||
|
||||
// Check if this is actually a missing value (empty string)
|
||||
if value.is_empty() {
|
||||
// This is the case where no value was provided for an option that requires one
|
||||
let error_word = translate!("common-error");
|
||||
eprintln!(
|
||||
"{}",
|
||||
translate!("clap-error-value-required", "error_word" => error_word, "option" => option)
|
||||
);
|
||||
} else {
|
||||
// Get localized error word and prepare message components outside conditionals
|
||||
let error_word = translate!("common-error");
|
||||
let colored_error_word = maybe_colorize(&error_word, Color::Red);
|
||||
let colored_value = maybe_colorize(&value, Color::Yellow);
|
||||
let colored_option = maybe_colorize(&option, Color::Green);
|
||||
|
||||
let error_msg = translate!(
|
||||
"clap-error-invalid-value",
|
||||
"error_word" => colored_error_word,
|
||||
"value" => colored_value,
|
||||
"option" => colored_option
|
||||
);
|
||||
|
||||
// For ValueValidation errors, include the validation error in the message
|
||||
match err.source() {
|
||||
Some(source) if matches!(err.kind(), ErrorKind::ValueValidation) => {
|
||||
eprintln!("{error_msg}: {source}");
|
||||
}
|
||||
_ => eprintln!("{error_msg}"),
|
||||
}
|
||||
}
|
||||
|
||||
// For ValueValidation errors, include the validation error details
|
||||
// Note: We don't print these separately anymore as they're part of the main message
|
||||
|
||||
// Show possible values if available (for InvalidValue errors)
|
||||
if matches!(err.kind(), ErrorKind::InvalidValue) {
|
||||
if let Some(valid_values) = err.get(ContextKind::ValidValue) {
|
||||
if !valid_values.to_string().is_empty() {
|
||||
// Don't show possible values if they are empty
|
||||
eprintln!();
|
||||
let possible_values_label = translate!("clap-error-possible-values");
|
||||
eprintln!(" [{possible_values_label}: {valid_values}]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
eprintln!();
|
||||
eprintln!("{}", translate!("common-help-suggestion"));
|
||||
} else {
|
||||
// Fallback if we can't extract context - use clap's default formatting
|
||||
let rendered_str = err.render().to_string();
|
||||
let lines: Vec<&str> = rendered_str.lines().collect();
|
||||
if let Some(main_error_line) = lines.first() {
|
||||
eprintln!("{main_error_line}");
|
||||
eprintln!();
|
||||
eprintln!("{}", translate!("common-help-suggestion"));
|
||||
} else {
|
||||
eprint!("{}", err.render());
|
||||
}
|
||||
}
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
pub fn handle_clap_error_with_exit_code(err: Error, util_name: &str, exit_code: i32) -> ! {
|
||||
// Check if colors are enabled by examining clap's rendered output
|
||||
let rendered_str = err.render().to_string();
|
||||
let colors_enabled = rendered_str.contains("\x1b[");
|
||||
|
||||
// Helper function to conditionally colorize text
|
||||
let maybe_colorize = |text: &str, color: Color| -> String {
|
||||
if colors_enabled {
|
||||
colorize(text, color)
|
||||
} else {
|
||||
text.to_string()
|
||||
}
|
||||
};
|
||||
|
||||
match err.kind() {
|
||||
ErrorKind::DisplayHelp | ErrorKind::DisplayVersion => {
|
||||
handle_display_errors(err);
|
||||
}
|
||||
ErrorKind::UnknownArgument => {
|
||||
handle_unknown_argument_error(err, util_name, maybe_colorize);
|
||||
}
|
||||
// Check if this is a simple validation error that should show simple help
|
||||
kind if should_show_simple_help_for_clap_error(kind) => {
|
||||
// Special handling for InvalidValue and ValueValidation to provide localized error
|
||||
if matches!(kind, ErrorKind::InvalidValue | ErrorKind::ValueValidation) {
|
||||
// Force localization initialization
|
||||
crate::locale::setup_localization(util_name).ok();
|
||||
|
||||
// Extract value and option from error context using clap's context API
|
||||
// This is much more robust than parsing the error string
|
||||
let invalid_arg = err.get(ContextKind::InvalidArg);
|
||||
let invalid_value = err.get(ContextKind::InvalidValue);
|
||||
|
||||
if let (Some(arg), Some(value)) = (invalid_arg, invalid_value) {
|
||||
let option = arg.to_string();
|
||||
let value = value.to_string();
|
||||
|
||||
// Check if this is actually a missing value (empty string)
|
||||
if value.is_empty() {
|
||||
// This is the case where no value was provided for an option that requires one
|
||||
let error_word = translate!("common-error");
|
||||
eprintln!(
|
||||
"{}",
|
||||
translate!("clap-error-value-required", "error_word" => error_word, "option" => option)
|
||||
);
|
||||
eprintln!();
|
||||
eprintln!("{}", translate!("common-help-suggestion"));
|
||||
std::process::exit(1);
|
||||
} else {
|
||||
// Get localized error word and prepare message components outside conditionals
|
||||
let error_word = translate!("common-error");
|
||||
let colored_error_word = maybe_colorize(&error_word, Color::Red);
|
||||
let colored_value = maybe_colorize(&value, Color::Yellow);
|
||||
let colored_option = maybe_colorize(&option, Color::Green);
|
||||
|
||||
let error_msg = translate!(
|
||||
"clap-error-invalid-value",
|
||||
"error_word" => colored_error_word,
|
||||
"value" => colored_value,
|
||||
"option" => colored_option
|
||||
);
|
||||
|
||||
// For ValueValidation errors, include the validation error in the message
|
||||
if matches!(kind, ErrorKind::ValueValidation) {
|
||||
if let Some(source) = err.source() {
|
||||
// Print error with validation detail on same line
|
||||
eprintln!("{error_msg}: {}", source);
|
||||
} else {
|
||||
// Print localized error message
|
||||
eprintln!("{error_msg}");
|
||||
}
|
||||
} else {
|
||||
// Print localized error message
|
||||
eprintln!("{error_msg}");
|
||||
}
|
||||
}
|
||||
|
||||
// For ValueValidation errors, include the validation error details
|
||||
// Note: We don't print these separately anymore as they're part of the main message
|
||||
|
||||
// Show possible values if available (for InvalidValue errors)
|
||||
if matches!(kind, ErrorKind::InvalidValue) {
|
||||
if let Some(valid_values) = err.get(ContextKind::ValidValue) {
|
||||
eprintln!();
|
||||
let possible_values_label = translate!("clap-error-possible-values");
|
||||
eprintln!(" [{}: {}]", possible_values_label, valid_values);
|
||||
}
|
||||
}
|
||||
|
||||
eprintln!();
|
||||
eprintln!("{}", translate!("common-help-suggestion"));
|
||||
std::process::exit(1);
|
||||
} else {
|
||||
// Fallback if we can't extract context - use clap's default formatting
|
||||
let lines: Vec<&str> = rendered_str.lines().collect();
|
||||
if let Some(main_error_line) = lines.first() {
|
||||
eprintln!("{}", main_error_line);
|
||||
eprintln!();
|
||||
eprintln!("{}", translate!("common-help-suggestion"));
|
||||
} else {
|
||||
eprint!("{}", err.render());
|
||||
}
|
||||
std::process::exit(1);
|
||||
}
|
||||
handle_invalid_value_error(err, maybe_colorize);
|
||||
}
|
||||
|
||||
// For other simple validation errors, use the same simple format as other errors
|
||||
|
||||
Reference in New Issue
Block a user