Fix #15440 default --empty fails at empty streams (#15562)

Fixes #15440 

# Description
Wraps ListStream stream type from `impl Iterator` to `Peekable<impl
Iterator>`, this allows checking for empty streams and treating them as
empty values
 
Example:
```
# previously
$ glob ? | default -e void
> # empty list

$ echo '' | default -e void
> void

####################

# now
$ glob ? | default -e void
> void

$ echo '' | default -e void
> void
```

# User-Facing Changes

empty list streams will behave as `nothing` values when testing for
emptiness

# Tests + Formatting

- Add 2 tests
- clippy OK
- fmt OK

# After Submitting
This commit is contained in:
Julian Amarilla 2025-04-17 11:57:25 -03:00 committed by GitHub
parent 0e9927ea4d
commit 7fcebf37ec
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 54 additions and 1 deletions

View File

@ -1,4 +1,5 @@
use nu_engine::command_prelude::*;
use nu_protocol::{ListStream, Signals};
#[derive(Clone)]
pub struct Default;
@ -146,6 +147,20 @@ fn default(
&& matches!(input, PipelineData::Value(ref value, _) if value.is_empty()))
{
Ok(value.into_pipeline_data())
} else if default_when_empty && matches!(input, PipelineData::ListStream(..)) {
let PipelineData::ListStream(ls, metadata) = input else {
unreachable!()
};
let span = ls.span();
let mut stream = ls.into_inner().peekable();
if stream.peek().is_none() {
return Ok(value.into_pipeline_data());
}
// stream's internal state already preserves the original signals config, so if this
// Signals::empty list stream gets interrupted it will be caught by the underlying iterator
let ls = ListStream::new(stream, span, Signals::empty());
Ok(PipelineData::ListStream(ls, metadata))
} else {
Ok(input)
}

View File

@ -1,4 +1,4 @@
use nu_test_support::{nu, pipeline};
use nu_test_support::{fs::Stub::EmptyFile, nu, pipeline, playground::Playground};
#[test]
fn adds_row_data_if_column_missing() {
@ -112,3 +112,41 @@ fn do_not_replace_empty_record() {
let actual = nu!(r#"{} | default {a:5} | columns | length"#);
assert_eq!(actual.out, "0");
}
#[test]
fn replace_empty_list_stream() {
// This is specific for testing ListStreams when empty behave like other empty values
Playground::setup("glob_empty_list", |dirs, sandbox| {
sandbox.with_files(&[
EmptyFile("yehuda.txt"),
EmptyFile("jttxt"),
EmptyFile("andres.txt"),
]);
let actual = nu!(
cwd: dirs.test(),
"glob ? | default -e void",
);
assert_eq!(actual.out, "void");
})
}
#[test]
fn do_not_replace_non_empty_list_stream() {
// This is specific for testing ListStreams when empty behave like other empty values
Playground::setup("glob_non_empty_list", |dirs, sandbox| {
sandbox.with_files(&[
EmptyFile("yehuda.txt"),
EmptyFile("jt.rs"),
EmptyFile("andres.txt"),
]);
let actual = nu!(
cwd: dirs.test(),
"glob '*.txt' | default -e void | length",
);
assert_eq!(actual.out, "2");
})
}