forked from extern/nushell
Add rest support to blocks (#2962)
This commit is contained in:
parent
a3be6affa4
commit
a4b8d4a098
@ -8,7 +8,7 @@ use nu_errors::ShellError;
|
|||||||
use nu_parser::ParserScope;
|
use nu_parser::ParserScope;
|
||||||
use nu_protocol::hir::Block;
|
use nu_protocol::hir::Block;
|
||||||
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
|
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 nu_stream::{OutputStream, ToOutputStream};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@ -67,17 +67,55 @@ impl WholeStreamCommand for Block {
|
|||||||
let input = args.input;
|
let input = args.input;
|
||||||
ctx.scope.enter_scope();
|
ctx.scope.enter_scope();
|
||||||
if let Some(args) = evaluated.args.positional {
|
if let Some(args) = evaluated.args.positional {
|
||||||
// FIXME: do not do this
|
let mut args_iter = args.into_iter().peekable();
|
||||||
for arg in args.into_iter().zip(self.params.positional.iter()) {
|
let mut params_iter = self.params.positional.iter();
|
||||||
let name = arg.1 .0.name();
|
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('$') {
|
if name.starts_with('$') {
|
||||||
ctx.scope.add_var(name, arg.0);
|
ctx.scope.add_var(name.to_string(), arg);
|
||||||
} else {
|
} else {
|
||||||
ctx.scope.add_var(format!("${}", name), arg.0);
|
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 {
|
if let Some(args) = evaluated.args.named {
|
||||||
for named in &block.params.named {
|
for named in &block.params.named {
|
||||||
let name = named.0;
|
let name = named.0;
|
||||||
|
@ -428,6 +428,42 @@ fn run_broken_inner_custom_command() {
|
|||||||
assert!(actual.err.contains("not found"));
|
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]
|
#[test]
|
||||||
fn set_variable() {
|
fn set_variable() {
|
||||||
let actual = nu!(
|
let actual = nu!(
|
||||||
|
Loading…
Reference in New Issue
Block a user