Numbered each (#2100)

* Add --numbered to each

* Fix example tester and add numbered each
This commit is contained in:
Jonathan Turner 2020-07-03 01:43:55 -07:00 committed by GitHub
parent 5cafead4a4
commit de8e2841a0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 79 additions and 29 deletions

View File

@ -40,7 +40,7 @@ impl WholeStreamCommand for Compact {
vec![ vec![
Example { Example {
description: "Filter out all null entries in a list", description: "Filter out all null entries in a list",
example: "echo [1 2 $null 3 $null $null] | compact target", example: "echo [1 2 $null 3 $null $null] | compact",
result: Some(vec![ result: Some(vec![
UntaggedValue::int(1).into(), UntaggedValue::int(1).into(),
UntaggedValue::int(2).into(), UntaggedValue::int(2).into(),

View File

@ -7,14 +7,16 @@ use futures::stream::once;
use nu_errors::ShellError; use nu_errors::ShellError;
use nu_protocol::{ use nu_protocol::{
hir::Block, hir::Expression, hir::SpannedExpression, hir::Synthetic, Scope, Signature, hir::Block, hir::Expression, hir::SpannedExpression, hir::Synthetic, Scope, Signature,
SyntaxShape, UntaggedValue, Value, SyntaxShape, TaggedDictBuilder, UntaggedValue, Value,
}; };
use nu_source::Tagged;
pub struct Each; pub struct Each;
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct EachArgs { pub struct EachArgs {
block: Block, block: Block,
numbered: Tagged<bool>,
} }
#[async_trait] #[async_trait]
@ -24,10 +26,12 @@ impl WholeStreamCommand for Each {
} }
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("each").required( Signature::build("each")
"block", .required("block", SyntaxShape::Block, "the block to run on each row")
SyntaxShape::Block, .switch(
"the block to run on each row", "numbered",
"returned a numbered item ($it.index and $it.item)",
Some('n'),
) )
} }
@ -45,6 +49,11 @@ impl WholeStreamCommand for Each {
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
vec![ vec![
Example {
description: "Echo the sum of each row",
example: "echo [[1 2] [3 4]] | each { echo $it | math sum }",
result: None,
},
Example { Example {
description: "Echo the square of each integer", description: "Echo the square of each integer",
example: "echo [1 2 3] | each { echo $(= $it * $it) }", example: "echo [1 2 3] | each { echo $(= $it * $it) }",
@ -55,12 +64,10 @@ impl WholeStreamCommand for Each {
]), ]),
}, },
Example { Example {
description: "Echo the sum of each row", description: "Number each item and echo a message",
example: "echo [[1 2] [3 4]] | each { echo $it | math sum }", example:
result: Some(vec![ "echo ['bob' 'fred'] | each --numbered { echo `{{$it.index}} is {{$it.item}}` }",
UntaggedValue::int(3).into(), result: Some(vec![Value::from("0 is bob"), Value::from("1 is fred")]),
UntaggedValue::int(7).into(),
]),
}, },
] ]
} }
@ -111,12 +118,37 @@ async fn each(
let context = Arc::new(Context::from_raw(&raw_args, &registry)); let context = Arc::new(Context::from_raw(&raw_args, &registry));
let (each_args, input): (EachArgs, _) = raw_args.process(&registry).await?; let (each_args, input): (EachArgs, _) = raw_args.process(&registry).await?;
let block = Arc::new(each_args.block); let block = Arc::new(each_args.block);
if each_args.numbered.item {
Ok(input
.enumerate()
.then(move |input| {
let block = block.clone();
let scope = scope.clone();
let head = head.clone();
let context = context.clone();
let mut dict = TaggedDictBuilder::new(input.1.tag());
dict.insert_untagged("index", UntaggedValue::int(input.0));
dict.insert_value("item", input.1);
async {
match process_row(block, scope, head, context, dict.into_value()).await {
Ok(s) => s,
Err(e) => OutputStream::one(Err(e)),
}
}
})
.flatten()
.to_output_stream())
} else {
Ok(input Ok(input
.then(move |input| { .then(move |input| {
let block = block.clone(); let block = block.clone();
let scope = scope.clone(); let scope = scope.clone();
let head = head.clone(); let head = head.clone();
let context = context.clone(); let context = context.clone();
async { async {
match process_row(block, scope, head, context, input).await { match process_row(block, scope, head, context, input).await {
Ok(s) => s, Ok(s) => s,
@ -126,6 +158,7 @@ async fn each(
}) })
.flatten() .flatten()
.to_output_stream()) .to_output_stream())
}
} }
#[cfg(test)] #[cfg(test)]

View File

@ -6,7 +6,7 @@ use nu_protocol::hir::ClassifiedBlock;
use nu_protocol::{ShellTypeName, Value}; use nu_protocol::{ShellTypeName, Value};
use crate::commands::classified::block::run_block; use crate::commands::classified::block::run_block;
use crate::commands::{whole_stream_command, Echo}; use crate::commands::{whole_stream_command, BuildString, Echo};
use crate::context::Context; use crate::context::Context;
use crate::stream::InputStream; use crate::stream::InputStream;
use crate::WholeStreamCommand; use crate::WholeStreamCommand;
@ -14,8 +14,10 @@ use crate::WholeStreamCommand;
pub fn test(cmd: impl WholeStreamCommand + 'static) { pub fn test(cmd: impl WholeStreamCommand + 'static) {
let examples = cmd.examples(); let examples = cmd.examples();
let mut base_context = Context::basic().expect("could not create basic context"); let mut base_context = Context::basic().expect("could not create basic context");
base_context.add_commands(vec![ base_context.add_commands(vec![
whole_stream_command(Echo {}), whole_stream_command(Echo {}),
whole_stream_command(BuildString {}),
whole_stream_command(cmd), whole_stream_command(cmd),
]); ]);
@ -24,12 +26,27 @@ pub fn test(cmd: impl WholeStreamCommand + 'static) {
let block = parse_line(example.example, &mut ctx).expect("failed to parse example"); let block = parse_line(example.example, &mut ctx).expect("failed to parse example");
if let Some(expected) = example.result { if let Some(expected) = example.result {
let result = block_on(evaluate_block(block, &mut ctx)).expect("failed to run example"); let result = block_on(evaluate_block(block, &mut ctx)).expect("failed to run example");
let errors = ctx.get_errors();
assert!(
errors.is_empty(),
"errors while running command.\ncommand: {}\nerrors: {:?}",
example.example,
errors
);
assert!(expected.len() == result.len(), "example command produced unexpected number of results.\ncommand: {}\nexpected number: {}\nactual: {}",
example.example,
expected.len(),
result.len(),);
assert!( assert!(
expected expected
.iter() .iter()
.zip(result.iter()) .zip(result.iter())
.all(|(e, a)| values_equal(e, a)), .all(|(e, a)| values_equal(e, a)),
"example command produced unexpected result.\ncommand: {}\nexpected: {:?}\nactual:{:?}", "example command produced unexpected result.\ncommand: {}\nexpected: {:?}\nactual: {:?}",
example.example, example.example,
expected, expected,
result, result,