6
6
// spell-checker:ignore (path) eacces inacc rm-r4
7
7
8
8
use clap:: { Arg , ArgAction , Command , builder:: ValueParser , parser:: ValueSource } ;
9
+ use std:: collections:: HashMap ;
9
10
use std:: ffi:: { OsStr , OsString } ;
10
11
use std:: fs:: { self , Metadata } ;
11
12
use std:: io:: { IsTerminal , stdin} ;
@@ -16,11 +17,38 @@ use std::os::unix::ffi::OsStrExt;
16
17
use std:: os:: unix:: fs:: PermissionsExt ;
17
18
use std:: path:: MAIN_SEPARATOR ;
18
19
use std:: path:: { Path , PathBuf } ;
20
+ use thiserror:: Error ;
19
21
use uucore:: display:: Quotable ;
20
- use uucore:: error:: { FromIo , UResult , USimpleError , UUsageError } ;
21
- use uucore:: locale:: get_message;
22
+ use uucore:: error:: { FromIo , UError , UResult } ;
23
+ use uucore:: locale:: { get_message, get_message_with_args } ;
22
24
use uucore:: { format_usage, os_str_as_bytes, prompt_yes, show_error} ;
23
25
26
+ #[ derive( Debug , Error ) ]
27
+ enum RmError {
28
+ #[ error( "{}" , get_message_with_args( "rm-error-missing-operand" ,
29
+ HashMap :: from( [
30
+ ( "util_name" . to_string( ) ,
31
+ uucore:: execution_phrase( ) . to_string( ) )
32
+ ] ) ) ) ]
33
+ MissingOperand ,
34
+ #[ error( "{}" , get_message_with_args( "rm-error-invalid-interactive-argument" , HashMap :: from( [ ( "arg" . to_string( ) , _0. clone( ) ) ] ) ) ) ]
35
+ InvalidInteractiveArgument ( String ) ,
36
+ #[ error( "{}" , get_message_with_args( "rm-error-cannot-remove-no-such-file" , HashMap :: from( [ ( "file" . to_string( ) , _0. quote( ) . to_string( ) ) ] ) ) ) ]
37
+ CannotRemoveNoSuchFile ( String ) ,
38
+ #[ error( "{}" , get_message_with_args( "rm-error-cannot-remove-permission-denied" , HashMap :: from( [ ( "file" . to_string( ) , _0. quote( ) . to_string( ) ) ] ) ) ) ]
39
+ CannotRemovePermissionDenied ( String ) ,
40
+ #[ error( "{}" , get_message_with_args( "rm-error-cannot-remove-is-directory" , HashMap :: from( [ ( "file" . to_string( ) , _0. quote( ) . to_string( ) ) ] ) ) ) ]
41
+ CannotRemoveIsDirectory ( String ) ,
42
+ #[ error( "{}" , get_message( "rm-error-dangerous-recursive-operation" ) ) ]
43
+ DangerousRecursiveOperation ,
44
+ #[ error( "{}" , get_message( "rm-error-use-no-preserve-root" ) ) ]
45
+ UseNoPreserveRoot ,
46
+ #[ error( "{}" , get_message_with_args( "rm-error-refusing-to-remove-directory" , HashMap :: from( [ ( "path" . to_string( ) , _0. to_string( ) ) ] ) ) ) ]
47
+ RefusingToRemoveDirectory ( String ) ,
48
+ }
49
+
50
+ impl UError for RmError { }
51
+
24
52
#[ derive( Eq , PartialEq , Clone , Copy ) ]
25
53
/// Enum, determining when the `rm` will prompt the user about the file deletion
26
54
pub enum InteractiveMode {
@@ -117,7 +145,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
117
145
if files. is_empty ( ) && !force_flag {
118
146
// Still check by hand and not use clap
119
147
// Because "rm -f" is a thing
120
- return Err ( UUsageError :: new ( 1 , "missing operand" ) ) ;
148
+ return Err ( RmError :: MissingOperand . into ( ) ) ;
121
149
}
122
150
123
151
// If -f(--force) is before any -i (or variants) we want prompts else no prompts
@@ -146,10 +174,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
146
174
"once" => InteractiveMode :: Once ,
147
175
"always" => InteractiveMode :: Always ,
148
176
val => {
149
- return Err ( USimpleError :: new (
150
- 1 ,
151
- format ! ( "Invalid argument to interactive ({val})" ) ,
152
- ) ) ;
177
+ return Err ( RmError :: InvalidInteractiveArgument ( val. to_string ( ) ) . into ( ) ) ;
153
178
}
154
179
}
155
180
} else {
@@ -206,31 +231,27 @@ pub fn uu_app() -> Command {
206
231
Arg :: new ( OPT_FORCE )
207
232
. short ( 'f' )
208
233
. long ( OPT_FORCE )
209
- . help ( "ignore nonexistent files and arguments, never prompt" )
234
+ . help ( get_message ( "rm-help-force" ) )
210
235
. action ( ArgAction :: SetTrue ) ,
211
236
)
212
237
. arg (
213
238
Arg :: new ( OPT_PROMPT_ALWAYS )
214
239
. short ( 'i' )
215
- . help ( " prompt before every removal" )
240
+ . help ( get_message ( "rm-help- prompt-always" ) )
216
241
. overrides_with_all ( [ OPT_PROMPT_ONCE , OPT_INTERACTIVE ] )
217
242
. action ( ArgAction :: SetTrue ) ,
218
243
)
219
244
. arg (
220
245
Arg :: new ( OPT_PROMPT_ONCE )
221
246
. short ( 'I' )
222
- . help ( "prompt once before removing more than three files, or when removing recursively. \
223
- Less intrusive than -i, while still giving some protection against most mistakes")
247
+ . help ( get_message ( "rm-help-prompt-once" ) )
224
248
. overrides_with_all ( [ OPT_PROMPT_ALWAYS , OPT_INTERACTIVE ] )
225
249
. action ( ArgAction :: SetTrue ) ,
226
250
)
227
251
. arg (
228
252
Arg :: new ( OPT_INTERACTIVE )
229
253
. long ( OPT_INTERACTIVE )
230
- . help (
231
- "prompt according to WHEN: never, once (-I), or always (-i). Without WHEN, \
232
- prompts always",
233
- )
254
+ . help ( get_message ( "rm-help-interactive" ) )
234
255
. value_name ( "WHEN" )
235
256
. num_args ( 0 ..=1 )
236
257
. require_equals ( true )
@@ -240,44 +261,41 @@ pub fn uu_app() -> Command {
240
261
. arg (
241
262
Arg :: new ( OPT_ONE_FILE_SYSTEM )
242
263
. long ( OPT_ONE_FILE_SYSTEM )
243
- . help (
244
- "when removing a hierarchy recursively, skip any directory that is on a file \
245
- system different from that of the corresponding command line argument (NOT \
246
- IMPLEMENTED)",
247
- ) . action ( ArgAction :: SetTrue ) ,
264
+ . help ( get_message ( "rm-help-one-file-system" ) )
265
+ . action ( ArgAction :: SetTrue ) ,
248
266
)
249
267
. arg (
250
268
Arg :: new ( OPT_NO_PRESERVE_ROOT )
251
269
. long ( OPT_NO_PRESERVE_ROOT )
252
- . help ( "do not treat '/' specially" )
270
+ . help ( get_message ( "rm-help-no-preserve-root" ) )
253
271
. action ( ArgAction :: SetTrue ) ,
254
272
)
255
273
. arg (
256
274
Arg :: new ( OPT_PRESERVE_ROOT )
257
275
. long ( OPT_PRESERVE_ROOT )
258
- . help ( "do not remove '/' (default)" )
276
+ . help ( get_message ( "rm-help-preserve-root" ) )
259
277
. action ( ArgAction :: SetTrue ) ,
260
278
)
261
279
. arg (
262
280
Arg :: new ( OPT_RECURSIVE )
263
281
. short ( 'r' )
264
282
. visible_short_alias ( 'R' )
265
283
. long ( OPT_RECURSIVE )
266
- . help ( "remove directories and their contents recursively" )
284
+ . help ( get_message ( "rm-help-recursive" ) )
267
285
. action ( ArgAction :: SetTrue ) ,
268
286
)
269
287
. arg (
270
288
Arg :: new ( OPT_DIR )
271
289
. short ( 'd' )
272
290
. long ( OPT_DIR )
273
- . help ( "remove empty directories" )
291
+ . help ( get_message ( "rm-help-dir" ) )
274
292
. action ( ArgAction :: SetTrue ) ,
275
293
)
276
294
. arg (
277
295
Arg :: new ( OPT_VERBOSE )
278
296
. short ( 'v' )
279
297
. long ( OPT_VERBOSE )
280
- . help ( "explain what is being done" )
298
+ . help ( get_message ( "rm-help-verbose" ) )
281
299
. action ( ArgAction :: SetTrue ) ,
282
300
)
283
301
// From the GNU source code:
@@ -337,8 +355,8 @@ pub fn remove(files: &[&OsStr], options: &Options) -> bool {
337
355
false
338
356
} else {
339
357
show_error ! (
340
- "cannot remove {}: No such file or directory " ,
341
- filename. quote ( )
358
+ "{} " ,
359
+ RmError :: CannotRemoveNoSuchFile ( filename. to_string_lossy ( ) . to_string ( ) )
342
360
) ;
343
361
true
344
362
}
@@ -510,8 +528,8 @@ fn handle_dir(path: &Path, options: &Options) -> bool {
510
528
let path = clean_trailing_slashes ( path) ;
511
529
if path_is_current_or_parent_directory ( path) {
512
530
show_error ! (
513
- "refusing to remove '.' or '..' directory: skipping '{}' " ,
514
- path. display( )
531
+ "{} " ,
532
+ RmError :: RefusingToRemoveDirectory ( path. display( ) . to_string ( ) )
515
533
) ;
516
534
return true ;
517
535
}
@@ -522,13 +540,13 @@ fn handle_dir(path: &Path, options: &Options) -> bool {
522
540
} else if options. dir && ( !is_root || !options. preserve_root ) {
523
541
had_err = remove_dir ( path, options) . bitor ( had_err) ;
524
542
} else if options. recursive {
525
- show_error ! ( "it is dangerous to operate recursively on '{MAIN_SEPARATOR}'" ) ;
526
- show_error ! ( "use --no-preserve-root to override this failsafe" ) ;
543
+ show_error ! ( "{}" , RmError :: DangerousRecursiveOperation ) ;
544
+ show_error ! ( "{}" , RmError :: UseNoPreserveRoot ) ;
527
545
had_err = true ;
528
546
} else {
529
547
show_error ! (
530
- "cannot remove {}: Is a directory" , // GNU's rm error message does not include help
531
- path. quote ( )
548
+ "{}" ,
549
+ RmError :: CannotRemoveIsDirectory ( path. to_string_lossy ( ) . to_string ( ) )
532
550
) ;
533
551
had_err = true ;
534
552
}
@@ -547,7 +565,10 @@ fn remove_dir(path: &Path, options: &Options) -> bool {
547
565
548
566
// Called to remove a symlink_dir (windows) without "-r"/"-R" or "-d".
549
567
if !options. dir && !options. recursive {
550
- show_error ! ( "cannot remove {}: Is a directory" , path. quote( ) ) ;
568
+ show_error ! (
569
+ "{}" ,
570
+ RmError :: CannotRemoveIsDirectory ( path. to_string_lossy( ) . to_string( ) )
571
+ ) ;
551
572
return true ;
552
573
}
553
574
@@ -578,7 +599,10 @@ fn remove_file(path: &Path, options: &Options) -> bool {
578
599
Err ( e) => {
579
600
if e. kind ( ) == std:: io:: ErrorKind :: PermissionDenied {
580
601
// GNU compatibility (rm/fail-eacces.sh)
581
- show_error ! ( "cannot remove {}: {}" , path. quote( ) , "Permission denied" ) ;
602
+ show_error ! (
603
+ "{}" ,
604
+ RmError :: CannotRemovePermissionDenied ( path. to_string_lossy( ) . to_string( ) )
605
+ ) ;
582
606
} else {
583
607
show_error ! ( "cannot remove {}: {e}" , path. quote( ) ) ;
584
608
}
0 commit comments