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
This commit is contained in:
Leon 2022-11-02 04:36:54 +10:00 committed by GitHub
parent e46d610f77
commit 43aec8cdbe
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 168 additions and 24 deletions

View file

@ -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<Example> {
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<PipelineData, ShellError> {
// 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,
);

View file

@ -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<Example> {
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,
);

View file

@ -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");
}

View file

@ -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");
}

View file

@ -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");
}