From e0eb29f1611637fc150b083132824f5948aa4ae3 Mon Sep 17 00:00:00 2001 From: Bahex Date: Tue, 13 May 2025 22:24:45 +0300 Subject: [PATCH] feat(where): Support stored closure (#15697) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description - `where` used to be able to filter using stored closures at some point using a flag. > #5955 - This was later removed and `filter` command added to cover the use case. > #7365 This PR once again allows using `where` with closures stored in variables. ```nushell let cond = { $in mod 2 == 0 } 1..10 | where $cond # => ╭───┬────╮ # => │ 0 │ 2 │ # => │ 1 │ 4 │ # => │ 2 │ 6 │ # => │ 3 │ 8 │ # => │ 4 │ 10 │ # => ╰───┴────╯ let nested = {cond: { $in mod 2 == 0 }} 1..10 | where $nested.cond # => ╭───┬────╮ # => │ 0 │ 2 │ # => │ 1 │ 4 │ # => │ 2 │ 6 │ # => │ 3 │ 8 │ # => │ 4 │ 10 │ # => ╰───┴────╯ ``` This does not interfere with using `$it` or one of its fields as the condition. ```nushell [[name state]; [foo true] [bar false] [baz true] [quox false]] | where $it.state # => ╭───┬──────┬───────╮ # => │ # │ name │ state │ # => ├───┼──────┼───────┤ # => │ 0 │ foo │ true │ # => │ 1 │ baz │ true │ # => ╰───┴──────┴───────╯ ``` This change obsoletes `filter`, deprecate it in the future? # User-Facing Changes # Tests + Formatting Added examples and tests. - :green_circle: toolkit fmt - :green_circle: toolkit clippy - :green_circle: toolkit test - :green_circle: toolkit test stdlib # After Submitting --------- Co-authored-by: Bahex <17417311+Bahex@users.noreply.github.com> --- crates/nu-command/src/filters/where_.rs | 20 +++++++++++++++++++- crates/nu-command/tests/commands/where_.rs | 19 +++++++++++++++++++ crates/nu-parser/src/parser.rs | 10 ++++++++++ 3 files changed, 48 insertions(+), 1 deletion(-) diff --git a/crates/nu-command/src/filters/where_.rs b/crates/nu-command/src/filters/where_.rs index 7859448231..eb43998358 100644 --- a/crates/nu-command/src/filters/where_.rs +++ b/crates/nu-command/src/filters/where_.rs @@ -35,7 +35,10 @@ not supported."# ]) .required( "row_condition", - SyntaxShape::RowCondition, + SyntaxShape::OneOf(vec![ + SyntaxShape::RowCondition, + SyntaxShape::Closure(Some(vec![SyntaxShape::Any])), + ]), "Filter condition.", ) .allow_variants_without_examples(true) @@ -122,6 +125,21 @@ not supported."# example: "ls | where name =~ '(?i)readme'", result: None, }, + Example { + description: "Filter rows of a table according to a stored condition", + example: "let cond = {|x| $x.a > 1}; [{a: 1} {a: 2}] | where $cond", + result: Some(Value::test_list(vec![Value::test_record(record! { + "a" => Value::test_int(2), + })])), + }, + Example { + description: "List all numbers above 3, using an existing closure condition", + example: "let a = {$in > 3}; [1, 2, 5, 6] | where $a", + result: Some(Value::test_list(vec![ + Value::test_int(5), + Value::test_int(6), + ])), + }, ] } } diff --git a/crates/nu-command/tests/commands/where_.rs b/crates/nu-command/tests/commands/where_.rs index 7c1fa37f23..6ce540635a 100644 --- a/crates/nu-command/tests/commands/where_.rs +++ b/crates/nu-command/tests/commands/where_.rs @@ -28,6 +28,12 @@ fn where_inside_block_works() { assert_eq!(actual.out, "closure"); } +#[test] +fn it_inside_complex_subexpression() { + let actual = nu!(r#"1..10 | where [($it * $it)].0 > 40 | to nuon"#); + assert_eq!(actual.out, r#"[7, 8, 9, 10]"#) +} + #[test] fn filters_with_0_arity_block() { let actual = nu!("[1 2 3 4] | where {|| $in < 3 } | to nuon"); @@ -209,3 +215,16 @@ fn has_operator() { let actual = nu!(r#"{foo: 1} has bar "#); assert_eq!(actual.out, "false"); } + +#[test] +fn stored_condition() { + let actual = nu!(r#"let cond = { $in mod 2 == 0 }; 1..10 | where $cond | to nuon"#); + assert_eq!(actual.out, r#"[2, 4, 6, 8, 10]"#) +} + +#[test] +fn nested_stored_condition() { + let actual = + nu!(r#"let nested = {cond: { $in mod 2 == 0 }}; 1..10 | where $nested.cond | to nuon"#); + assert_eq!(actual.out, r#"[2, 4, 6, 8, 10]"#) +} diff --git a/crates/nu-parser/src/parser.rs b/crates/nu-parser/src/parser.rs index dc022a75df..a458fb85ed 100644 --- a/crates/nu-parser/src/parser.rs +++ b/crates/nu-parser/src/parser.rs @@ -3609,6 +3609,16 @@ pub fn parse_row_condition(working_set: &mut StateWorkingSet, spans: &[Span]) -> let block_id = match expression.expr { Expr::Block(block_id) => block_id, Expr::Closure(block_id) => block_id, + Expr::FullCellPath(ref box_fcp) if box_fcp.head.as_var().is_some_and(|id| id != var_id) => { + let mut expression = expression; + expression.ty = Type::Any; + return expression; + } + Expr::Var(arg_var_id) if arg_var_id != var_id => { + let mut expression = expression; + expression.ty = Type::Any; + return expression; + } _ => { // We have an expression, so let's convert this into a block. let mut block = Block::new();