add interact-once switch to rm (#7432)

# Description

Fixes: #7216 

Adds `interact-once` switch which numbers out the number of files to
delete and asks the user for confirmation.

```
/home/gabriel/test〉ls                                                                                                                                                  12/11/2022 11:25:42 AM
╭───┬───────┬──────┬──────┬──────────╮
│ # │ name  │ type │ size │ modified │
├───┼───────┼──────┼──────┼──────────┤
│ 0 │ a.txt │ file │  0 B │ now      │
│ 1 │ b.txt │ file │  0 B │ now      │
│ 2 │ c.txt │ file │  0 B │ now      │
╰───┴───────┴──────┴──────┴──────────╯
/home/gabriel/test〉rm *.txt -I                                                                                                                                         12/11/2022 11:25:42 AM
rm: remove 3 files? : y

/home/gabriel/test〉ls                                                                                                                                                  12/11/2022 11:25:51 AM
/home/gabriel/test〉                                                                                                                                                    12/11/2022 11:25:54 AM
```

# User-Facing Changes

_(List of all changes that impact the user experience here. This helps
us keep track of breaking changes.)_

# Tests + Formatting

Don't forget to add tests that cover your changes.

Make sure you've run and fixed any issues with these commands:

- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A
clippy::needless_collect` to check that you're using the standard code
style
- `cargo test --workspace` to check that all tests pass

# After Submitting

If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
This commit is contained in:
pwygab 2022-12-12 05:22:27 +08:00 committed by GitHub
parent 585ab56ea4
commit 5036672a58
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 37 additions and 9 deletions

View File

@ -322,8 +322,10 @@ fn interactive_copy(
span: Span, span: Span,
copy_impl: impl Fn(PathBuf, PathBuf, Span) -> Value, copy_impl: impl Fn(PathBuf, PathBuf, Span) -> Value,
) -> Value { ) -> Value {
let (interaction, confirmed) = let (interaction, confirmed) = try_interaction(
try_interaction(interactive, "cp: overwrite", &dst.to_string_lossy()); interactive,
format!("cp: overwrite '{}'? ", dst.to_string_lossy()),
);
if let Err(e) = interaction { if let Err(e) = interaction {
Value::Error { Value::Error {
error: ShellError::GenericError( error: ShellError::GenericError(

View File

@ -283,8 +283,10 @@ fn move_file(
} }
if interactive && to.exists() { if interactive && to.exists() {
let (interaction, confirmed) = let (interaction, confirmed) = try_interaction(
try_interaction(interactive, "mv: overwrite", &to.to_string_lossy()); interactive,
format!("mv: overwrite '{}'? ", to.to_string_lossy()),
);
if let Err(e) = interaction { if let Err(e) = interaction {
return Err(ShellError::GenericError( return Err(ShellError::GenericError(
format!("Error during interaction: {:}", e), format!("Error during interaction: {:}", e),

View File

@ -65,6 +65,11 @@ impl Command for Rm {
.switch("force", "suppress error when no file", Some('f')) .switch("force", "suppress error when no file", Some('f'))
.switch("verbose", "print names of deleted files", Some('v')) .switch("verbose", "print names of deleted files", Some('v'))
.switch("interactive", "ask user to confirm action", Some('i')) .switch("interactive", "ask user to confirm action", Some('i'))
.switch(
"interactive-once",
"ask user to confirm action only once",
Some('I'),
)
.rest( .rest(
"rest", "rest",
SyntaxShape::GlobPattern, SyntaxShape::GlobPattern,
@ -138,6 +143,7 @@ fn rm(
let force = call.has_flag("force"); let force = call.has_flag("force");
let verbose = call.has_flag("verbose"); let verbose = call.has_flag("verbose");
let interactive = call.has_flag("interactive"); let interactive = call.has_flag("interactive");
let interactive_once = call.has_flag("interactive-once") && !interactive;
let ctrlc = engine_state.ctrlc.clone(); let ctrlc = engine_state.ctrlc.clone();
@ -299,6 +305,24 @@ fn rm(
)); ));
} }
if interactive_once {
let (interaction, confirmed) = try_interaction(
interactive_once,
format!("rm: remove {} files? ", all_targets.len()),
);
if let Err(e) = interaction {
return Err(ShellError::GenericError(
format!("Error during interaction: {:}", e),
"could not move".into(),
None,
None,
Vec::new(),
));
} else if !confirmed {
return Ok(PipelineData::Empty);
}
}
Ok(all_targets Ok(all_targets
.into_keys() .into_keys()
.map(move |f| { .map(move |f| {
@ -325,8 +349,10 @@ fn rm(
|| is_fifo || is_fifo
|| is_empty() || is_empty()
{ {
let (interaction, confirmed) = let (interaction, confirmed) = try_interaction(
try_interaction(interactive, "rm: remove", &f.to_string_lossy()); interactive,
format!("rm: remove '{}'? ", f.to_string_lossy()),
);
let result; let result;
#[cfg(all( #[cfg(all(

View File

@ -90,11 +90,9 @@ impl Resource {}
pub fn try_interaction( pub fn try_interaction(
interactive: bool, interactive: bool,
prompt_msg: &str, prompt: String,
file_name: &str,
) -> (Result<Option<bool>, Box<dyn Error>>, bool) { ) -> (Result<Option<bool>, Box<dyn Error>>, bool) {
let interaction = if interactive { let interaction = if interactive {
let prompt = format!("{} '{}'? ", prompt_msg, file_name);
match get_interactive_confirmation(prompt) { match get_interactive_confirmation(prompt) {
Ok(i) => Ok(Some(i)), Ok(i) => Ok(Some(i)),
Err(e) => Err(e), Err(e) => Err(e),