From a70867ed3451a2779be5bb90f7f49b76094f1e60 Mon Sep 17 00:00:00 2001 From: Bahex <17417311+Bahex@users.noreply.github.com> Date: Mon, 5 May 2025 21:36:44 +0300 Subject: [PATCH 1/3] feat(where): Support closures stored in variables --- crates/nu-command/src/filters/where_.rs | 26 ++++++++++++++++++---- crates/nu-command/tests/commands/where_.rs | 13 +++++++++++ crates/nu-parser/src/parser.rs | 10 +++++++++ 3 files changed, 45 insertions(+), 4 deletions(-) diff --git a/crates/nu-command/src/filters/where_.rs b/crates/nu-command/src/filters/where_.rs index cf40639172..2506c90e50 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) @@ -125,9 +128,24 @@ not supported."# description: "same as above but with regex only", 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..837b715c3f 100644 --- a/crates/nu-command/tests/commands/where_.rs +++ b/crates/nu-command/tests/commands/where_.rs @@ -209,3 +209,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 06a38afe7e..3f9d51fb68 100644 --- a/crates/nu-parser/src/parser.rs +++ b/crates/nu-parser/src/parser.rs @@ -3613,6 +3613,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() != Some(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(); From ac36b4a24757b942cbdf666d4e4cbb21f614a9d0 Mon Sep 17 00:00:00 2001 From: Bahex <17417311+Bahex@users.noreply.github.com> Date: Tue, 6 May 2025 11:27:34 +0300 Subject: [PATCH 2/3] fix: Further limit expressions that are accepted as a closure - Only variables and cell-path accesses on variables are allowed as closures. These can't be `$it` or a path on `$it` - Any other expression falls back to the usual RowCondition expression --- crates/nu-parser/src/parser.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/nu-parser/src/parser.rs b/crates/nu-parser/src/parser.rs index 3f9d51fb68..71510466b6 100644 --- a/crates/nu-parser/src/parser.rs +++ b/crates/nu-parser/src/parser.rs @@ -3613,7 +3613,7 @@ 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() != Some(var_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; From 50e360d898d12cd7c00274f18130e410da5ab1ce Mon Sep 17 00:00:00 2001 From: Bahex <17417311+Bahex@users.noreply.github.com> Date: Tue, 6 May 2025 12:11:46 +0300 Subject: [PATCH 3/3] test(where): test for possible regression after closure support --- crates/nu-command/tests/commands/where_.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/crates/nu-command/tests/commands/where_.rs b/crates/nu-command/tests/commands/where_.rs index 837b715c3f..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");