From 43aec8cdbef19c8e50d2f147a1ff4669ac2e1014 Mon Sep 17 00:00:00 2001 From: Leon Date: Wed, 2 Nov 2022 04:36:54 +1000 Subject: [PATCH] Fix `$in` in blocks given to `any` and `all` (#6951) * Fix $in in blocks given to `any` and `all` (closes #6917) * Fix help message typos * Fix tests ($in doesn't work in examples?!) * Fix formatting --- crates/nu-command/src/filters/all.rs | 30 +++++++++---- crates/nu-command/src/filters/any.rs | 28 ++++++++---- crates/nu-command/tests/commands/all.rs | 40 +++++++++++++++++ crates/nu-command/tests/commands/any.rs | 40 +++++++++++++++++ crates/nu-command/tests/commands/empty.rs | 54 ++++++++++++++++++++--- 5 files changed, 168 insertions(+), 24 deletions(-) diff --git a/crates/nu-command/src/filters/all.rs b/crates/nu-command/src/filters/all.rs index 6e3ec49da..e81c9f938 100644 --- a/crates/nu-command/src/filters/all.rs +++ b/crates/nu-command/src/filters/all.rs @@ -18,7 +18,7 @@ impl Command for All { .required( "predicate", SyntaxShape::RowCondition, - "the predicate expression that must evaluate to a boolean", + "the expression, or block, that must evaluate to a boolean", ) .category(Category::Filters) } @@ -34,18 +34,26 @@ impl Command for All { fn examples(&self) -> Vec { vec![ Example { - description: "Find if services are running", - example: "echo [[status]; [UP] [UP]] | all status == UP", + description: "Check if each row's status is the string 'UP'", + example: "[[status]; [UP] [UP]] | all status == UP", result: Some(Value::test_bool(true)), }, Example { - description: "Check that all values are even", - example: "echo [2 4 6 8] | all ($it mod 2) == 0", + description: + "Check that all of the values are even, using the built-in $it variable", + example: "[2 4 6 8] | all ($it mod 2) == 0", + result: Some(Value::test_bool(true)), + }, + Example { + description: "Check that all of the values are even, using a block", + example: "[2 4 6 8] | all {|e| $e mod 2 == 0 }", result: Some(Value::test_bool(true)), }, ] } - + // This is almost entirely a copy-paste of `any`'s run(), so make sure any changes to this are + // reflected in the other!! (Or, you could figure out a way for both of them to use + // the same function...) fn run( &self, engine_state: &EngineState, @@ -53,7 +61,6 @@ impl Command for All { call: &Call, input: PipelineData, ) -> Result { - // let predicate = &call.positional[0]; let span = call.head; let capture_block: CaptureBlock = call.req(engine_state, stack, 0)?; @@ -63,19 +70,24 @@ impl Command for All { let var_id = block.signature.get_positional(0).and_then(|arg| arg.var_id); let mut stack = stack.captures_to_stack(&capture_block.captures); + let orig_env_vars = stack.env_vars.clone(); + let orig_env_hidden = stack.env_hidden.clone(); + let ctrlc = engine_state.ctrlc.clone(); let engine_state = engine_state.clone(); for value in input.into_interruptible_iter(ctrlc) { + stack.with_env(&orig_env_vars, &orig_env_hidden); + if let Some(var_id) = var_id { - stack.add_var(var_id, value); + stack.add_var(var_id, value.clone()); } let eval = eval_block( &engine_state, &mut stack, block, - PipelineData::new(span), + value.into_pipeline_data(), call.redirect_stdout, call.redirect_stderr, ); diff --git a/crates/nu-command/src/filters/any.rs b/crates/nu-command/src/filters/any.rs index 4a641136d..e8de50cff 100644 --- a/crates/nu-command/src/filters/any.rs +++ b/crates/nu-command/src/filters/any.rs @@ -18,7 +18,7 @@ impl Command for Any { .required( "predicate", SyntaxShape::RowCondition, - "the predicate expression that should return a boolean", + "the expression, or block, that should return a boolean", ) .category(Category::Filters) } @@ -34,18 +34,25 @@ impl Command for Any { fn examples(&self) -> Vec { vec![ Example { - description: "Find if a service is not running", - example: "echo [[status]; [UP] [DOWN] [UP]] | any status == DOWN", + description: "Check if any row's status is the string 'DOWN'", + example: "[[status]; [UP] [DOWN] [UP]] | any status == DOWN", result: Some(Value::test_bool(true)), }, Example { - description: "Check if any of the values is odd", - example: "echo [2 4 1 6 8] | any ($it mod 2) == 1", + description: "Check if any of the values is odd, using the built-in $it variable", + example: "[2 4 1 6 8] | any ($it mod 2) == 1", + result: Some(Value::test_bool(true)), + }, + Example { + description: "Check if any of the values are odd, using a block", + example: "[2 4 1 6 8] | any {|e| $e mod 2 == 1 }", result: Some(Value::test_bool(true)), }, ] } - + // This is almost entirely a copy-paste of `all`'s run(), so make sure any changes to this are + // reflected in the other!! Or, you could figure out a way for both of them to use + // the same function... fn run( &self, engine_state: &EngineState, @@ -62,19 +69,24 @@ impl Command for Any { let var_id = block.signature.get_positional(0).and_then(|arg| arg.var_id); let mut stack = stack.captures_to_stack(&capture_block.captures); + let orig_env_vars = stack.env_vars.clone(); + let orig_env_hidden = stack.env_hidden.clone(); + let ctrlc = engine_state.ctrlc.clone(); let engine_state = engine_state.clone(); for value in input.into_interruptible_iter(ctrlc) { + stack.with_env(&orig_env_vars, &orig_env_hidden); + if let Some(var_id) = var_id { - stack.add_var(var_id, value); + stack.add_var(var_id, value.clone()); } let eval = eval_block( &engine_state, &mut stack, block, - PipelineData::new(span), + value.into_pipeline_data(), call.redirect_stdout, call.redirect_stderr, ); diff --git a/crates/nu-command/tests/commands/all.rs b/crates/nu-command/tests/commands/all.rs index 2f9c231c2..a4bbd9c35 100644 --- a/crates/nu-command/tests/commands/all.rs +++ b/crates/nu-command/tests/commands/all.rs @@ -67,3 +67,43 @@ fn checks_if_all_returns_error_with_invalid_command() { assert!(actual.err.contains("can't run executable") || actual.err.contains("did you mean")); } + +#[test] +fn works_with_1_param_blocks() { + let actual = nu!( + cwd: ".", pipeline( + r#"[1 2 3] | all {|e| print $e | true }"# + )); + + assert_eq!(actual.out, "123true"); +} + +#[test] +fn works_with_0_param_blocks() { + let actual = nu!( + cwd: ".", pipeline( + r#"[1 2 3] | all { print $in | true }"# + )); + + assert_eq!(actual.out, "123true"); +} + +#[test] +fn early_exits_with_1_param_blocks() { + let actual = nu!( + cwd: ".", pipeline( + r#"[1 2 3] | all {|e| print $e | false }"# + )); + + assert_eq!(actual.out, "1false"); +} + +#[test] +fn early_exits_with_0_param_blocks() { + let actual = nu!( + cwd: ".", pipeline( + r#"[1 2 3] | all { print $in | false }"# + )); + + assert_eq!(actual.out, "1false"); +} diff --git a/crates/nu-command/tests/commands/any.rs b/crates/nu-command/tests/commands/any.rs index f08ca1086..5156816cf 100644 --- a/crates/nu-command/tests/commands/any.rs +++ b/crates/nu-command/tests/commands/any.rs @@ -43,3 +43,43 @@ fn checks_if_any_returns_error_with_invalid_command() { assert!(actual.err.contains("can't run executable") || actual.err.contains("did you mean")); } + +#[test] +fn works_with_1_param_blocks() { + let actual = nu!( + cwd: ".", pipeline( + r#"[1 2 3] | any {|e| print $e | false }"# + )); + + assert_eq!(actual.out, "123false"); +} + +#[test] +fn works_with_0_param_blocks() { + let actual = nu!( + cwd: ".", pipeline( + r#"[1 2 3] | any { print $in | false }"# + )); + + assert_eq!(actual.out, "123false"); +} + +#[test] +fn early_exits_with_1_param_blocks() { + let actual = nu!( + cwd: ".", pipeline( + r#"[1 2 3] | any {|e| print $e | true }"# + )); + + assert_eq!(actual.out, "1true"); +} + +#[test] +fn early_exits_with_0_param_blocks() { + let actual = nu!( + cwd: ".", pipeline( + r#"[1 2 3] | any { print $in | true }"# + )); + + assert_eq!(actual.out, "1true"); +} diff --git a/crates/nu-command/tests/commands/empty.rs b/crates/nu-command/tests/commands/empty.rs index 55852b8bc..45b4bbf9b 100644 --- a/crates/nu-command/tests/commands/empty.rs +++ b/crates/nu-command/tests/commands/empty.rs @@ -5,17 +5,57 @@ fn reports_emptiness() { let actual = nu!( cwd: ".", pipeline( r#" - echo [[are_empty]; - [([[check]; [[]] ])] - [([[check]; [""] ])] - [([[check]; [{}] ])] - ] - | get are_empty + [[] '' {} null] | all { - is-empty check + is-empty } "# )); assert_eq!(actual.out, "true"); } + +#[test] +fn reports_nonemptiness() { + let actual = nu!( + cwd: ".", pipeline( + r#" + [[1] ' ' {a:1} 0] + | any { + is-empty + } + "# + )); + + assert_eq!(actual.out, "false"); +} + +#[test] +fn reports_emptiness_by_columns() { + let actual = nu!( + cwd: ".", pipeline( + r#" + [{a:1 b:null c:null} {a:2 b:null c:null}] + | any { + is-empty b c + } + "# + )); + + assert_eq!(actual.out, "true"); +} + +#[test] +fn reports_nonemptiness_by_columns() { + let actual = nu!( + cwd: ".", pipeline( + r#" + [{a:1 b:null c:3} {a:null b:5 c:2}] + | any { + is-empty a b + } + "# + )); + + assert_eq!(actual.out, "false"); +}