diff --git a/crates/nu-command/src/filters/append.rs b/crates/nu-command/src/filters/append.rs index 6592f67bb4..374b28ab05 100644 --- a/crates/nu-command/src/filters/append.rs +++ b/crates/nu-command/src/filters/append.rs @@ -99,7 +99,7 @@ only unwrap the outer list, and leave the variable's contents untouched."# let metadata = input.metadata(); Ok(input - .into_iter() + .into_iter_strict(call.head)? .chain(vec) .into_iter() .into_pipeline_data(engine_state.ctrlc.clone()) diff --git a/crates/nu-command/src/filters/drop/drop_.rs b/crates/nu-command/src/filters/drop/drop_.rs index ce2bb02580..48b3c0ed90 100644 --- a/crates/nu-command/src/filters/drop/drop_.rs +++ b/crates/nu-command/src/filters/drop/drop_.rs @@ -91,7 +91,7 @@ impl Command for Drop { ) -> Result { let metadata = input.metadata(); let rows: Option = 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 rows_to_drop = if let Some(quantity) = rows { diff --git a/crates/nu-command/src/filters/last.rs b/crates/nu-command/src/filters/last.rs index 87cf3ed5eb..7a59f145f3 100644 --- a/crates/nu-command/src/filters/last.rs +++ b/crates/nu-command/src/filters/last.rs @@ -92,7 +92,7 @@ impl Command for Last { // only keep last `to_keep` rows in memory 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 { buf.pop_front(); } diff --git a/crates/nu-command/src/filters/prepend.rs b/crates/nu-command/src/filters/prepend.rs index ea3699b9fd..208d6b8e5a 100644 --- a/crates/nu-command/src/filters/prepend.rs +++ b/crates/nu-command/src/filters/prepend.rs @@ -105,7 +105,7 @@ only unwrap the outer list, and leave the variable's contents untouched."# Ok(vec .into_iter() - .chain(input) + .chain(input.into_iter_strict(call.head)?) .into_iter() .into_pipeline_data(engine_state.ctrlc.clone()) .set_metadata(metadata)) diff --git a/crates/nu-command/src/filters/reverse.rs b/crates/nu-command/src/filters/reverse.rs index c21439918c..b8fad219c5 100644 --- a/crates/nu-command/src/filters/reverse.rs +++ b/crates/nu-command/src/filters/reverse.rs @@ -66,13 +66,13 @@ impl Command for Reverse { &self, engine_state: &EngineState, _stack: &mut Stack, - _call: &Call, + call: &Call, input: PipelineData, ) -> Result { let metadata = input.metadata(); #[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(); Ok(iter .into_pipeline_data(engine_state.ctrlc.clone()) diff --git a/crates/nu-command/src/filters/shuffle.rs b/crates/nu-command/src/filters/shuffle.rs index 81bbd70cf1..2795792a7f 100644 --- a/crates/nu-command/src/filters/shuffle.rs +++ b/crates/nu-command/src/filters/shuffle.rs @@ -31,11 +31,11 @@ impl Command for Shuffle { &self, engine_state: &EngineState, _stack: &mut Stack, - _call: &Call, + call: &Call, input: PipelineData, ) -> Result { 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()); let iter = v.into_iter(); Ok(iter diff --git a/crates/nu-command/src/filters/skip/skip_.rs b/crates/nu-command/src/filters/skip/skip_.rs index deafd3afe3..1976aa9245 100644 --- a/crates/nu-command/src/filters/skip/skip_.rs +++ b/crates/nu-command/src/filters/skip/skip_.rs @@ -141,7 +141,7 @@ impl Command for Skip { .set_metadata(metadata)) } _ => Ok(input - .into_iter() + .into_iter_strict(call.head)? .skip(n) .into_pipeline_data(ctrlc) .set_metadata(metadata)), diff --git a/crates/nu-command/src/filters/skip/skip_until.rs b/crates/nu-command/src/filters/skip/skip_until.rs index a04395f4ec..d0eea13caf 100644 --- a/crates/nu-command/src/filters/skip/skip_until.rs +++ b/crates/nu-command/src/filters/skip/skip_until.rs @@ -94,7 +94,7 @@ impl Command for SkipUntil { let redirect_stderr = call.redirect_stderr; Ok(input - .into_iter() + .into_iter_strict(span)? .skip_while(move |value| { if let Some(var_id) = var_id { stack.add_var(var_id, value.clone()); diff --git a/crates/nu-command/src/filters/skip/skip_while.rs b/crates/nu-command/src/filters/skip/skip_while.rs index fd7ae6aa7c..1512fa0508 100644 --- a/crates/nu-command/src/filters/skip/skip_while.rs +++ b/crates/nu-command/src/filters/skip/skip_while.rs @@ -95,7 +95,7 @@ impl Command for SkipWhile { let redirect_stderr = call.redirect_stderr; Ok(input - .into_iter() + .into_iter_strict(span)? .skip_while(move |value| { if let Some(var_id) = var_id { stack.add_var(var_id, value.clone()); diff --git a/crates/nu-command/src/filters/sort_by.rs b/crates/nu-command/src/filters/sort_by.rs index 86317d89e0..3ec5dd5c87 100644 --- a/crates/nu-command/src/filters/sort_by.rs +++ b/crates/nu-command/src/filters/sort_by.rs @@ -84,7 +84,7 @@ impl Command for SortBy { let insensitive = call.has_flag("ignore-case"); let natural = call.has_flag("natural"); 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() { return Err(ShellError::MissingParameter("columns".into(), call.head)); diff --git a/crates/nu-command/src/filters/take/take_until.rs b/crates/nu-command/src/filters/take/take_until.rs index 07a2b63225..1f9a94bf5e 100644 --- a/crates/nu-command/src/filters/take/take_until.rs +++ b/crates/nu-command/src/filters/take/take_until.rs @@ -90,7 +90,7 @@ impl Command for TakeUntil { let redirect_stderr = call.redirect_stderr; Ok(input - .into_iter() + .into_iter_strict(span)? .take_while(move |value| { if let Some(var_id) = var_id { stack.add_var(var_id, value.clone()); diff --git a/crates/nu-command/src/filters/take/take_while.rs b/crates/nu-command/src/filters/take/take_while.rs index 8dd53dfdb7..7fd0d9fbdb 100644 --- a/crates/nu-command/src/filters/take/take_while.rs +++ b/crates/nu-command/src/filters/take/take_while.rs @@ -90,7 +90,7 @@ impl Command for TakeWhile { let redirect_stderr = call.redirect_stderr; Ok(input - .into_iter() + .into_iter_strict(span)? .take_while(move |value| { if let Some(var_id) = var_id { stack.add_var(var_id, value.clone()); diff --git a/crates/nu-command/src/filters/where_.rs b/crates/nu-command/src/filters/where_.rs index d7ec659567..4d5fd3f128 100644 --- a/crates/nu-command/src/filters/where_.rs +++ b/crates/nu-command/src/filters/where_.rs @@ -84,7 +84,7 @@ not supported."# let redirect_stdout = call.redirect_stdout; let redirect_stderr = call.redirect_stderr; Ok(input - .into_iter() + .into_iter_strict(span)? .enumerate() .filter_map(move |(idx, value)| { stack.with_env(&orig_env_vars, &orig_env_hidden); diff --git a/crates/nu-command/tests/commands/append.rs b/crates/nu-command/tests/commands/append.rs index 4851a60f59..4d9a5246f4 100644 --- a/crates/nu-command/tests/commands/append.rs +++ b/crates/nu-command/tests/commands/append.rs @@ -13,3 +13,10 @@ fn adds_a_row_to_the_end() { 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")); +} diff --git a/crates/nu-command/tests/commands/drop.rs b/crates/nu-command/tests/commands/drop.rs index 8500b284bb..24ae7fe8c2 100644 --- a/crates/nu-command/tests/commands/drop.rs +++ b/crates/nu-command/tests/commands/drop.rs @@ -65,7 +65,7 @@ fn rows() { #[test] 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"); } @@ -90,3 +90,10 @@ fn nth_missing_first_argument() { 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")); +} diff --git a/crates/nu-command/tests/commands/last.rs b/crates/nu-command/tests/commands/last.rs index 92c7fc8884..d9eb651648 100644 --- a/crates/nu-command/tests/commands/last.rs +++ b/crates/nu-command/tests/commands/last.rs @@ -58,7 +58,7 @@ fn requests_more_rows_than_table_has() { let actual = nu!( cwd: ".", pipeline( 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")); } + +#[test] +fn fail_on_non_iterator() { + let actual = nu!(cwd: ".", pipeline("1 | last")); + + assert!(actual.err.contains("only_supports_this_input_type")); +} diff --git a/crates/nu-command/tests/commands/prepend.rs b/crates/nu-command/tests/commands/prepend.rs index c062855767..f9c2a97472 100644 --- a/crates/nu-command/tests/commands/prepend.rs +++ b/crates/nu-command/tests/commands/prepend.rs @@ -27,3 +27,10 @@ fn adds_a_row_to_the_beginning() { 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")); +} diff --git a/crates/nu-command/tests/commands/reverse.rs b/crates/nu-command/tests/commands/reverse.rs index 70f037d006..e41459800f 100644 --- a/crates/nu-command/tests/commands/reverse.rs +++ b/crates/nu-command/tests/commands/reverse.rs @@ -1,4 +1,4 @@ -use nu_test_support::nu; +use nu_test_support::{nu, pipeline}; #[test] fn can_get_reverse_first() { @@ -9,3 +9,10 @@ fn can_get_reverse_first() { 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")); +} diff --git a/crates/nu-command/tests/commands/skip/skip_.rs b/crates/nu-command/tests/commands/skip/skip_.rs index e931be7195..ac461523b5 100644 --- a/crates/nu-command/tests/commands/skip/skip_.rs +++ b/crates/nu-command/tests/commands/skip/skip_.rs @@ -5,12 +5,19 @@ fn binary_skip() { let actual = nu!( cwd: "tests/fixtures/formats", pipeline( r#" - open sample_data.ods --raw | - skip 2 | - take 2 | + open sample_data.ods --raw | + skip 2 | + take 2 | into int "# )); 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")); +} diff --git a/crates/nu-command/tests/commands/skip/until.rs b/crates/nu-command/tests/commands/skip/until.rs index 39f0c591d5..73260672dd 100644 --- a/crates/nu-command/tests/commands/skip/until.rs +++ b/crates/nu-command/tests/commands/skip/until.rs @@ -49,3 +49,10 @@ fn condition_is_met() { 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")); +} diff --git a/crates/nu-command/tests/commands/skip/while_.rs b/crates/nu-command/tests/commands/skip/while_.rs index a8cd5adc3a..817cf90952 100644 --- a/crates/nu-command/tests/commands/skip/while_.rs +++ b/crates/nu-command/tests/commands/skip/while_.rs @@ -49,3 +49,10 @@ fn condition_is_met() { 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")); +} diff --git a/crates/nu-command/tests/commands/sort_by.rs b/crates/nu-command/tests/commands/sort_by.rs index 7288b144ce..914f29e610 100644 --- a/crates/nu-command/tests/commands/sort_by.rs +++ b/crates/nu-command/tests/commands/sort_by.rs @@ -119,3 +119,10 @@ fn no_column_specified_fails() { 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")); +} diff --git a/crates/nu-command/tests/commands/str_/mod.rs b/crates/nu-command/tests/commands/str_/mod.rs index 7a05c02317..1b592bce7d 100644 --- a/crates/nu-command/tests/commands/str_/mod.rs +++ b/crates/nu-command/tests/commands/str_/mod.rs @@ -123,7 +123,7 @@ fn converts_to_int() { let actual = nu!( cwd: "tests/fixtures/formats", pipeline( r#" - echo '{number_as_string: "1"}' + echo '[{number_as_string: "1"}]' | from json | into int number_as_string | rename number diff --git a/crates/nu-command/tests/commands/take/rows.rs b/crates/nu-command/tests/commands/take/rows.rs index e5819bfe60..d50a72a765 100644 --- a/crates/nu-command/tests/commands/take/rows.rs +++ b/crates/nu-command/tests/commands/take/rows.rs @@ -51,7 +51,7 @@ fn fails_on_string() { "# )); - assert!(actual.err.contains("pipeline_mismatch")); + assert!(actual.err.contains("only_supports_this_input_type")); } #[test] diff --git a/crates/nu-command/tests/commands/take/until.rs b/crates/nu-command/tests/commands/take/until.rs index dd95f58efe..d7ba73d442 100644 --- a/crates/nu-command/tests/commands/take/until.rs +++ b/crates/nu-command/tests/commands/take/until.rs @@ -50,3 +50,10 @@ fn condition_is_met() { 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")); +} diff --git a/crates/nu-command/tests/commands/take/while_.rs b/crates/nu-command/tests/commands/take/while_.rs index 1899055c71..c54983f30f 100644 --- a/crates/nu-command/tests/commands/take/while_.rs +++ b/crates/nu-command/tests/commands/take/while_.rs @@ -49,3 +49,10 @@ fn condition_is_met() { 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")); +} diff --git a/crates/nu-command/tests/commands/where_.rs b/crates/nu-command/tests/commands/where_.rs index 6841144dc5..c9bbbee2ef 100644 --- a/crates/nu-command/tests/commands/where_.rs +++ b/crates/nu-command/tests/commands/where_.rs @@ -188,3 +188,10 @@ fn contains_operator() { 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")); +} diff --git a/crates/nu-protocol/src/pipeline_data.rs b/crates/nu-protocol/src/pipeline_data.rs index 3ad07cfb16..c06cb3c55a 100644 --- a/crates/nu-protocol/src/pipeline_data.rs +++ b/crates/nu-protocol/src/pipeline_data.rs @@ -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 { + 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>) -> PipelineIterator { let mut iter = self.into_iter(); diff --git a/crates/nu-protocol/src/shell_error.rs b/crates/nu-protocol/src/shell_error.rs index ee212ae02d..d6c48402ca 100644 --- a/crates/nu-protocol/src/shell_error.rs +++ b/crates/nu-protocol/src/shell_error.rs @@ -51,8 +51,8 @@ pub enum ShellError { #[label("value originates from here")] Span, ), - #[error("Pipeline mismatch.")] - #[diagnostic(code(nu::shell::pipeline_mismatch), url(docsrs))] + #[error("Only supports for specific input types.")] + #[diagnostic(code(nu::shell::only_supports_this_input_type), url(docsrs))] OnlySupportsThisInputType( String, String,