last, skip, drop, take until, take while, skip until, skip while, where, reverse, shuffle, append, prepend and sort-by raise error when given non-lists (#7623)
Closes https://github.com/nushell/nushell/issues/6941
This commit is contained in:
parent
81a7d17b33
commit
e9cc417fd5
|
@ -99,7 +99,7 @@ only unwrap the outer list, and leave the variable's contents untouched."#
|
||||||
let metadata = input.metadata();
|
let metadata = input.metadata();
|
||||||
|
|
||||||
Ok(input
|
Ok(input
|
||||||
.into_iter()
|
.into_iter_strict(call.head)?
|
||||||
.chain(vec)
|
.chain(vec)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.into_pipeline_data(engine_state.ctrlc.clone())
|
.into_pipeline_data(engine_state.ctrlc.clone())
|
||||||
|
|
|
@ -91,7 +91,7 @@ impl Command for Drop {
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let metadata = input.metadata();
|
let metadata = input.metadata();
|
||||||
let rows: Option<i64> = call.opt(engine_state, stack, 0)?;
|
let rows: Option<i64> = call.opt(engine_state, stack, 0)?;
|
||||||
let v: Vec<_> = input.into_iter().collect();
|
let v: Vec<_> = input.into_iter_strict(call.head)?.collect();
|
||||||
let vlen: i64 = v.len() as i64;
|
let vlen: i64 = v.len() as i64;
|
||||||
|
|
||||||
let rows_to_drop = if let Some(quantity) = rows {
|
let rows_to_drop = if let Some(quantity) = rows {
|
||||||
|
|
|
@ -92,7 +92,7 @@ impl Command for Last {
|
||||||
|
|
||||||
// only keep last `to_keep` rows in memory
|
// only keep last `to_keep` rows in memory
|
||||||
let mut buf = VecDeque::<_>::new();
|
let mut buf = VecDeque::<_>::new();
|
||||||
for row in input.into_iter() {
|
for row in input.into_iter_strict(call.head)? {
|
||||||
if buf.len() == to_keep {
|
if buf.len() == to_keep {
|
||||||
buf.pop_front();
|
buf.pop_front();
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,7 +105,7 @@ only unwrap the outer list, and leave the variable's contents untouched."#
|
||||||
|
|
||||||
Ok(vec
|
Ok(vec
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.chain(input)
|
.chain(input.into_iter_strict(call.head)?)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.into_pipeline_data(engine_state.ctrlc.clone())
|
.into_pipeline_data(engine_state.ctrlc.clone())
|
||||||
.set_metadata(metadata))
|
.set_metadata(metadata))
|
||||||
|
|
|
@ -66,13 +66,13 @@ impl Command for Reverse {
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
_stack: &mut Stack,
|
_stack: &mut Stack,
|
||||||
_call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let metadata = input.metadata();
|
let metadata = input.metadata();
|
||||||
|
|
||||||
#[allow(clippy::needless_collect)]
|
#[allow(clippy::needless_collect)]
|
||||||
let v: Vec<_> = input.into_iter().collect();
|
let v: Vec<_> = input.into_iter_strict(call.head)?.collect();
|
||||||
let iter = v.into_iter().rev();
|
let iter = v.into_iter().rev();
|
||||||
Ok(iter
|
Ok(iter
|
||||||
.into_pipeline_data(engine_state.ctrlc.clone())
|
.into_pipeline_data(engine_state.ctrlc.clone())
|
||||||
|
|
|
@ -31,11 +31,11 @@ impl Command for Shuffle {
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
_stack: &mut Stack,
|
_stack: &mut Stack,
|
||||||
_call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let metadata = input.metadata();
|
let metadata = input.metadata();
|
||||||
let mut v: Vec<_> = input.into_iter().collect();
|
let mut v: Vec<_> = input.into_iter_strict(call.head)?.collect();
|
||||||
v.shuffle(&mut thread_rng());
|
v.shuffle(&mut thread_rng());
|
||||||
let iter = v.into_iter();
|
let iter = v.into_iter();
|
||||||
Ok(iter
|
Ok(iter
|
||||||
|
|
|
@ -141,7 +141,7 @@ impl Command for Skip {
|
||||||
.set_metadata(metadata))
|
.set_metadata(metadata))
|
||||||
}
|
}
|
||||||
_ => Ok(input
|
_ => Ok(input
|
||||||
.into_iter()
|
.into_iter_strict(call.head)?
|
||||||
.skip(n)
|
.skip(n)
|
||||||
.into_pipeline_data(ctrlc)
|
.into_pipeline_data(ctrlc)
|
||||||
.set_metadata(metadata)),
|
.set_metadata(metadata)),
|
||||||
|
|
|
@ -94,7 +94,7 @@ impl Command for SkipUntil {
|
||||||
let redirect_stderr = call.redirect_stderr;
|
let redirect_stderr = call.redirect_stderr;
|
||||||
|
|
||||||
Ok(input
|
Ok(input
|
||||||
.into_iter()
|
.into_iter_strict(span)?
|
||||||
.skip_while(move |value| {
|
.skip_while(move |value| {
|
||||||
if let Some(var_id) = var_id {
|
if let Some(var_id) = var_id {
|
||||||
stack.add_var(var_id, value.clone());
|
stack.add_var(var_id, value.clone());
|
||||||
|
|
|
@ -95,7 +95,7 @@ impl Command for SkipWhile {
|
||||||
let redirect_stderr = call.redirect_stderr;
|
let redirect_stderr = call.redirect_stderr;
|
||||||
|
|
||||||
Ok(input
|
Ok(input
|
||||||
.into_iter()
|
.into_iter_strict(span)?
|
||||||
.skip_while(move |value| {
|
.skip_while(move |value| {
|
||||||
if let Some(var_id) = var_id {
|
if let Some(var_id) = var_id {
|
||||||
stack.add_var(var_id, value.clone());
|
stack.add_var(var_id, value.clone());
|
||||||
|
|
|
@ -84,7 +84,7 @@ impl Command for SortBy {
|
||||||
let insensitive = call.has_flag("ignore-case");
|
let insensitive = call.has_flag("ignore-case");
|
||||||
let natural = call.has_flag("natural");
|
let natural = call.has_flag("natural");
|
||||||
let metadata = &input.metadata();
|
let metadata = &input.metadata();
|
||||||
let mut vec: Vec<_> = input.into_iter().collect();
|
let mut vec: Vec<_> = input.into_iter_strict(call.head)?.collect();
|
||||||
|
|
||||||
if columns.is_empty() {
|
if columns.is_empty() {
|
||||||
return Err(ShellError::MissingParameter("columns".into(), call.head));
|
return Err(ShellError::MissingParameter("columns".into(), call.head));
|
||||||
|
|
|
@ -90,7 +90,7 @@ impl Command for TakeUntil {
|
||||||
let redirect_stderr = call.redirect_stderr;
|
let redirect_stderr = call.redirect_stderr;
|
||||||
|
|
||||||
Ok(input
|
Ok(input
|
||||||
.into_iter()
|
.into_iter_strict(span)?
|
||||||
.take_while(move |value| {
|
.take_while(move |value| {
|
||||||
if let Some(var_id) = var_id {
|
if let Some(var_id) = var_id {
|
||||||
stack.add_var(var_id, value.clone());
|
stack.add_var(var_id, value.clone());
|
||||||
|
|
|
@ -90,7 +90,7 @@ impl Command for TakeWhile {
|
||||||
let redirect_stderr = call.redirect_stderr;
|
let redirect_stderr = call.redirect_stderr;
|
||||||
|
|
||||||
Ok(input
|
Ok(input
|
||||||
.into_iter()
|
.into_iter_strict(span)?
|
||||||
.take_while(move |value| {
|
.take_while(move |value| {
|
||||||
if let Some(var_id) = var_id {
|
if let Some(var_id) = var_id {
|
||||||
stack.add_var(var_id, value.clone());
|
stack.add_var(var_id, value.clone());
|
||||||
|
|
|
@ -84,7 +84,7 @@ not supported."#
|
||||||
let redirect_stdout = call.redirect_stdout;
|
let redirect_stdout = call.redirect_stdout;
|
||||||
let redirect_stderr = call.redirect_stderr;
|
let redirect_stderr = call.redirect_stderr;
|
||||||
Ok(input
|
Ok(input
|
||||||
.into_iter()
|
.into_iter_strict(span)?
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.filter_map(move |(idx, value)| {
|
.filter_map(move |(idx, value)| {
|
||||||
stack.with_env(&orig_env_vars, &orig_env_hidden);
|
stack.with_env(&orig_env_vars, &orig_env_hidden);
|
||||||
|
|
|
@ -13,3 +13,10 @@ fn adds_a_row_to_the_end() {
|
||||||
|
|
||||||
assert_eq!(actual.out, "pollo loco");
|
assert_eq!(actual.out, "pollo loco");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fail_on_non_iterator() {
|
||||||
|
let actual = nu!(cwd: ".", pipeline("1 | append 3"));
|
||||||
|
|
||||||
|
assert!(actual.err.contains("only_supports_this_input_type"));
|
||||||
|
}
|
||||||
|
|
|
@ -65,7 +65,7 @@ fn rows() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn more_rows_than_table_has() {
|
fn more_rows_than_table_has() {
|
||||||
let actual = nu!(cwd: ".", "date | drop 50 | length");
|
let actual = nu!(cwd: ".", "[date] | drop 50 | length");
|
||||||
|
|
||||||
assert_eq!(actual.out, "0");
|
assert_eq!(actual.out, "0");
|
||||||
}
|
}
|
||||||
|
@ -90,3 +90,10 @@ fn nth_missing_first_argument() {
|
||||||
|
|
||||||
assert!(actual.err.contains("int or range"));
|
assert!(actual.err.contains("int or range"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fail_on_non_iterator() {
|
||||||
|
let actual = nu!(cwd: ".", pipeline("1 | drop 50"));
|
||||||
|
|
||||||
|
assert!(actual.err.contains("only_supports_this_input_type"));
|
||||||
|
}
|
||||||
|
|
|
@ -58,7 +58,7 @@ fn requests_more_rows_than_table_has() {
|
||||||
let actual = nu!(
|
let actual = nu!(
|
||||||
cwd: ".", pipeline(
|
cwd: ".", pipeline(
|
||||||
r#"
|
r#"
|
||||||
date | last 50 | length
|
[date] | last 50 | length
|
||||||
"#
|
"#
|
||||||
));
|
));
|
||||||
|
|
||||||
|
@ -91,3 +91,10 @@ fn last_errors_on_negative_index() {
|
||||||
|
|
||||||
assert!(actual.err.contains("use a positive value"));
|
assert!(actual.err.contains("use a positive value"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fail_on_non_iterator() {
|
||||||
|
let actual = nu!(cwd: ".", pipeline("1 | last"));
|
||||||
|
|
||||||
|
assert!(actual.err.contains("only_supports_this_input_type"));
|
||||||
|
}
|
||||||
|
|
|
@ -27,3 +27,10 @@ fn adds_a_row_to_the_beginning() {
|
||||||
assert_eq!(actual.out, "pollo loco");
|
assert_eq!(actual.out, "pollo loco");
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fail_on_non_iterator() {
|
||||||
|
let actual = nu!(cwd: ".", pipeline("1 | prepend 4"));
|
||||||
|
|
||||||
|
assert!(actual.err.contains("only_supports_this_input_type"));
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use nu_test_support::nu;
|
use nu_test_support::{nu, pipeline};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn can_get_reverse_first() {
|
fn can_get_reverse_first() {
|
||||||
|
@ -9,3 +9,10 @@ fn can_get_reverse_first() {
|
||||||
|
|
||||||
assert_eq!(actual.out, "utf16.ini");
|
assert_eq!(actual.out, "utf16.ini");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fail_on_non_iterator() {
|
||||||
|
let actual = nu!(cwd: ".", pipeline("1 | reverse"));
|
||||||
|
|
||||||
|
assert!(actual.err.contains("only_supports_this_input_type"));
|
||||||
|
}
|
||||||
|
|
|
@ -14,3 +14,10 @@ fn binary_skip() {
|
||||||
|
|
||||||
assert_eq!(actual.out, "772");
|
assert_eq!(actual.out, "772");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fail_on_non_iterator() {
|
||||||
|
let actual = nu!(cwd: ".", pipeline("1 | skip 2"));
|
||||||
|
|
||||||
|
assert!(actual.err.contains("only_supports_this_input_type"));
|
||||||
|
}
|
||||||
|
|
|
@ -49,3 +49,10 @@ fn condition_is_met() {
|
||||||
assert_eq!(actual.out, "6");
|
assert_eq!(actual.out, "6");
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fail_on_non_iterator() {
|
||||||
|
let actual = nu!(cwd: ".", pipeline("1 | skip until {|row| $row == 2}"));
|
||||||
|
|
||||||
|
assert!(actual.err.contains("only_supports_this_input_type"));
|
||||||
|
}
|
||||||
|
|
|
@ -49,3 +49,10 @@ fn condition_is_met() {
|
||||||
assert_eq!(actual.out, "6");
|
assert_eq!(actual.out, "6");
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fail_on_non_iterator() {
|
||||||
|
let actual = nu!(cwd: ".", pipeline("1 | skip while {|row| $row == 2}"));
|
||||||
|
|
||||||
|
assert!(actual.err.contains("only_supports_this_input_type"));
|
||||||
|
}
|
||||||
|
|
|
@ -119,3 +119,10 @@ fn no_column_specified_fails() {
|
||||||
|
|
||||||
assert!(actual.err.contains("missing parameter"));
|
assert!(actual.err.contains("missing parameter"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fail_on_non_iterator() {
|
||||||
|
let actual = nu!(cwd: ".", pipeline("1 | sort-by"));
|
||||||
|
|
||||||
|
assert!(actual.err.contains("only_supports_this_input_type"));
|
||||||
|
}
|
||||||
|
|
|
@ -123,7 +123,7 @@ fn converts_to_int() {
|
||||||
let actual = nu!(
|
let actual = nu!(
|
||||||
cwd: "tests/fixtures/formats", pipeline(
|
cwd: "tests/fixtures/formats", pipeline(
|
||||||
r#"
|
r#"
|
||||||
echo '{number_as_string: "1"}'
|
echo '[{number_as_string: "1"}]'
|
||||||
| from json
|
| from json
|
||||||
| into int number_as_string
|
| into int number_as_string
|
||||||
| rename number
|
| rename number
|
||||||
|
|
|
@ -51,7 +51,7 @@ fn fails_on_string() {
|
||||||
"#
|
"#
|
||||||
));
|
));
|
||||||
|
|
||||||
assert!(actual.err.contains("pipeline_mismatch"));
|
assert!(actual.err.contains("only_supports_this_input_type"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -50,3 +50,10 @@ fn condition_is_met() {
|
||||||
assert_eq!(actual.out, "8");
|
assert_eq!(actual.out, "8");
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fail_on_non_iterator() {
|
||||||
|
let actual = nu!(cwd: ".", pipeline("1 | take until {|row| $row == 2}"));
|
||||||
|
|
||||||
|
assert!(actual.err.contains("only_supports_this_input_type"));
|
||||||
|
}
|
||||||
|
|
|
@ -49,3 +49,10 @@ fn condition_is_met() {
|
||||||
assert_eq!(actual.out, "4");
|
assert_eq!(actual.out, "4");
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fail_on_non_iterator() {
|
||||||
|
let actual = nu!(cwd: ".", pipeline("1 | take while {|row| $row == 2}"));
|
||||||
|
|
||||||
|
assert!(actual.err.contains("only_supports_this_input_type"));
|
||||||
|
}
|
||||||
|
|
|
@ -188,3 +188,10 @@ fn contains_operator() {
|
||||||
|
|
||||||
assert_eq!(actual.out, "2");
|
assert_eq!(actual.out, "2");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fail_on_non_iterator() {
|
||||||
|
let actual = nu!(cwd: ".", pipeline(r#"{"name": "foo", "size": 3} | where name == "foo""#));
|
||||||
|
|
||||||
|
assert!(actual.err.contains("only_supports_this_input_type"));
|
||||||
|
}
|
||||||
|
|
|
@ -196,6 +196,56 @@ impl PipelineData {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Try convert from self into iterator
|
||||||
|
///
|
||||||
|
/// It returns Err if the `self` cannot be converted to an iterator.
|
||||||
|
pub fn into_iter_strict(self, span: Span) -> Result<PipelineIterator, ShellError> {
|
||||||
|
match self {
|
||||||
|
PipelineData::Value(val, metadata) => match val {
|
||||||
|
Value::List { vals, .. } => Ok(PipelineIterator(PipelineData::ListStream(
|
||||||
|
ListStream {
|
||||||
|
stream: Box::new(vals.into_iter()),
|
||||||
|
ctrlc: None,
|
||||||
|
},
|
||||||
|
metadata,
|
||||||
|
))),
|
||||||
|
Value::Binary { val, span } => Ok(PipelineIterator(PipelineData::ListStream(
|
||||||
|
ListStream {
|
||||||
|
stream: Box::new(val.into_iter().map(move |x| Value::int(x as i64, span))),
|
||||||
|
ctrlc: None,
|
||||||
|
},
|
||||||
|
metadata,
|
||||||
|
))),
|
||||||
|
Value::Range { val, .. } => match val.into_range_iter(None) {
|
||||||
|
Ok(iter) => Ok(PipelineIterator(PipelineData::ListStream(
|
||||||
|
ListStream {
|
||||||
|
stream: Box::new(iter),
|
||||||
|
ctrlc: None,
|
||||||
|
},
|
||||||
|
metadata,
|
||||||
|
))),
|
||||||
|
Err(error) => Err(error),
|
||||||
|
},
|
||||||
|
// Propagate errors by explicitly matching them before the final case.
|
||||||
|
Value::Error { error } => Err(error),
|
||||||
|
other => Err(ShellError::OnlySupportsThisInputType(
|
||||||
|
"list, binary, raw data or range".into(),
|
||||||
|
other.get_type().to_string(),
|
||||||
|
span,
|
||||||
|
// This line requires the Value::Error match above.
|
||||||
|
other.expect_span(),
|
||||||
|
)),
|
||||||
|
},
|
||||||
|
PipelineData::Empty => Err(ShellError::OnlySupportsThisInputType(
|
||||||
|
"list, binary, raw data or range".into(),
|
||||||
|
"null".into(),
|
||||||
|
span,
|
||||||
|
span, // TODO: make PipelineData::Empty spanned, so that the span can be used here.
|
||||||
|
)),
|
||||||
|
other => Ok(PipelineIterator(other)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn into_interruptible_iter(self, ctrlc: Option<Arc<AtomicBool>>) -> PipelineIterator {
|
pub fn into_interruptible_iter(self, ctrlc: Option<Arc<AtomicBool>>) -> PipelineIterator {
|
||||||
let mut iter = self.into_iter();
|
let mut iter = self.into_iter();
|
||||||
|
|
||||||
|
|
|
@ -51,8 +51,8 @@ pub enum ShellError {
|
||||||
#[label("value originates from here")] Span,
|
#[label("value originates from here")] Span,
|
||||||
),
|
),
|
||||||
|
|
||||||
#[error("Pipeline mismatch.")]
|
#[error("Only supports for specific input types.")]
|
||||||
#[diagnostic(code(nu::shell::pipeline_mismatch), url(docsrs))]
|
#[diagnostic(code(nu::shell::only_supports_this_input_type), url(docsrs))]
|
||||||
OnlySupportsThisInputType(
|
OnlySupportsThisInputType(
|
||||||
String,
|
String,
|
||||||
String,
|
String,
|
||||||
|
|
Loading…
Reference in New Issue
Block a user