@@ -12,23 +12,99 @@ use clap::error::{ContextKind, ErrorKind};
12
12
use clap:: { ArgMatches , Command , Error } ;
13
13
use std:: ffi:: OsString ;
14
14
15
+ /// Determines if a clap error should show simple help instead of full usage
16
+ /// Based on clap's own design patterns and error categorization
17
+ fn should_show_simple_help_for_clap_error ( kind : ErrorKind ) -> bool {
18
+ match kind {
19
+ // Most validation errors should show simple help
20
+ ErrorKind :: InvalidValue
21
+ | ErrorKind :: InvalidSubcommand
22
+ | ErrorKind :: ValueValidation
23
+ | ErrorKind :: InvalidUtf8
24
+ | ErrorKind :: ArgumentConflict
25
+ | ErrorKind :: NoEquals => true ,
26
+
27
+ // Argument count and structural errors need special formatting
28
+ ErrorKind :: TooFewValues
29
+ | ErrorKind :: TooManyValues
30
+ | ErrorKind :: WrongNumberOfValues
31
+ | ErrorKind :: MissingSubcommand => false ,
32
+
33
+ // MissingRequiredArgument needs different handling
34
+ ErrorKind :: MissingRequiredArgument => false ,
35
+
36
+ // Special cases - handle their own display
37
+ ErrorKind :: DisplayHelp
38
+ | ErrorKind :: DisplayHelpOnMissingArgumentOrSubcommand
39
+ | ErrorKind :: DisplayVersion => false ,
40
+
41
+ // UnknownArgument gets special handling elsewhere, so mark as false here
42
+ ErrorKind :: UnknownArgument => false ,
43
+
44
+ // System errors - keep simple
45
+ ErrorKind :: Io | ErrorKind :: Format => true ,
46
+
47
+ // Default for any new ErrorKind variants - be conservative and show simple help
48
+ _ => true ,
49
+ }
50
+ }
51
+
52
+ /// Color enum for consistent styling
53
+ #[ derive( Debug , Clone , Copy ) ]
54
+ pub enum Color {
55
+ Red ,
56
+ Yellow ,
57
+ Green ,
58
+ }
59
+
60
+ impl Color {
61
+ fn code ( self ) -> & ' static str {
62
+ match self {
63
+ Color :: Red => "31" ,
64
+ Color :: Yellow => "33" ,
65
+ Color :: Green => "32" ,
66
+ }
67
+ }
68
+ }
69
+
15
70
/// Apply color to text using ANSI escape codes
16
- fn colorize ( text : & str , color_code : & str ) -> String {
17
- format ! ( "\x1b [{color_code }m{text}\x1b [0m" )
71
+ fn colorize ( text : & str , color : Color ) -> String {
72
+ format ! ( "\x1b [{}m{text}\x1b [0m" , color . code ( ) )
18
73
}
19
74
20
- /// Color constants for consistent styling
21
- pub mod colors {
22
- pub const RED : & str = "31" ;
23
- pub const YELLOW : & str = "33" ;
24
- pub const GREEN : & str = "32" ;
75
+ /// Display usage information and help suggestion for errors that require it
76
+ /// This consolidates the shared logic between clap errors and UUsageError
77
+ pub fn display_usage_and_help ( util_name : & str ) {
78
+ eprintln ! ( ) ;
79
+ // Try to get usage information from localization
80
+ let usage_key = format ! ( "{}-usage" , util_name) ;
81
+ let usage_text = translate ! ( & usage_key) ;
82
+ let formatted_usage = crate :: format_usage ( & usage_text) ;
83
+ let usage_label = translate ! ( "common-usage" ) ;
84
+ eprintln ! ( "{}: {}" , usage_label, formatted_usage) ;
85
+ eprintln ! ( ) ;
86
+ let help_msg = translate ! ( "common-help-suggestion" , "command" => crate :: execution_phrase( ) ) ;
87
+ eprintln ! ( "{help_msg}" ) ;
25
88
}
26
89
27
90
pub fn handle_clap_error_with_exit_code ( err : Error , util_name : & str , exit_code : i32 ) -> ! {
28
91
// Try to ensure localization is initialized for this utility
29
92
// If it's already initialized, that's fine - we'll use the existing one
30
93
let _ = crate :: locale:: setup_localization_with_common ( util_name) ;
31
94
95
+ // Check if colors are enabled by examining clap's rendered output
96
+ let rendered_str = err. render ( ) . to_string ( ) ;
97
+ let colors_enabled = rendered_str. contains ( "\x1b [" ) ;
98
+
99
+ // Helper function to conditionally colorize text
100
+ let maybe_colorize = |text : & str , color : Color | -> String {
101
+ if colors_enabled {
102
+ colorize ( text, color)
103
+ } else {
104
+ text. to_string ( )
105
+ }
106
+ } ;
107
+
32
108
match err. kind ( ) {
33
109
ErrorKind :: DisplayHelp | ErrorKind :: DisplayVersion => {
34
110
// For help and version, use clap's built-in formatting and exit with 0
@@ -37,33 +113,17 @@ pub fn handle_clap_error_with_exit_code(err: Error, util_name: &str, exit_code:
37
113
std:: process:: exit ( 0 ) ;
38
114
}
39
115
ErrorKind :: UnknownArgument => {
40
- // Use clap's rendering system but capture the output to check if colors are used
41
- let rendered = err. render ( ) ;
42
- let rendered_str = rendered. to_string ( ) ;
43
-
44
- // Simple check - if the rendered output contains ANSI escape codes, colors are enabled
45
- let colors_enabled = rendered_str. contains ( "\x1b [" ) ;
46
-
47
- // Helper closure to conditionally apply colors
48
- let apply_color = |text : & str , color : & str | {
49
- if colors_enabled {
50
- colorize ( text, color)
51
- } else {
52
- text. to_string ( )
53
- }
54
- } ;
55
-
116
+ // UnknownArgument gets special handling for suggestions, but should still show simple help
56
117
if let Some ( invalid_arg) = err. get ( ContextKind :: InvalidArg ) {
57
118
let arg_str = invalid_arg. to_string ( ) ;
58
119
59
120
// Get the uncolored words from common strings
60
121
let error_word = translate ! ( "common-error" ) ;
61
122
let tip_word = translate ! ( "common-tip" ) ;
62
123
63
- // Apply colors using helper closure
64
- let colored_arg = apply_color ( & arg_str, colors:: YELLOW ) ;
65
- let colored_error_word = apply_color ( & error_word, colors:: RED ) ;
66
- let colored_tip_word = apply_color ( & tip_word, colors:: GREEN ) ;
124
+ let colored_arg = maybe_colorize ( & arg_str, Color :: Yellow ) ;
125
+ let colored_error_word = maybe_colorize ( & error_word, Color :: Red ) ;
126
+ let colored_tip_word = maybe_colorize ( & tip_word, Color :: Green ) ;
67
127
68
128
// Print main error message
69
129
let error_msg = translate ! (
@@ -77,15 +137,17 @@ pub fn handle_clap_error_with_exit_code(err: Error, util_name: &str, exit_code:
77
137
// Show suggestion or generic tip
78
138
let suggestion = err. get ( ContextKind :: SuggestedArg ) ;
79
139
if let Some ( suggested_arg) = suggestion {
80
- let colored_suggestion = apply_color ( & suggested_arg. to_string ( ) , colors:: GREEN ) ;
140
+ let colored_suggestion =
141
+ maybe_colorize ( & suggested_arg. to_string ( ) , Color :: Green ) ;
81
142
let suggestion_msg = translate ! (
82
143
"clap-error-similar-argument" ,
83
144
"tip_word" => colored_tip_word,
84
145
"suggestion" => colored_suggestion
85
146
) ;
86
147
eprintln ! ( " {suggestion_msg}" ) ;
87
148
} else {
88
- let colored_tip_command = apply_color ( & format ! ( "-- {arg_str}" ) , colors:: GREEN ) ;
149
+ let colored_tip_command =
150
+ maybe_colorize ( & format ! ( "-- {arg_str}" ) , Color :: Green ) ;
89
151
let tip_msg = translate ! (
90
152
"clap-error-pass-as-value" ,
91
153
"arg" => colored_arg,
@@ -95,28 +157,56 @@ pub fn handle_clap_error_with_exit_code(err: Error, util_name: &str, exit_code:
95
157
eprintln ! ( " {tip_msg}" ) ;
96
158
}
97
159
98
- // Show usage and help
160
+ // Show usage information for unknown arguments but use simple -- help format
99
161
eprintln ! ( ) ;
162
+ // Try to get usage information from localization
163
+ let usage_key = format ! ( "{}-usage" , util_name) ;
164
+ let usage_text = translate ! ( & usage_key) ;
165
+ let formatted_usage = crate :: format_usage ( & usage_text) ;
100
166
let usage_label = translate ! ( "common-usage" ) ;
101
- let usage_pattern = translate ! ( & format!( "{util_name}-usage" ) ) ;
102
- eprintln ! ( "{usage_label}: {usage_pattern}" ) ;
167
+ eprintln ! ( "{}: {}" , usage_label, formatted_usage) ;
103
168
eprintln ! ( ) ;
104
-
105
- let help_msg = translate ! ( "clap-error-help-suggestion" , "command" => util_name) ;
106
- eprintln ! ( "{help_msg}" ) ;
169
+ // Use simple --help format for GNU test compatibility
170
+ eprintln ! ( "For more information, try '--help'." ) ;
107
171
108
172
std:: process:: exit ( exit_code) ;
109
173
} else {
110
- // Generic fallback case - reuse colors_enabled and apply_color from above scope
111
- let colored_error_word = apply_color ( & translate ! ( "common-error" ) , colors :: RED ) ;
174
+ // Generic fallback case
175
+ let colored_error_word = maybe_colorize ( & translate ! ( "common-error" ) , Color :: Red ) ;
112
176
eprintln ! ( "{colored_error_word}: unexpected argument" ) ;
113
177
std:: process:: exit ( exit_code) ;
114
178
}
115
179
}
180
+ // Check if this is a simple validation error that should show simple help
181
+ kind if should_show_simple_help_for_clap_error ( kind) => {
182
+ // For simple validation errors, use the same simple format as other errors
183
+ let lines: Vec < & str > = rendered_str. lines ( ) . collect ( ) ;
184
+ if let Some ( main_error_line) = lines. first ( ) {
185
+ // Keep the "error: " prefix for test compatibility
186
+ eprintln ! ( "{}" , main_error_line) ;
187
+
188
+ // Use the execution phrase for the help suggestion to match test expectations
189
+ eprintln ! ( "For more information, try '--help'" ) ;
190
+ } else {
191
+ // Fallback to original rendering if we can't parse
192
+ eprint ! ( "{}" , err. render( ) ) ;
193
+ }
194
+ std:: process:: exit ( exit_code) ;
195
+ }
116
196
_ => {
117
- // For other errors, print using clap's formatter but exit with code 1
118
- eprint ! ( "{}" , err. render( ) ) ;
119
- std:: process:: exit ( 1 ) ;
197
+ // For other errors, use the simple format but with original clap wording
198
+ let rendered_str = err. render ( ) . to_string ( ) ;
199
+ let lines: Vec < & str > = rendered_str. lines ( ) . collect ( ) ;
200
+
201
+ // Print error message (first line)
202
+ if let Some ( first_line) = lines. first ( ) {
203
+ eprintln ! ( "{}" , first_line) ;
204
+ }
205
+
206
+ // Always use the expected test format for help
207
+ eprintln ! ( "For more information, try '--help'" ) ;
208
+
209
+ std:: process:: exit ( exit_code) ;
120
210
}
121
211
}
122
212
}
0 commit comments