Skip to content

Commit 2a80c83

Browse files
authored
Merge pull request #8419 from cakebaker/rm_interactive_arguments
rm: support the `--interactive` arg aliases
2 parents f877f64 + 194aa89 commit 2a80c83

File tree

5 files changed

+65
-45
lines changed

5 files changed

+65
-45
lines changed

src/uu/rm/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ path = "src/rm.rs"
2020
[dependencies]
2121
thiserror = { workspace = true }
2222
clap = { workspace = true }
23-
uucore = { workspace = true, features = ["fs"] }
23+
uucore = { workspace = true, features = ["fs", "parser"] }
2424
fluent = { workspace = true }
2525

2626
[target.'cfg(unix)'.dependencies]

src/uu/rm/locales/en-US.ftl

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ rm-help-verbose = explain what is being done
3232
# Error messages
3333
rm-error-missing-operand = missing operand
3434
Try '{$util_name} --help' for more information.
35-
rm-error-invalid-interactive-argument = Invalid argument to interactive ({$arg})
3635
rm-error-cannot-remove-no-such-file = cannot remove {$file}: No such file or directory
3736
rm-error-cannot-remove-permission-denied = cannot remove {$file}: Permission denied
3837
rm-error-cannot-remove-is-directory = cannot remove {$file}: Is a directory

src/uu/rm/locales/fr-FR.ftl

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ rm-help-verbose = expliquer ce qui est fait
3232
# Messages d'erreur
3333
rm-error-missing-operand = opérande manquant
3434
Essayez '{$util_name} --help' pour plus d'informations.
35-
rm-error-invalid-interactive-argument = Argument invalide pour interactive ({$arg})
3635
rm-error-cannot-remove-no-such-file = impossible de supprimer {$file} : Aucun fichier ou répertoire de ce type
3736
rm-error-cannot-remove-permission-denied = impossible de supprimer {$file} : Permission refusée
3837
rm-error-cannot-remove-is-directory = impossible de supprimer {$file} : C'est un répertoire

src/uu/rm/src/rm.rs

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55

66
// spell-checker:ignore (path) eacces inacc rm-r4
77

8-
use clap::{Arg, ArgAction, Command, builder::ValueParser, parser::ValueSource};
8+
use clap::builder::{PossibleValue, ValueParser};
9+
use clap::{Arg, ArgAction, Command, parser::ValueSource};
910
use std::ffi::{OsStr, OsString};
1011
use std::fs::{self, Metadata};
1112
use std::io::{IsTerminal, stdin};
@@ -19,6 +20,7 @@ use std::path::{Path, PathBuf};
1920
use thiserror::Error;
2021
use uucore::display::Quotable;
2122
use uucore::error::{FromIo, UError, UResult};
23+
use uucore::parser::shortcut_value_parser::ShortcutValueParser;
2224
use uucore::translate;
2325

2426
use uucore::{format_usage, os_str_as_bytes, prompt_yes, show_error};
@@ -27,8 +29,6 @@ use uucore::{format_usage, os_str_as_bytes, prompt_yes, show_error};
2729
enum RmError {
2830
#[error("{}", translate!("rm-error-missing-operand", "util_name" => uucore::execution_phrase()))]
2931
MissingOperand,
30-
#[error("{}", translate!("rm-error-invalid-interactive-argument", "arg" => _0.clone()))]
31-
InvalidInteractiveArgument(String),
3232
#[error("{}", translate!("rm-error-cannot-remove-no-such-file", "file" => _0.quote()))]
3333
CannotRemoveNoSuchFile(String),
3434
#[error("{}", translate!("rm-error-cannot-remove-permission-denied", "file" => _0.quote()))]
@@ -59,6 +59,20 @@ pub enum InteractiveMode {
5959
PromptProtected,
6060
}
6161

62+
// We implement `From` instead of `TryFrom` because clap guarantees that we only receive valid values.
63+
//
64+
// The `PromptProtected` variant is not supposed to be created from a string.
65+
impl From<&str> for InteractiveMode {
66+
fn from(s: &str) -> Self {
67+
match s {
68+
"never" => Self::Never,
69+
"once" => Self::Once,
70+
"always" => Self::Always,
71+
_ => unreachable!("should be prevented by clap"),
72+
}
73+
}
74+
}
75+
6276
/// Options for the `rm` command
6377
///
6478
/// All options are public so that the options can be programmatically
@@ -165,14 +179,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
165179
} else if matches.get_flag(OPT_PROMPT_ONCE) {
166180
InteractiveMode::Once
167181
} else if matches.contains_id(OPT_INTERACTIVE) {
168-
match matches.get_one::<String>(OPT_INTERACTIVE).unwrap().as_str() {
169-
"never" => InteractiveMode::Never,
170-
"once" => InteractiveMode::Once,
171-
"always" => InteractiveMode::Always,
172-
val => {
173-
return Err(RmError::InvalidInteractiveArgument(val.to_string()).into());
174-
}
175-
}
182+
InteractiveMode::from(matches.get_one::<String>(OPT_INTERACTIVE).unwrap().as_str())
176183
} else {
177184
InteractiveMode::PromptProtected
178185
}
@@ -249,6 +256,11 @@ pub fn uu_app() -> Command {
249256
.long(OPT_INTERACTIVE)
250257
.help(translate!("rm-help-interactive"))
251258
.value_name("WHEN")
259+
.value_parser(ShortcutValueParser::new([
260+
PossibleValue::new("always").alias("yes"),
261+
PossibleValue::new("once"),
262+
PossibleValue::new("never").alias("no").alias("none"),
263+
]))
252264
.num_args(0..=1)
253265
.require_equals(true)
254266
.default_missing_value("always")

tests/by-util/test_rm.rs

Lines changed: 41 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@
66

77
use std::process::Stdio;
88

9-
use uutests::util::TestScenario;
10-
use uutests::{at_and_ucmd, new_ucmd, util_name};
9+
use uutests::{at_and_ucmd, new_ucmd, util::TestScenario, util_name};
1110

1211
#[test]
1312
fn test_invalid_arg() {
@@ -379,42 +378,53 @@ fn test_silently_accepts_presume_input_tty2() {
379378
fn test_interactive_never() {
380379
let scene = TestScenario::new(util_name!());
381380
let at = &scene.fixtures;
381+
let file = "a";
382382

383-
let file_2 = "test_rm_interactive";
383+
for arg in ["never", "no", "none"] {
384+
at.touch(file);
385+
#[cfg(feature = "chmod")]
386+
scene.ccmd("chmod").arg("0").arg(file).succeeds();
384387

385-
at.touch(file_2);
386-
#[cfg(feature = "chmod")]
387-
scene.ccmd("chmod").arg("0").arg(file_2).succeeds();
388-
389-
scene
390-
.ucmd()
391-
.arg("--interactive=never")
392-
.arg(file_2)
393-
.succeeds()
394-
.stdout_is("");
388+
scene
389+
.ucmd()
390+
.arg(format!("--interactive={arg}"))
391+
.arg(file)
392+
.succeeds()
393+
.no_output();
395394

396-
assert!(!at.file_exists(file_2));
395+
assert!(!at.file_exists(file));
396+
}
397397
}
398398

399399
#[test]
400-
fn test_interactive_missing_value() {
401-
// `--interactive` is equivalent to `--interactive=always` or `-i`
402-
let (at, mut ucmd) = at_and_ucmd!();
403-
404-
let file1 = "test_rm_interactive_missing_value_file1";
405-
let file2 = "test_rm_interactive_missing_value_file2";
406-
407-
at.touch(file1);
408-
at.touch(file2);
409-
410-
ucmd.arg("--interactive")
411-
.arg(file1)
412-
.arg(file2)
413-
.pipe_in("y\ny")
414-
.succeeds();
400+
fn test_interactive_always() {
401+
let scene = TestScenario::new(util_name!());
402+
let at = &scene.fixtures;
415403

416-
assert!(!at.file_exists(file1));
417-
assert!(!at.file_exists(file2));
404+
let file_a = "a";
405+
let file_b = "b";
406+
407+
for arg in [
408+
"-i",
409+
"--interactive",
410+
"--interactive=always",
411+
"--interactive=yes",
412+
] {
413+
at.touch(file_a);
414+
at.touch(file_b);
415+
416+
scene
417+
.ucmd()
418+
.arg(arg)
419+
.arg(file_a)
420+
.arg(file_b)
421+
.pipe_in("y\ny")
422+
.succeeds()
423+
.no_stdout();
424+
425+
assert!(!at.file_exists(file_a));
426+
assert!(!at.file_exists(file_b));
427+
}
418428
}
419429

420430
#[test]

0 commit comments

Comments
 (0)