diff --git a/crates/nu-command/src/core_commands/do_.rs b/crates/nu-command/src/core_commands/do_.rs index d87148fa1a..ab4c97d859 100644 --- a/crates/nu-command/src/core_commands/do_.rs +++ b/crates/nu-command/src/core_commands/do_.rs @@ -1,4 +1,4 @@ -use nu_engine::{eval_block, eval_expression}; +use nu_engine::{eval_block, CallExt}; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EvaluationContext}; use nu_protocol::{Signature, SyntaxShape, Value}; @@ -15,11 +15,14 @@ impl Command for Do { } fn signature(&self) -> nu_protocol::Signature { - Signature::build("do").desc(self.usage()).required( - "block", - SyntaxShape::Block(Some(vec![])), - "the block to run", - ) + Signature::build("do") + .desc(self.usage()) + .required( + "block", + SyntaxShape::Block(Some(vec![])), + "the block to run", + ) + .rest("rest", SyntaxShape::Any, "the parameter(s) for the block") } fn run( @@ -28,17 +31,54 @@ impl Command for Do { call: &Call, input: Value, ) -> Result { - let block = &call.positional[0]; + let block_id = call.positional[0] + .as_block() + .expect("internal error: expected block"); + let rest: Vec = call.rest(context, 1)?; - let out = eval_expression(context, block)?; + let engine_state = context.engine_state.borrow(); + let block = engine_state.get_block(block_id); - match out { - Value::Block { val: block_id, .. } => { - let engine_state = context.engine_state.borrow(); - let block = engine_state.get_block(block_id); - eval_block(context, block, input) + let state = context.enter_scope(); + + let params: Vec<_> = block + .signature + .required_positional + .iter() + .chain(block.signature.optional_positional.iter()) + .collect(); + + for param in params.iter().zip(&rest) { + if let Some(var_id) = param.0.var_id { + state.add_var(var_id, param.1.clone()) } - _ => Ok(Value::nothing()), } + + if let Some(param) = &block.signature.rest_positional { + if rest.len() > params.len() { + let mut rest_items = vec![]; + + for r in rest.into_iter().skip(params.len()) { + rest_items.push(r); + } + + let span = if let Some(rest_item) = rest_items.first() { + rest_item.span()? + } else { + call.head + }; + + state.add_var( + param + .var_id + .expect("Internal error: rest positional parameter lacks var_id"), + Value::List { + vals: rest_items, + span, + }, + ) + } + } + eval_block(&state, block, input) } } diff --git a/src/tests.rs b/src/tests.rs index fbfbf67d99..8e2ecd2742 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -722,3 +722,8 @@ fn flag_param_value() -> TestResult { "155", ) } + +#[test] +fn do_rest_args() -> TestResult { + run_test(r#"(do { |...rest| $rest } 1 2).1 + 10"#, "12") +}