66// spell-checker:ignore (path) eacces inacc rm-r4
77
88use clap:: { Arg , ArgAction , Command , builder:: ValueParser , parser:: ValueSource } ;
9+ use std:: collections:: HashMap ;
910use std:: ffi:: { OsStr , OsString } ;
1011use std:: fs:: { self , Metadata } ;
1112use std:: io:: { IsTerminal , stdin} ;
@@ -16,11 +17,38 @@ use std::os::unix::ffi::OsStrExt;
1617use std:: os:: unix:: fs:: PermissionsExt ;
1718use std:: path:: MAIN_SEPARATOR ;
1819use std:: path:: { Path , PathBuf } ;
20+ use thiserror:: Error ;
1921use 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 } ;
2224use uucore:: { format_usage, os_str_as_bytes, prompt_yes, show_error} ;
2325
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+
2452#[ derive( Eq , PartialEq , Clone , Copy ) ]
2553/// Enum, determining when the `rm` will prompt the user about the file deletion
2654pub enum InteractiveMode {
@@ -117,7 +145,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
117145 if files. is_empty ( ) && !force_flag {
118146 // Still check by hand and not use clap
119147 // Because "rm -f" is a thing
120- return Err ( UUsageError :: new ( 1 , "missing operand" ) ) ;
148+ return Err ( RmError :: MissingOperand . into ( ) ) ;
121149 }
122150
123151 // 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<()> {
146174 "once" => InteractiveMode :: Once ,
147175 "always" => InteractiveMode :: Always ,
148176 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 ( ) ) ;
153178 }
154179 }
155180 } else {
@@ -206,31 +231,27 @@ pub fn uu_app() -> Command {
206231 Arg :: new ( OPT_FORCE )
207232 . short ( 'f' )
208233 . long ( OPT_FORCE )
209- . help ( "ignore nonexistent files and arguments, never prompt" )
234+ . help ( get_message ( "rm-help-force" ) )
210235 . action ( ArgAction :: SetTrue ) ,
211236 )
212237 . arg (
213238 Arg :: new ( OPT_PROMPT_ALWAYS )
214239 . short ( 'i' )
215- . help ( " prompt before every removal" )
240+ . help ( get_message ( "rm-help- prompt-always" ) )
216241 . overrides_with_all ( [ OPT_PROMPT_ONCE , OPT_INTERACTIVE ] )
217242 . action ( ArgAction :: SetTrue ) ,
218243 )
219244 . arg (
220245 Arg :: new ( OPT_PROMPT_ONCE )
221246 . 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" ) )
224248 . overrides_with_all ( [ OPT_PROMPT_ALWAYS , OPT_INTERACTIVE ] )
225249 . action ( ArgAction :: SetTrue ) ,
226250 )
227251 . arg (
228252 Arg :: new ( OPT_INTERACTIVE )
229253 . 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" ) )
234255 . value_name ( "WHEN" )
235256 . num_args ( 0 ..=1 )
236257 . require_equals ( true )
@@ -240,44 +261,41 @@ pub fn uu_app() -> Command {
240261 . arg (
241262 Arg :: new ( OPT_ONE_FILE_SYSTEM )
242263 . 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 ) ,
248266 )
249267 . arg (
250268 Arg :: new ( OPT_NO_PRESERVE_ROOT )
251269 . long ( OPT_NO_PRESERVE_ROOT )
252- . help ( "do not treat '/' specially" )
270+ . help ( get_message ( "rm-help-no-preserve-root" ) )
253271 . action ( ArgAction :: SetTrue ) ,
254272 )
255273 . arg (
256274 Arg :: new ( OPT_PRESERVE_ROOT )
257275 . long ( OPT_PRESERVE_ROOT )
258- . help ( "do not remove '/' (default)" )
276+ . help ( get_message ( "rm-help-preserve-root" ) )
259277 . action ( ArgAction :: SetTrue ) ,
260278 )
261279 . arg (
262280 Arg :: new ( OPT_RECURSIVE )
263281 . short ( 'r' )
264282 . visible_short_alias ( 'R' )
265283 . long ( OPT_RECURSIVE )
266- . help ( "remove directories and their contents recursively" )
284+ . help ( get_message ( "rm-help-recursive" ) )
267285 . action ( ArgAction :: SetTrue ) ,
268286 )
269287 . arg (
270288 Arg :: new ( OPT_DIR )
271289 . short ( 'd' )
272290 . long ( OPT_DIR )
273- . help ( "remove empty directories" )
291+ . help ( get_message ( "rm-help-dir" ) )
274292 . action ( ArgAction :: SetTrue ) ,
275293 )
276294 . arg (
277295 Arg :: new ( OPT_VERBOSE )
278296 . short ( 'v' )
279297 . long ( OPT_VERBOSE )
280- . help ( "explain what is being done" )
298+ . help ( get_message ( "rm-help-verbose" ) )
281299 . action ( ArgAction :: SetTrue ) ,
282300 )
283301 // From the GNU source code:
@@ -337,8 +355,8 @@ pub fn remove(files: &[&OsStr], options: &Options) -> bool {
337355 false
338356 } else {
339357 show_error ! (
340- "cannot remove {}: No such file or directory " ,
341- filename. quote ( )
358+ "{} " ,
359+ RmError :: CannotRemoveNoSuchFile ( filename. to_string_lossy ( ) . to_string ( ) )
342360 ) ;
343361 true
344362 }
@@ -510,8 +528,8 @@ fn handle_dir(path: &Path, options: &Options) -> bool {
510528 let path = clean_trailing_slashes ( path) ;
511529 if path_is_current_or_parent_directory ( path) {
512530 show_error ! (
513- "refusing to remove '.' or '..' directory: skipping '{}' " ,
514- path. display( )
531+ "{} " ,
532+ RmError :: RefusingToRemoveDirectory ( path. display( ) . to_string ( ) )
515533 ) ;
516534 return true ;
517535 }
@@ -522,13 +540,13 @@ fn handle_dir(path: &Path, options: &Options) -> bool {
522540 } else if options. dir && ( !is_root || !options. preserve_root ) {
523541 had_err = remove_dir ( path, options) . bitor ( had_err) ;
524542 } 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 ) ;
527545 had_err = true ;
528546 } else {
529547 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 ( ) )
532550 ) ;
533551 had_err = true ;
534552 }
@@ -547,7 +565,10 @@ fn remove_dir(path: &Path, options: &Options) -> bool {
547565
548566 // Called to remove a symlink_dir (windows) without "-r"/"-R" or "-d".
549567 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+ ) ;
551572 return true ;
552573 }
553574
@@ -578,7 +599,10 @@ fn remove_file(path: &Path, options: &Options) -> bool {
578599 Err ( e) => {
579600 if e. kind ( ) == std:: io:: ErrorKind :: PermissionDenied {
580601 // 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+ ) ;
582606 } else {
583607 show_error ! ( "cannot remove {}: {e}" , path. quote( ) ) ;
584608 }
0 commit comments