Filesystem commands print --verbose to stderr (#8014)

# Description

Makes `mkdir`, `cp`, `mv` and `rm` return nothing and print info to
stderr:

![image](https://user-images.githubusercontent.com/17511668/217859228-feffa4bc-c22d-45d3-b330-1903f5a4d938.png)
See https://github.com/nushell/nushell/pull/7925#issuecomment-1420861638
and
[discord](https://discord.com/channels/601130461678272522/615329862395101194/1072523941865857055).

# User-Facing Changes

`mkdir`, `cp`, `mv` and `rm` will return nothing and print info to
stderr with `--verbose` flag.

# 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:
Artemiy 2023-02-09 21:29:34 +03:00 committed by GitHub
parent 023e244958
commit 99aea0c71c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 54 additions and 13 deletions

View File

@ -279,14 +279,19 @@ impl Command for Cp {
}
if verbose {
Ok(result.into_iter().into_pipeline_data(ctrlc))
result
.into_iter()
.into_pipeline_data(ctrlc)
.print_not_formatted(engine_state, false, true)?;
} else {
// filter to only errors
Ok(result
result
.into_iter()
.filter(|v| matches!(v, Value::Error { .. }))
.into_pipeline_data(ctrlc))
.into_pipeline_data(ctrlc)
.print_not_formatted(engine_state, false, true)?;
}
Ok(PipelineData::empty())
}
fn examples(&self) -> Vec<Example> {

View File

@ -19,7 +19,7 @@ impl Command for Mkdir {
fn signature(&self) -> Signature {
Signature::build("mkdir")
.input_output_types(vec![(Type::Nothing, Type::List(Box::new(Type::String)))])
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.rest(
"rest",
SyntaxShape::Directory,
@ -83,9 +83,11 @@ impl Command for Mkdir {
}
}
Ok(stream
stream
.into_iter()
.into_pipeline_data(engine_state.ctrlc.clone()))
.into_pipeline_data(engine_state.ctrlc.clone())
.print_not_formatted(engine_state, false, true)?;
Ok(PipelineData::empty())
}
fn examples(&self) -> Vec<Example> {

View File

@ -35,7 +35,7 @@ impl Command for Mv {
fn signature(&self) -> nu_protocol::Signature {
Signature::build("mv")
.input_output_types(vec![(Type::Nothing, Type::List(Box::new(Type::String)))])
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.required(
"source",
SyntaxShape::GlobPattern,
@ -177,7 +177,7 @@ impl Command for Mv {
}
let span = call.head;
Ok(sources
sources
.into_iter()
.flatten()
.filter_map(move |entry| {
@ -212,7 +212,9 @@ impl Command for Mv {
None
}
})
.into_pipeline_data(ctrlc))
.into_pipeline_data(ctrlc)
.print_not_formatted(engine_state, false, true)?;
Ok(PipelineData::empty())
}
fn examples(&self) -> Vec<Example> {

View File

@ -45,7 +45,7 @@ impl Command for Rm {
fn signature(&self) -> Signature {
let sig = Signature::build("rm")
.input_output_types(vec![(Type::Nothing, Type::List(Box::new(Type::String)))])
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.required(
"filename",
SyntaxShape::Filepath,
@ -329,7 +329,7 @@ fn rm(
}
}
Ok(all_targets
all_targets
.into_keys()
.map(move |f| {
let is_empty = || match f.read_dir() {
@ -450,5 +450,8 @@ fn rm(
}
})
.filter(|x| !matches!(x.get_type(), Type::Nothing))
.into_pipeline_data(ctrlc))
.into_pipeline_data(ctrlc)
.print_not_formatted(engine_state, false, true)?;
Ok(PipelineData::empty())
}

View File

@ -79,6 +79,8 @@ fn print_created_paths() {
dirs.test()
));
assert_eq!(actual.out, "3");
assert!(actual.err.contains("dir_1"));
assert!(actual.err.contains("dir_2"));
assert!(actual.err.contains("dir_3"));
})
}

View File

@ -644,6 +644,33 @@ impl PipelineData {
Ok(0)
}
/// Consume and print self data immediately.
///
/// Unlike [print] does not call `table` to format data and just prints it
/// one element on a line
/// * `no_newline` controls if we need to attach newline character to output.
/// * `to_stderr` controls if data is output to stderr, when the value is false, the data is output to stdout.
pub fn print_not_formatted(
self,
engine_state: &EngineState,
no_newline: bool,
to_stderr: bool,
) -> Result<i64, ShellError> {
let config = engine_state.get_config();
if let PipelineData::ExternalStream {
stdout: stream,
stderr: stderr_stream,
exit_code,
..
} = self
{
print_if_stream(stream, stderr_stream, to_stderr, exit_code)
} else {
self.write_all_and_flush(engine_state, config, no_newline, to_stderr)
}
}
fn write_all_and_flush(
self,
engine_state: &EngineState,