mirror of
https://github.com/nushell/nushell
synced 2024-12-26 13:03:07 +00:00
Add rest support to blocks (#2962)
This commit is contained in:
parent
a3be6affa4
commit
a4b8d4a098
2 changed files with 83 additions and 9 deletions
|
@ -8,7 +8,7 @@ use nu_errors::ShellError;
|
|||
use nu_parser::ParserScope;
|
||||
use nu_protocol::hir::Block;
|
||||
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
|
||||
use nu_source::{b, DebugDocBuilder, PrettyDebugWithSource, Tag};
|
||||
use nu_source::{b, DebugDocBuilder, PrettyDebugWithSource, Span, Tag};
|
||||
use nu_stream::{OutputStream, ToOutputStream};
|
||||
use std::sync::Arc;
|
||||
|
||||
|
@ -67,16 +67,54 @@ impl WholeStreamCommand for Block {
|
|||
let input = args.input;
|
||||
ctx.scope.enter_scope();
|
||||
if let Some(args) = evaluated.args.positional {
|
||||
// FIXME: do not do this
|
||||
for arg in args.into_iter().zip(self.params.positional.iter()) {
|
||||
let name = arg.1 .0.name();
|
||||
|
||||
if name.starts_with('$') {
|
||||
ctx.scope.add_var(name, arg.0);
|
||||
} else {
|
||||
ctx.scope.add_var(format!("${}", name), arg.0);
|
||||
let mut args_iter = args.into_iter().peekable();
|
||||
let mut params_iter = self.params.positional.iter();
|
||||
loop {
|
||||
match (args_iter.peek(), params_iter.next()) {
|
||||
(Some(_), Some(param)) => {
|
||||
let name = param.0.name();
|
||||
// we just checked the peek above, so this should be infallible
|
||||
if let Some(arg) = args_iter.next() {
|
||||
if name.starts_with('$') {
|
||||
ctx.scope.add_var(name.to_string(), arg);
|
||||
} else {
|
||||
ctx.scope.add_var(format!("${}", name), arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
(Some(arg), None) => {
|
||||
if block.params.rest_positional.is_none() {
|
||||
ctx.scope.exit_scope();
|
||||
return Err(ShellError::labeled_error(
|
||||
"Unexpected argument to command",
|
||||
"unexpected argument",
|
||||
arg.tag.span,
|
||||
));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
if block.params.rest_positional.is_some() {
|
||||
let elements: Vec<_> = args_iter.collect();
|
||||
let start = if let Some(first) = elements.first() {
|
||||
first.tag.span.start()
|
||||
} else {
|
||||
0
|
||||
};
|
||||
let end = if let Some(last) = elements.last() {
|
||||
last.tag.span.end()
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
ctx.scope.add_var(
|
||||
"$rest",
|
||||
UntaggedValue::Table(elements).into_value(Span::new(start, end)),
|
||||
);
|
||||
}
|
||||
}
|
||||
if let Some(args) = evaluated.args.named {
|
||||
for named in &block.params.named {
|
||||
|
|
|
@ -428,6 +428,42 @@ fn run_broken_inner_custom_command() {
|
|||
assert!(actual.err.contains("not found"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn run_custom_command_with_rest() {
|
||||
let actual = nu!(
|
||||
cwd: ".",
|
||||
r#"
|
||||
def rest-me [...rest: string] { echo $rest.1 $rest.0}; rest-me "hello" "world" | to json
|
||||
"#
|
||||
);
|
||||
|
||||
assert_eq!(actual.out, r#"["world","hello"]"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn run_custom_command_with_rest_and_arg() {
|
||||
let actual = nu!(
|
||||
cwd: ".",
|
||||
r#"
|
||||
def rest-me-with-arg [name: string, ...rest: string] { echo $rest.1 $rest.0 $name}; rest-me-with-arg "hello" "world" "yay" | to json
|
||||
"#
|
||||
);
|
||||
|
||||
assert_eq!(actual.out, r#"["yay","world","hello"]"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn run_custom_command_with_rest_and_flag() {
|
||||
let actual = nu!(
|
||||
cwd: ".",
|
||||
r#"
|
||||
def rest-me-with-flag [--name: string, ...rest: string] { echo $rest.1 $rest.0 $name}; rest-me-with-flag "hello" "world" --name "yay" | to json
|
||||
"#
|
||||
);
|
||||
|
||||
assert_eq!(actual.out, r#"["world","hello","yay"]"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_variable() {
|
||||
let actual = nu!(
|
||||
|
|
Loading…
Reference in a new issue