From 5f04bbbb8bac490732fcfd297112a4e47bb2fbf5 Mon Sep 17 00:00:00 2001 From: 132ikl <132@ikl.sh> Date: Fri, 29 Nov 2024 15:45:27 -0500 Subject: [PATCH] Make length only operate on supported input types (#14475) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description Before this PR, `length` did not check its input type at run-time, so it would attempt to calculate a length for any input with indeterminate type (e.g., `echo` which has an `any` output type). This PR makes `length` only work on the types specifically supported in its input/output types (list/table, binary, and nothing), making the behavior the same at parse-time and at run-time. Fixes #14462 # User-Facing Changes Length will error if passed an unsupported type: Before (only caught at parse-time): ```nushell "hello" | length Error: nu::parser::input_type_mismatch × Command does not support string input. ╭─[entry #2:1:11] 1 │ "hello" | length · ───┬── · ╰── command doesn't support string input ╰──── echo "hello" | length # => 1 ``` After (caught at parse-time and run-time): ```nushell "hello" | length Error: nu::parser::input_type_mismatch × Command does not support string input. ╭─[entry #22:1:11] 1 │ "hello" | length · ───┬── · ╰── command doesn't support string input ╰──── echo "hello" | length Error: nu::shell::only_supports_this_input_type × Input type not supported. ╭─[entry #23:1:6] 1 │ echo "hello" | length · ───┬─── ───┬── · │ ╰── only list, table, binary, and nothing input data is supported · ╰── input type: string ╰──── ``` --- crates/nu-command/src/filters/length.rs | 43 ++++++++++++------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/crates/nu-command/src/filters/length.rs b/crates/nu-command/src/filters/length.rs index 3d7b739ef2..b643c4dfea 100644 --- a/crates/nu-command/src/filters/length.rs +++ b/crates/nu-command/src/filters/length.rs @@ -19,6 +19,7 @@ impl Command for Length { .input_output_types(vec![ (Type::List(Box::new(Type::Any)), Type::Int), (Type::Binary, Type::Int), + (Type::Nothing, Type::Int), ]) .category(Category::Filters) } @@ -54,6 +55,11 @@ impl Command for Length { example: "0x[01 02] | length", result: Some(Value::test_int(2)), }, + Example { + description: "Count the length a null value", + example: "null | length", + result: Some(Value::test_int(0)), + }, ] } } @@ -61,23 +67,19 @@ impl Command for Length { fn length_row(call: &Call, input: PipelineData) -> Result { let span = input.span().unwrap_or(call.head); match input { - PipelineData::Value(Value::Nothing { .. }, ..) => { + PipelineData::Empty | PipelineData::Value(Value::Nothing { .. }, ..) => { Ok(Value::int(0, call.head).into_pipeline_data()) } - // I added this here because input_output_type() wasn't catching a record - // being sent in as input from echo. e.g. "echo {a:1 b:2} | length" - PipelineData::Value(Value::Record { .. }, ..) => { - Err(ShellError::OnlySupportsThisInputType { - exp_input_type: "list, and table".into(), - wrong_type: "record".into(), - dst_span: call.head, - src_span: span, - }) - } PipelineData::Value(Value::Binary { val, .. }, ..) => { Ok(Value::int(val.len() as i64, call.head).into_pipeline_data()) } - PipelineData::ByteStream(stream, _) if stream.type_().is_binary_coercible() => { + PipelineData::Value(Value::List { vals, .. }, ..) => { + Ok(Value::int(vals.len() as i64, call.head).into_pipeline_data()) + } + PipelineData::ListStream(stream, ..) => { + Ok(Value::int(stream.into_iter().count() as i64, call.head).into_pipeline_data()) + } + PipelineData::ByteStream(stream, ..) if stream.type_().is_binary_coercible() => { Ok(Value::int( match stream.reader() { Some(r) => r.bytes().count() as i64, @@ -87,17 +89,12 @@ fn length_row(call: &Call, input: PipelineData) -> Result { - let mut count: i64 = 0; - // Check for and propagate errors - for value in input.into_iter() { - if let Value::Error { error, .. } = value { - return Err(*error); - } - count += 1 - } - Ok(Value::int(count, call.head).into_pipeline_data()) - } + _ => Err(ShellError::OnlySupportsThisInputType { + exp_input_type: "list, table, binary, and nothing".into(), + wrong_type: input.get_type().to_string(), + dst_span: call.head, + src_span: span, + }), } }