forked from extern/nushell
Stdout/Stderr redirection (#7185)
This adds new pipeline connectors called out> and err> which redirect either stdout or stderr to a file. You can also use out+err> (or err+out>) to redirect both streams into a file.
This commit is contained in:
parent
c9f9078726
commit
74a73f9838
@ -134,10 +134,10 @@ impl NuCompleter {
|
||||
for pipeline in output.pipelines.into_iter() {
|
||||
for pipeline_element in pipeline.elements {
|
||||
match pipeline_element {
|
||||
PipelineElement::Expression(expr)
|
||||
| PipelineElement::Redirect(expr)
|
||||
| PipelineElement::And(expr)
|
||||
| PipelineElement::Or(expr) => {
|
||||
PipelineElement::Expression(_, expr)
|
||||
| PipelineElement::Redirection(_, _, expr)
|
||||
| PipelineElement::And(_, expr)
|
||||
| PipelineElement::Or(_, expr) => {
|
||||
let flattened: Vec<_> = flatten_expression(&working_set, &expr);
|
||||
let span_offset: usize = alias_offset.iter().sum();
|
||||
let mut spans: Vec<String> = vec![];
|
||||
|
@ -120,6 +120,10 @@ impl Highlighter for NuHighlighter {
|
||||
FlatShape::GlobPattern => add_colored_token!(shape.1, next_token),
|
||||
FlatShape::Variable => add_colored_token!(shape.1, next_token),
|
||||
FlatShape::Flag => add_colored_token!(shape.1, next_token),
|
||||
FlatShape::Pipe => add_colored_token!(shape.1, next_token),
|
||||
FlatShape::And => add_colored_token!(shape.1, next_token),
|
||||
FlatShape::Or => add_colored_token!(shape.1, next_token),
|
||||
FlatShape::Redirection => add_colored_token!(shape.1, next_token),
|
||||
FlatShape::Custom(..) => add_colored_token!(shape.1, next_token),
|
||||
}
|
||||
last_seen_span = shape.0.end;
|
||||
@ -232,10 +236,10 @@ fn find_matching_block_end_in_block(
|
||||
for p in &block.pipelines {
|
||||
for e in &p.elements {
|
||||
match e {
|
||||
PipelineElement::Expression(e)
|
||||
| PipelineElement::Redirect(e)
|
||||
| PipelineElement::And(e)
|
||||
| PipelineElement::Or(e) => {
|
||||
PipelineElement::Expression(_, e)
|
||||
| PipelineElement::Redirection(_, _, e)
|
||||
| PipelineElement::And(_, e)
|
||||
| PipelineElement::Or(_, e) => {
|
||||
if e.span.contains(global_cursor_offset) {
|
||||
if let Some(pos) = find_matching_block_end_in_expr(
|
||||
line,
|
||||
|
@ -34,6 +34,10 @@ pub fn get_shape_color(shape: String, conf: &Config) -> Style {
|
||||
"shape_variable" => Style::new().fg(Color::Purple),
|
||||
"shape_flag" => Style::new().fg(Color::Blue).bold(),
|
||||
"shape_custom" => Style::new().fg(Color::Green),
|
||||
"shape_pipe" => Style::new().fg(Color::Purple).bold(),
|
||||
"shape_redirection" => Style::new().fg(Color::Purple).bold(),
|
||||
"shape_and" => Style::new().fg(Color::Purple).bold(),
|
||||
"shape_or" => Style::new().fg(Color::Purple).bold(),
|
||||
"shape_nothing" => Style::new().fg(Color::LightCyan),
|
||||
_ => Style::default(),
|
||||
},
|
||||
|
@ -156,10 +156,10 @@ impl Command for FromNuon {
|
||||
}
|
||||
} else {
|
||||
match pipeline.elements.remove(0) {
|
||||
PipelineElement::Expression(expression)
|
||||
| PipelineElement::Redirect(expression)
|
||||
| PipelineElement::And(expression)
|
||||
| PipelineElement::Or(expression) => expression,
|
||||
PipelineElement::Expression(_, expression)
|
||||
| PipelineElement::Redirection(_, _, expression)
|
||||
| PipelineElement::And(_, expression)
|
||||
| PipelineElement::Or(_, expression) => expression,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -57,6 +57,7 @@ mod print;
|
||||
mod query;
|
||||
mod random;
|
||||
mod range;
|
||||
mod redirection;
|
||||
mod reduce;
|
||||
mod reject;
|
||||
mod rename;
|
||||
|
66
crates/nu-command/tests/commands/redirection.rs
Normal file
66
crates/nu-command/tests/commands/redirection.rs
Normal file
@ -0,0 +1,66 @@
|
||||
use nu_test_support::nu;
|
||||
use nu_test_support::playground::Playground;
|
||||
|
||||
#[cfg(not(windows))]
|
||||
#[test]
|
||||
fn redirect_err() {
|
||||
Playground::setup("redirect_err_test", |dirs, _sandbox| {
|
||||
let output = nu!(
|
||||
cwd: dirs.test(),
|
||||
"cat asdfasdfasdf.txt err> a; cat a"
|
||||
);
|
||||
|
||||
assert!(output.err.contains("asdfasdfasdf.txt"));
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
#[test]
|
||||
fn redirect_err() {
|
||||
Playground::setup("redirect_err_test", |dirs, _sandbox| {
|
||||
let output = nu!(
|
||||
cwd: dirs.test(),
|
||||
"type asdfasdfasdf.txt err> a; type a"
|
||||
);
|
||||
|
||||
assert!(output.err.contains("asdfasdfasdf.txt"));
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
#[test]
|
||||
fn redirect_outerr() {
|
||||
Playground::setup("redirect_outerr_test", |dirs, _sandbox| {
|
||||
let output = nu!(
|
||||
cwd: dirs.test(),
|
||||
"cat asdfasdfasdf.txt out+err> a; cat a"
|
||||
);
|
||||
|
||||
assert!(output.err.contains("asdfasdfasdf.txt"));
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
#[test]
|
||||
fn redirect_outerr() {
|
||||
Playground::setup("redirect_outerr_test", |dirs, _sandbox| {
|
||||
let output = nu!(
|
||||
cwd: dirs.test(),
|
||||
"type asdfasdfasdf.txt out+err> a; type a"
|
||||
);
|
||||
|
||||
assert!(output.err.contains("asdfasdfasdf.txt"));
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn redirect_out() {
|
||||
Playground::setup("redirect_out_test", |dirs, _sandbox| {
|
||||
let output = nu!(
|
||||
cwd: dirs.test(),
|
||||
"echo 'hello' out> a; open a"
|
||||
);
|
||||
|
||||
assert!(output.out.contains("hello"));
|
||||
})
|
||||
}
|
@ -2,8 +2,8 @@ use crate::{current_dir_str, get_full_help, scope::create_scope};
|
||||
use nu_path::expand_path_with;
|
||||
use nu_protocol::{
|
||||
ast::{
|
||||
Assignment, Bits, Block, Boolean, Call, Comparison, Expr, Expression, Math, Operator,
|
||||
PathMember, PipelineElement,
|
||||
Argument, Assignment, Bits, Block, Boolean, Call, Comparison, Expr, Expression, Math,
|
||||
Operator, PathMember, PipelineElement, Redirection,
|
||||
},
|
||||
engine::{EngineState, Stack},
|
||||
Config, HistoryFileFormat, IntoInterruptiblePipelineData, IntoPipelineData, ListStream,
|
||||
@ -801,7 +801,7 @@ pub fn eval_element_with_input(
|
||||
redirect_stderr: bool,
|
||||
) -> Result<(PipelineData, bool), ShellError> {
|
||||
match element {
|
||||
PipelineElement::Expression(expr) => eval_expression_with_input(
|
||||
PipelineElement::Expression(_, expr) => eval_expression_with_input(
|
||||
engine_state,
|
||||
stack,
|
||||
expr,
|
||||
@ -809,7 +809,98 @@ pub fn eval_element_with_input(
|
||||
redirect_stdout,
|
||||
redirect_stderr,
|
||||
),
|
||||
PipelineElement::Redirect(expr) => eval_expression_with_input(
|
||||
PipelineElement::Redirection(span, redirection, expr) => match &expr.expr {
|
||||
Expr::String(_) => {
|
||||
let input = match (redirection, input) {
|
||||
(
|
||||
Redirection::Stderr,
|
||||
PipelineData::ExternalStream {
|
||||
stderr,
|
||||
exit_code,
|
||||
span,
|
||||
metadata,
|
||||
..
|
||||
},
|
||||
) => PipelineData::ExternalStream {
|
||||
stdout: stderr,
|
||||
stderr: None,
|
||||
exit_code,
|
||||
span,
|
||||
metadata,
|
||||
},
|
||||
(
|
||||
Redirection::StdoutAndStderr,
|
||||
PipelineData::ExternalStream {
|
||||
stdout,
|
||||
stderr,
|
||||
exit_code,
|
||||
span,
|
||||
metadata,
|
||||
},
|
||||
) => match (stdout, stderr) {
|
||||
(Some(stdout), Some(stderr)) => PipelineData::ExternalStream {
|
||||
stdout: Some(stdout.chain(stderr)),
|
||||
stderr: None,
|
||||
exit_code,
|
||||
span,
|
||||
metadata,
|
||||
},
|
||||
(None, Some(stderr)) => PipelineData::ExternalStream {
|
||||
stdout: Some(stderr),
|
||||
stderr: None,
|
||||
exit_code,
|
||||
span,
|
||||
metadata,
|
||||
},
|
||||
(Some(stdout), None) => PipelineData::ExternalStream {
|
||||
stdout: Some(stdout),
|
||||
stderr: None,
|
||||
exit_code,
|
||||
span,
|
||||
metadata,
|
||||
},
|
||||
(None, None) => PipelineData::ExternalStream {
|
||||
stdout: None,
|
||||
stderr: None,
|
||||
exit_code,
|
||||
span,
|
||||
metadata,
|
||||
},
|
||||
},
|
||||
(_, input) => input,
|
||||
};
|
||||
|
||||
if let Some(save_command) = engine_state.find_decl(b"save", &[]) {
|
||||
eval_call(
|
||||
engine_state,
|
||||
stack,
|
||||
&Call {
|
||||
decl_id: save_command,
|
||||
head: *span,
|
||||
arguments: vec![
|
||||
Argument::Positional(expr.clone()),
|
||||
Argument::Named((
|
||||
Spanned {
|
||||
item: "--raw".into(),
|
||||
span: *span,
|
||||
},
|
||||
None,
|
||||
None,
|
||||
)),
|
||||
],
|
||||
redirect_stdout: false,
|
||||
redirect_stderr: false,
|
||||
},
|
||||
input,
|
||||
)
|
||||
.map(|x| (x, false))
|
||||
} else {
|
||||
Err(ShellError::CommandNotFound(*span))
|
||||
}
|
||||
}
|
||||
_ => Err(ShellError::CommandNotFound(*span)),
|
||||
},
|
||||
PipelineElement::And(_, expr) => eval_expression_with_input(
|
||||
engine_state,
|
||||
stack,
|
||||
expr,
|
||||
@ -817,15 +908,7 @@ pub fn eval_element_with_input(
|
||||
redirect_stdout,
|
||||
redirect_stderr,
|
||||
),
|
||||
PipelineElement::And(expr) => eval_expression_with_input(
|
||||
engine_state,
|
||||
stack,
|
||||
expr,
|
||||
input,
|
||||
redirect_stdout,
|
||||
redirect_stderr,
|
||||
),
|
||||
PipelineElement::Or(expr) => eval_expression_with_input(
|
||||
PipelineElement::Or(_, expr) => eval_expression_with_input(
|
||||
engine_state,
|
||||
stack,
|
||||
expr,
|
||||
@ -846,15 +929,23 @@ pub fn eval_block(
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let num_pipelines = block.len();
|
||||
for (pipeline_idx, pipeline) in block.pipelines.iter().enumerate() {
|
||||
for (i, elem) in pipeline.elements.iter().enumerate() {
|
||||
let mut i = 0;
|
||||
|
||||
while i < pipeline.elements.len() {
|
||||
// if eval internal command failed, it can just make early return with `Err(ShellError)`.
|
||||
let eval_result = eval_element_with_input(
|
||||
engine_state,
|
||||
stack,
|
||||
elem,
|
||||
&pipeline.elements[i],
|
||||
input,
|
||||
redirect_stdout || (i != pipeline.elements.len() - 1),
|
||||
redirect_stderr,
|
||||
redirect_stderr
|
||||
|| ((i < pipeline.elements.len() - 1)
|
||||
&& (matches!(
|
||||
pipeline.elements[i + 1],
|
||||
PipelineElement::Redirection(_, Redirection::Stderr, _)
|
||||
| PipelineElement::Redirection(_, Redirection::StdoutAndStderr, _)
|
||||
))),
|
||||
)?;
|
||||
input = eval_result.0;
|
||||
// external command may runs to failed
|
||||
@ -863,6 +954,8 @@ pub fn eval_block(
|
||||
if eval_result.1 {
|
||||
return Ok(input);
|
||||
}
|
||||
|
||||
i += 1;
|
||||
}
|
||||
|
||||
if pipeline_idx < (num_pipelines) - 1 {
|
||||
|
@ -32,6 +32,10 @@ pub enum FlatShape {
|
||||
GlobPattern,
|
||||
Variable,
|
||||
Flag,
|
||||
Pipe,
|
||||
And,
|
||||
Or,
|
||||
Redirection,
|
||||
Custom(DeclId),
|
||||
}
|
||||
|
||||
@ -63,6 +67,10 @@ impl Display for FlatShape {
|
||||
FlatShape::GlobPattern => write!(f, "shape_globpattern"),
|
||||
FlatShape::Variable => write!(f, "shape_variable"),
|
||||
FlatShape::Flag => write!(f, "shape_flag"),
|
||||
FlatShape::Pipe => write!(f, "shape_pipe"),
|
||||
FlatShape::And => write!(f, "shape_and"),
|
||||
FlatShape::Or => write!(f, "shape_or"),
|
||||
FlatShape::Redirection => write!(f, "shape_redirection"),
|
||||
FlatShape::Custom(_) => write!(f, "shape_custom"),
|
||||
}
|
||||
}
|
||||
@ -502,10 +510,30 @@ pub fn flatten_pipeline_element(
|
||||
pipeline_element: &PipelineElement,
|
||||
) -> Vec<(Span, FlatShape)> {
|
||||
match pipeline_element {
|
||||
PipelineElement::Expression(expr)
|
||||
| PipelineElement::Redirect(expr)
|
||||
| PipelineElement::And(expr)
|
||||
| PipelineElement::Or(expr) => flatten_expression(working_set, expr),
|
||||
PipelineElement::Expression(span, expr) => {
|
||||
if let Some(span) = span {
|
||||
let mut output = vec![(*span, FlatShape::Pipe)];
|
||||
output.append(&mut flatten_expression(working_set, expr));
|
||||
output
|
||||
} else {
|
||||
flatten_expression(working_set, expr)
|
||||
}
|
||||
}
|
||||
PipelineElement::Redirection(span, _, expr) => {
|
||||
let mut output = vec![(*span, FlatShape::Redirection)];
|
||||
output.append(&mut flatten_expression(working_set, expr));
|
||||
output
|
||||
}
|
||||
PipelineElement::And(span, expr) => {
|
||||
let mut output = vec![(*span, FlatShape::And)];
|
||||
output.append(&mut flatten_expression(working_set, expr));
|
||||
output
|
||||
}
|
||||
PipelineElement::Or(span, expr) => {
|
||||
let mut output = vec![(*span, FlatShape::Or)];
|
||||
output.append(&mut flatten_expression(working_set, expr));
|
||||
output
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,12 +1,15 @@
|
||||
use crate::ParseError;
|
||||
use nu_protocol::Span;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum TokenContents {
|
||||
Item,
|
||||
Comment,
|
||||
Pipe,
|
||||
Semicolon,
|
||||
OutGreaterThan,
|
||||
ErrGreaterThan,
|
||||
OutErrGreaterThan,
|
||||
Eol,
|
||||
}
|
||||
|
||||
@ -74,7 +77,7 @@ pub fn lex_item(
|
||||
span_offset: usize,
|
||||
additional_whitespace: &[u8],
|
||||
special_tokens: &[u8],
|
||||
) -> (Span, Option<ParseError>) {
|
||||
) -> (Token, Option<ParseError>) {
|
||||
// This variable tracks the starting character of a string literal, so that
|
||||
// we remain inside the string literal lexer mode until we encounter the
|
||||
// closing quote.
|
||||
@ -113,7 +116,10 @@ pub fn lex_item(
|
||||
let span = Span::new(span_offset + token_start, span_offset + *curr_offset);
|
||||
|
||||
return (
|
||||
span,
|
||||
Token {
|
||||
contents: TokenContents::Item,
|
||||
span,
|
||||
},
|
||||
Some(ParseError::UnexpectedEof(
|
||||
(start as char).to_string(),
|
||||
Span {
|
||||
@ -195,7 +201,13 @@ pub fn lex_item(
|
||||
},
|
||||
);
|
||||
|
||||
return (span, Some(cause));
|
||||
return (
|
||||
Token {
|
||||
contents: TokenContents::Item,
|
||||
span,
|
||||
},
|
||||
Some(cause),
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(delim) = quote_start {
|
||||
@ -203,7 +215,10 @@ pub fn lex_item(
|
||||
// anyone wanting to consume this partial parse (e.g., completions) will be able to get
|
||||
// correct information from the non-lite parse.
|
||||
return (
|
||||
span,
|
||||
Token {
|
||||
contents: TokenContents::Item,
|
||||
span,
|
||||
},
|
||||
Some(ParseError::UnexpectedEof(
|
||||
(delim as char).to_string(),
|
||||
Span {
|
||||
@ -217,12 +232,44 @@ pub fn lex_item(
|
||||
// If we didn't accumulate any characters, it's an unexpected error.
|
||||
if *curr_offset - token_start == 0 {
|
||||
return (
|
||||
span,
|
||||
Token {
|
||||
contents: TokenContents::Item,
|
||||
span,
|
||||
},
|
||||
Some(ParseError::UnexpectedEof("command".to_string(), span)),
|
||||
);
|
||||
}
|
||||
|
||||
(span, None)
|
||||
match &input[(span.start - span_offset)..(span.end - span_offset)] {
|
||||
b"out>" => (
|
||||
Token {
|
||||
contents: TokenContents::OutGreaterThan,
|
||||
span,
|
||||
},
|
||||
None,
|
||||
),
|
||||
b"err>" => (
|
||||
Token {
|
||||
contents: TokenContents::ErrGreaterThan,
|
||||
span,
|
||||
},
|
||||
None,
|
||||
),
|
||||
b"out+err>" | b"err+out>" => (
|
||||
Token {
|
||||
contents: TokenContents::OutErrGreaterThan,
|
||||
span,
|
||||
},
|
||||
None,
|
||||
),
|
||||
_ => (
|
||||
Token {
|
||||
contents: TokenContents::Item,
|
||||
span,
|
||||
},
|
||||
None,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lex(
|
||||
@ -347,7 +394,7 @@ pub fn lex(
|
||||
} else {
|
||||
// Otherwise, try to consume an unclassified token.
|
||||
|
||||
let (span, err) = lex_item(
|
||||
let (token, err) = lex_item(
|
||||
input,
|
||||
&mut curr_offset,
|
||||
span_offset,
|
||||
@ -358,7 +405,7 @@ pub fn lex(
|
||||
error = err;
|
||||
}
|
||||
is_complete = true;
|
||||
output.push(Token::new(TokenContents::Item, span));
|
||||
output.push(token);
|
||||
}
|
||||
}
|
||||
(output, error)
|
||||
|
@ -865,10 +865,13 @@ pub fn parse_export_in_module(
|
||||
};
|
||||
|
||||
// Trying to warp the 'def' call into the 'export def' in a very clumsy way
|
||||
if let Some(PipelineElement::Expression(Expression {
|
||||
expr: Expr::Call(ref def_call),
|
||||
..
|
||||
})) = pipeline.elements.get(0)
|
||||
if let Some(PipelineElement::Expression(
|
||||
_,
|
||||
Expression {
|
||||
expr: Expr::Call(ref def_call),
|
||||
..
|
||||
},
|
||||
)) = pipeline.elements.get(0)
|
||||
{
|
||||
call = def_call.clone();
|
||||
|
||||
@ -930,10 +933,13 @@ pub fn parse_export_in_module(
|
||||
};
|
||||
|
||||
// Trying to warp the 'def' call into the 'export def' in a very clumsy way
|
||||
if let Some(PipelineElement::Expression(Expression {
|
||||
expr: Expr::Call(ref def_call),
|
||||
..
|
||||
})) = pipeline.elements.get(0)
|
||||
if let Some(PipelineElement::Expression(
|
||||
_,
|
||||
Expression {
|
||||
expr: Expr::Call(ref def_call),
|
||||
..
|
||||
},
|
||||
)) = pipeline.elements.get(0)
|
||||
{
|
||||
call = def_call.clone();
|
||||
|
||||
@ -996,10 +1002,13 @@ pub fn parse_export_in_module(
|
||||
};
|
||||
|
||||
// Trying to warp the 'def' call into the 'export def' in a very clumsy way
|
||||
if let Some(PipelineElement::Expression(Expression {
|
||||
expr: Expr::Call(ref def_call),
|
||||
..
|
||||
})) = pipeline.elements.get(0)
|
||||
if let Some(PipelineElement::Expression(
|
||||
_,
|
||||
Expression {
|
||||
expr: Expr::Call(ref def_call),
|
||||
..
|
||||
},
|
||||
)) = pipeline.elements.get(0)
|
||||
{
|
||||
call = def_call.clone();
|
||||
|
||||
@ -1062,10 +1071,13 @@ pub fn parse_export_in_module(
|
||||
};
|
||||
|
||||
// Trying to warp the 'alias' call into the 'export alias' in a very clumsy way
|
||||
if let Some(PipelineElement::Expression(Expression {
|
||||
expr: Expr::Call(ref alias_call),
|
||||
..
|
||||
})) = pipeline.elements.get(0)
|
||||
if let Some(PipelineElement::Expression(
|
||||
_,
|
||||
Expression {
|
||||
expr: Expr::Call(ref alias_call),
|
||||
..
|
||||
},
|
||||
)) = pipeline.elements.get(0)
|
||||
{
|
||||
call = alias_call.clone();
|
||||
|
||||
@ -1128,10 +1140,13 @@ pub fn parse_export_in_module(
|
||||
};
|
||||
|
||||
// Trying to warp the 'use' call into the 'export use' in a very clumsy way
|
||||
if let Some(PipelineElement::Expression(Expression {
|
||||
expr: Expr::Call(ref use_call),
|
||||
..
|
||||
})) = pipeline.elements.get(0)
|
||||
if let Some(PipelineElement::Expression(
|
||||
_,
|
||||
Expression {
|
||||
expr: Expr::Call(ref use_call),
|
||||
..
|
||||
},
|
||||
)) = pipeline.elements.get(0)
|
||||
{
|
||||
call = use_call.clone();
|
||||
|
||||
@ -1313,7 +1328,7 @@ pub fn parse_module_block(
|
||||
|
||||
for pipeline in &output.block {
|
||||
if pipeline.commands.len() == 1 {
|
||||
if let LiteElement::Command(command) = &pipeline.commands[0] {
|
||||
if let LiteElement::Command(_, command) = &pipeline.commands[0] {
|
||||
parse_def_predecl(working_set, &command.parts, expand_aliases_denylist);
|
||||
}
|
||||
}
|
||||
@ -1327,7 +1342,7 @@ pub fn parse_module_block(
|
||||
.map(|pipeline| {
|
||||
if pipeline.commands.len() == 1 {
|
||||
match &pipeline.commands[0] {
|
||||
LiteElement::Command(command) => {
|
||||
LiteElement::Command(_, command) => {
|
||||
let name = working_set.get_span_contents(command.parts[0]);
|
||||
|
||||
let (pipeline, err) = match name {
|
||||
@ -1407,9 +1422,9 @@ pub fn parse_module_block(
|
||||
|
||||
pipeline
|
||||
}
|
||||
LiteElement::Or(command)
|
||||
| LiteElement::And(command)
|
||||
| LiteElement::Redirection(command) => (garbage_pipeline(&command.parts)),
|
||||
LiteElement::Or(_, command)
|
||||
| LiteElement::And(_, command)
|
||||
| LiteElement::Redirection(_, _, command) => (garbage_pipeline(&command.parts)),
|
||||
}
|
||||
} else {
|
||||
error = Some(ParseError::Expected("not a pipeline".into(), span));
|
||||
|
@ -8,7 +8,7 @@ use nu_protocol::{
|
||||
ast::{
|
||||
Argument, Assignment, Bits, Block, Boolean, Call, CellPath, Comparison, Expr, Expression,
|
||||
FullCellPath, ImportPattern, ImportPatternHead, ImportPatternMember, Math, Operator,
|
||||
PathMember, Pipeline, PipelineElement, RangeInclusion, RangeOperator,
|
||||
PathMember, Pipeline, PipelineElement, RangeInclusion, RangeOperator, Redirection,
|
||||
},
|
||||
engine::StateWorkingSet,
|
||||
span, BlockId, Flag, PositionalArg, Signature, Span, Spanned, SyntaxShape, Type, Unit, VarId,
|
||||
@ -1056,7 +1056,7 @@ pub fn parse_call(
|
||||
let result = result.elements.remove(0);
|
||||
|
||||
// If this is the first element in a pipeline, we know it has to be an expression
|
||||
if let PipelineElement::Expression(mut result) = result {
|
||||
if let PipelineElement::Expression(_, mut result) = result {
|
||||
result.replace_span(working_set, expansion_span, orig_span);
|
||||
|
||||
return (result, err);
|
||||
@ -1203,7 +1203,12 @@ fn parse_binary_with_base(
|
||||
Some(ParseError::Expected("binary".into(), span)),
|
||||
);
|
||||
}
|
||||
TokenContents::Comment | TokenContents::Semicolon | TokenContents::Eol => {}
|
||||
TokenContents::Comment
|
||||
| TokenContents::Semicolon
|
||||
| TokenContents::Eol
|
||||
| TokenContents::OutGreaterThan
|
||||
| TokenContents::ErrGreaterThan
|
||||
| TokenContents::OutErrGreaterThan => {}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1924,7 +1929,7 @@ pub fn parse_full_cell_path(
|
||||
.last()
|
||||
.and_then(|Pipeline { elements, .. }| elements.last())
|
||||
.map(|element| match element {
|
||||
PipelineElement::Expression(expr)
|
||||
PipelineElement::Expression(_, expr)
|
||||
if matches!(
|
||||
expr,
|
||||
Expression {
|
||||
@ -3071,7 +3076,7 @@ pub fn parse_row_condition(
|
||||
let mut pipeline = Pipeline::new();
|
||||
pipeline
|
||||
.elements
|
||||
.push(PipelineElement::Expression(expression));
|
||||
.push(PipelineElement::Expression(None, expression));
|
||||
|
||||
block.pipelines.push(pipeline);
|
||||
|
||||
@ -3721,7 +3726,7 @@ pub fn parse_list_expression(
|
||||
for arg in &output.block[0].commands {
|
||||
let mut spans_idx = 0;
|
||||
|
||||
if let LiteElement::Command(command) = arg {
|
||||
if let LiteElement::Command(_, command) = arg {
|
||||
while spans_idx < command.parts.len() {
|
||||
let (arg, err) = parse_multispan_value(
|
||||
working_set,
|
||||
@ -3814,10 +3819,10 @@ pub fn parse_table_expression(
|
||||
}
|
||||
_ => {
|
||||
match &output.block[0].commands[0] {
|
||||
LiteElement::Command(command)
|
||||
| LiteElement::Redirection(command)
|
||||
| LiteElement::And(command)
|
||||
| LiteElement::Or(command) => {
|
||||
LiteElement::Command(_, command)
|
||||
| LiteElement::Redirection(_, _, command)
|
||||
| LiteElement::And(_, command)
|
||||
| LiteElement::Or(_, command) => {
|
||||
let mut table_headers = vec![];
|
||||
|
||||
let (headers, err) = parse_value(
|
||||
@ -3837,10 +3842,10 @@ pub fn parse_table_expression(
|
||||
}
|
||||
|
||||
match &output.block[1].commands[0] {
|
||||
LiteElement::Command(command)
|
||||
| LiteElement::Redirection(command)
|
||||
| LiteElement::And(command)
|
||||
| LiteElement::Or(command) => {
|
||||
LiteElement::Command(_, command)
|
||||
| LiteElement::Redirection(_, _, command)
|
||||
| LiteElement::And(_, command)
|
||||
| LiteElement::Or(_, command) => {
|
||||
let mut rows = vec![];
|
||||
for part in &command.parts {
|
||||
let (values, err) = parse_value(
|
||||
@ -5184,10 +5189,10 @@ pub fn parse_block(
|
||||
for pipeline in &lite_block.block {
|
||||
if pipeline.commands.len() == 1 {
|
||||
match &pipeline.commands[0] {
|
||||
LiteElement::Command(command)
|
||||
| LiteElement::Redirection(command)
|
||||
| LiteElement::And(command)
|
||||
| LiteElement::Or(command) => {
|
||||
LiteElement::Command(_, command)
|
||||
| LiteElement::Redirection(_, _, command)
|
||||
| LiteElement::And(_, command)
|
||||
| LiteElement::Or(_, command) => {
|
||||
if let Some(err) =
|
||||
parse_def_predecl(working_set, &command.parts, expand_aliases_denylist)
|
||||
{
|
||||
@ -5208,7 +5213,7 @@ pub fn parse_block(
|
||||
.commands
|
||||
.iter()
|
||||
.map(|command| match command {
|
||||
LiteElement::Command(command) => {
|
||||
LiteElement::Command(span, command) => {
|
||||
let (expr, err) = parse_expression(
|
||||
working_set,
|
||||
&command.parts,
|
||||
@ -5221,12 +5226,12 @@ pub fn parse_block(
|
||||
error = err;
|
||||
}
|
||||
|
||||
PipelineElement::Expression(expr)
|
||||
PipelineElement::Expression(*span, expr)
|
||||
}
|
||||
LiteElement::Redirection(command) => {
|
||||
let (expr, err) = parse_expression(
|
||||
LiteElement::Redirection(span, redirection, command) => {
|
||||
let (expr, err) = parse_string(
|
||||
working_set,
|
||||
&command.parts,
|
||||
command.parts[0],
|
||||
expand_aliases_denylist,
|
||||
);
|
||||
|
||||
@ -5236,9 +5241,9 @@ pub fn parse_block(
|
||||
error = err;
|
||||
}
|
||||
|
||||
PipelineElement::Redirect(expr)
|
||||
PipelineElement::Redirection(*span, redirection.clone(), expr)
|
||||
}
|
||||
LiteElement::And(command) => {
|
||||
LiteElement::And(span, command) => {
|
||||
let (expr, err) = parse_expression(
|
||||
working_set,
|
||||
&command.parts,
|
||||
@ -5251,9 +5256,9 @@ pub fn parse_block(
|
||||
error = err;
|
||||
}
|
||||
|
||||
PipelineElement::And(expr)
|
||||
PipelineElement::And(*span, expr)
|
||||
}
|
||||
LiteElement::Or(command) => {
|
||||
LiteElement::Or(span, command) => {
|
||||
let (expr, err) = parse_expression(
|
||||
working_set,
|
||||
&command.parts,
|
||||
@ -5266,7 +5271,7 @@ pub fn parse_block(
|
||||
error = err;
|
||||
}
|
||||
|
||||
PipelineElement::Or(expr)
|
||||
PipelineElement::Or(*span, expr)
|
||||
}
|
||||
})
|
||||
.collect::<Vec<PipelineElement>>();
|
||||
@ -5288,10 +5293,10 @@ pub fn parse_block(
|
||||
Pipeline { elements: output }
|
||||
} else {
|
||||
match &pipeline.commands[0] {
|
||||
LiteElement::Command(command)
|
||||
| LiteElement::Redirection(command)
|
||||
| LiteElement::And(command)
|
||||
| LiteElement::Or(command) => {
|
||||
LiteElement::Command(_, command)
|
||||
| LiteElement::Redirection(_, _, command)
|
||||
| LiteElement::And(_, command)
|
||||
| LiteElement::Or(_, command) => {
|
||||
let (mut pipeline, err) =
|
||||
parse_builtin_commands(working_set, command, expand_aliases_denylist);
|
||||
|
||||
@ -5301,10 +5306,13 @@ pub fn parse_block(
|
||||
working_set.find_decl(b"let-env", &Type::Any)
|
||||
{
|
||||
for element in pipeline.elements.iter_mut() {
|
||||
if let PipelineElement::Expression(Expression {
|
||||
expr: Expr::Call(call),
|
||||
..
|
||||
}) = element
|
||||
if let PipelineElement::Expression(
|
||||
_,
|
||||
Expression {
|
||||
expr: Expr::Call(call),
|
||||
..
|
||||
},
|
||||
) = element
|
||||
{
|
||||
if call.decl_id == let_decl_id
|
||||
|| call.decl_id == let_env_decl_id
|
||||
@ -5421,10 +5429,10 @@ pub fn discover_captures_in_pipeline_element(
|
||||
seen_blocks: &mut HashMap<BlockId, Vec<(VarId, Span)>>,
|
||||
) -> Result<Vec<(VarId, Span)>, ParseError> {
|
||||
match element {
|
||||
PipelineElement::Expression(expression)
|
||||
| PipelineElement::Redirect(expression)
|
||||
| PipelineElement::And(expression)
|
||||
| PipelineElement::Or(expression) => {
|
||||
PipelineElement::Expression(_, expression)
|
||||
| PipelineElement::Redirection(_, _, expression)
|
||||
| PipelineElement::And(_, expression)
|
||||
| PipelineElement::Or(_, expression) => {
|
||||
discover_captures_in_expr(working_set, expression, seen, seen_blocks)
|
||||
}
|
||||
}
|
||||
@ -5674,17 +5682,21 @@ fn wrap_element_with_collect(
|
||||
element: &PipelineElement,
|
||||
) -> PipelineElement {
|
||||
match element {
|
||||
PipelineElement::Expression(expression) => {
|
||||
PipelineElement::Expression(wrap_expr_with_collect(working_set, expression))
|
||||
PipelineElement::Expression(span, expression) => {
|
||||
PipelineElement::Expression(*span, wrap_expr_with_collect(working_set, expression))
|
||||
}
|
||||
PipelineElement::Redirect(expression) => {
|
||||
PipelineElement::Redirect(wrap_expr_with_collect(working_set, expression))
|
||||
PipelineElement::Redirection(span, redirection, expression) => {
|
||||
PipelineElement::Redirection(
|
||||
*span,
|
||||
redirection.clone(),
|
||||
wrap_expr_with_collect(working_set, expression),
|
||||
)
|
||||
}
|
||||
PipelineElement::And(expression) => {
|
||||
PipelineElement::And(wrap_expr_with_collect(working_set, expression))
|
||||
PipelineElement::And(span, expression) => {
|
||||
PipelineElement::And(*span, wrap_expr_with_collect(working_set, expression))
|
||||
}
|
||||
PipelineElement::Or(expression) => {
|
||||
PipelineElement::Or(wrap_expr_with_collect(working_set, expression))
|
||||
PipelineElement::Or(span, expression) => {
|
||||
PipelineElement::Or(*span, wrap_expr_with_collect(working_set, expression))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -5781,12 +5793,13 @@ impl LiteCommand {
|
||||
}
|
||||
}
|
||||
|
||||
// Note: the Span is the span of the connector not the whole element
|
||||
#[derive(Debug)]
|
||||
pub enum LiteElement {
|
||||
Command(LiteCommand),
|
||||
Redirection(LiteCommand),
|
||||
And(LiteCommand),
|
||||
Or(LiteCommand),
|
||||
Command(Option<Span>, LiteCommand),
|
||||
Redirection(Span, Redirection, LiteCommand),
|
||||
And(Span, LiteCommand),
|
||||
Or(Span, LiteCommand),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -5846,6 +5859,13 @@ pub fn lite_parse(tokens: &[Token]) -> (LiteBlock, Option<ParseError>) {
|
||||
|
||||
let mut last_token = TokenContents::Eol;
|
||||
|
||||
let mut last_connector = TokenContents::Pipe;
|
||||
let mut last_connector_span: Option<Span> = None;
|
||||
|
||||
if tokens.is_empty() {
|
||||
return (LiteBlock::new(), None);
|
||||
}
|
||||
|
||||
let mut curr_comment: Option<Vec<Span>> = None;
|
||||
|
||||
for token in tokens.iter() {
|
||||
@ -5858,17 +5878,96 @@ pub fn lite_parse(tokens: &[Token]) -> (LiteBlock, Option<ParseError>) {
|
||||
curr_command.push(token.span);
|
||||
last_token = TokenContents::Item;
|
||||
}
|
||||
TokenContents::OutGreaterThan
|
||||
| TokenContents::ErrGreaterThan
|
||||
| TokenContents::OutErrGreaterThan => {
|
||||
if !curr_command.is_empty() {
|
||||
match last_connector {
|
||||
TokenContents::OutGreaterThan => {
|
||||
curr_pipeline.push(LiteElement::Redirection(
|
||||
token.span,
|
||||
Redirection::Stdout,
|
||||
curr_command,
|
||||
));
|
||||
}
|
||||
TokenContents::ErrGreaterThan => {
|
||||
curr_pipeline.push(LiteElement::Redirection(
|
||||
token.span,
|
||||
Redirection::Stderr,
|
||||
curr_command,
|
||||
));
|
||||
}
|
||||
TokenContents::OutErrGreaterThan => {
|
||||
curr_pipeline.push(LiteElement::Redirection(
|
||||
token.span,
|
||||
Redirection::StdoutAndStderr,
|
||||
curr_command,
|
||||
));
|
||||
}
|
||||
_ => {
|
||||
curr_pipeline
|
||||
.push(LiteElement::Command(last_connector_span, curr_command));
|
||||
}
|
||||
}
|
||||
curr_command = LiteCommand::new();
|
||||
}
|
||||
last_token = token.contents;
|
||||
last_connector = token.contents;
|
||||
last_connector_span = Some(token.span);
|
||||
}
|
||||
TokenContents::Pipe => {
|
||||
if !curr_command.is_empty() {
|
||||
curr_pipeline.push(LiteElement::Command(curr_command));
|
||||
match last_connector {
|
||||
TokenContents::OutGreaterThan => {
|
||||
curr_pipeline.push(LiteElement::Redirection(
|
||||
token.span,
|
||||
Redirection::Stdout,
|
||||
curr_command,
|
||||
));
|
||||
}
|
||||
TokenContents::ErrGreaterThan => {
|
||||
curr_pipeline.push(LiteElement::Redirection(
|
||||
token.span,
|
||||
Redirection::Stderr,
|
||||
curr_command,
|
||||
));
|
||||
}
|
||||
TokenContents::OutErrGreaterThan => {
|
||||
curr_pipeline.push(LiteElement::Redirection(
|
||||
token.span,
|
||||
Redirection::StdoutAndStderr,
|
||||
curr_command,
|
||||
));
|
||||
}
|
||||
_ => {
|
||||
curr_pipeline
|
||||
.push(LiteElement::Command(last_connector_span, curr_command));
|
||||
}
|
||||
}
|
||||
curr_command = LiteCommand::new();
|
||||
}
|
||||
last_token = TokenContents::Pipe;
|
||||
last_connector = TokenContents::Pipe;
|
||||
last_connector_span = Some(token.span);
|
||||
}
|
||||
TokenContents::Eol => {
|
||||
if last_token != TokenContents::Pipe {
|
||||
if last_token != TokenContents::Pipe && last_token != TokenContents::OutGreaterThan
|
||||
{
|
||||
if !curr_command.is_empty() {
|
||||
curr_pipeline.push(LiteElement::Command(curr_command));
|
||||
if last_connector == TokenContents::OutGreaterThan
|
||||
|| last_connector == TokenContents::ErrGreaterThan
|
||||
|| last_connector == TokenContents::OutErrGreaterThan
|
||||
{
|
||||
curr_pipeline.push(LiteElement::Redirection(
|
||||
last_connector_span
|
||||
.expect("internal error: redirection missing span information"),
|
||||
Redirection::Stdout,
|
||||
curr_command,
|
||||
));
|
||||
} else {
|
||||
curr_pipeline
|
||||
.push(LiteElement::Command(last_connector_span, curr_command));
|
||||
}
|
||||
|
||||
curr_command = LiteCommand::new();
|
||||
}
|
||||
@ -5889,7 +5988,19 @@ pub fn lite_parse(tokens: &[Token]) -> (LiteBlock, Option<ParseError>) {
|
||||
}
|
||||
TokenContents::Semicolon => {
|
||||
if !curr_command.is_empty() {
|
||||
curr_pipeline.push(LiteElement::Command(curr_command));
|
||||
if last_connector == TokenContents::OutGreaterThan
|
||||
|| last_connector == TokenContents::ErrGreaterThan
|
||||
|| last_connector == TokenContents::OutErrGreaterThan
|
||||
{
|
||||
curr_pipeline.push(LiteElement::Redirection(
|
||||
last_connector_span
|
||||
.expect("internal error: redirection missing span information"),
|
||||
Redirection::Stdout,
|
||||
curr_command,
|
||||
));
|
||||
} else {
|
||||
curr_pipeline.push(LiteElement::Command(last_connector_span, curr_command));
|
||||
}
|
||||
|
||||
curr_command = LiteCommand::new();
|
||||
}
|
||||
@ -5922,7 +6033,35 @@ pub fn lite_parse(tokens: &[Token]) -> (LiteBlock, Option<ParseError>) {
|
||||
}
|
||||
|
||||
if !curr_command.is_empty() {
|
||||
curr_pipeline.push(LiteElement::Command(curr_command));
|
||||
match last_connector {
|
||||
TokenContents::OutGreaterThan => {
|
||||
curr_pipeline.push(LiteElement::Redirection(
|
||||
last_connector_span
|
||||
.expect("internal error: redirection missing span information"),
|
||||
Redirection::Stdout,
|
||||
curr_command,
|
||||
));
|
||||
}
|
||||
TokenContents::ErrGreaterThan => {
|
||||
curr_pipeline.push(LiteElement::Redirection(
|
||||
last_connector_span
|
||||
.expect("internal error: redirection missing span information"),
|
||||
Redirection::Stderr,
|
||||
curr_command,
|
||||
));
|
||||
}
|
||||
TokenContents::OutErrGreaterThan => {
|
||||
curr_pipeline.push(LiteElement::Redirection(
|
||||
last_connector_span
|
||||
.expect("internal error: redirection missing span information"),
|
||||
Redirection::StdoutAndStderr,
|
||||
curr_command,
|
||||
));
|
||||
}
|
||||
_ => {
|
||||
curr_pipeline.push(LiteElement::Command(last_connector_span, curr_command));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !curr_pipeline.is_empty() {
|
||||
|
@ -54,10 +54,13 @@ pub fn parse_int() {
|
||||
assert!(expressions.len() == 1);
|
||||
assert!(matches!(
|
||||
expressions[0],
|
||||
PipelineElement::Expression(Expression {
|
||||
expr: Expr::Int(3),
|
||||
..
|
||||
})
|
||||
PipelineElement::Expression(
|
||||
_,
|
||||
Expression {
|
||||
expr: Expr::Int(3),
|
||||
..
|
||||
}
|
||||
)
|
||||
))
|
||||
}
|
||||
|
||||
@ -72,7 +75,7 @@ pub fn parse_binary_with_hex_format() {
|
||||
assert!(block.len() == 1);
|
||||
let expressions = &block[0];
|
||||
assert!(expressions.len() == 1);
|
||||
if let PipelineElement::Expression(expr) = &expressions[0] {
|
||||
if let PipelineElement::Expression(_, expr) = &expressions[0] {
|
||||
assert_eq!(expr.expr, Expr::Binary(vec![0x13]))
|
||||
} else {
|
||||
panic!("Not an expression")
|
||||
@ -90,7 +93,7 @@ pub fn parse_binary_with_incomplete_hex_format() {
|
||||
assert!(block.len() == 1);
|
||||
let expressions = &block[0];
|
||||
assert!(expressions.len() == 1);
|
||||
if let PipelineElement::Expression(expr) = &expressions[0] {
|
||||
if let PipelineElement::Expression(_, expr) = &expressions[0] {
|
||||
assert_eq!(expr.expr, Expr::Binary(vec![0x03]))
|
||||
} else {
|
||||
panic!("Not an expression")
|
||||
@ -108,7 +111,7 @@ pub fn parse_binary_with_binary_format() {
|
||||
assert!(block.len() == 1);
|
||||
let expressions = &block[0];
|
||||
assert!(expressions.len() == 1);
|
||||
if let PipelineElement::Expression(expr) = &expressions[0] {
|
||||
if let PipelineElement::Expression(_, expr) = &expressions[0] {
|
||||
assert_eq!(expr.expr, Expr::Binary(vec![0b10101000]))
|
||||
} else {
|
||||
panic!("Not an expression")
|
||||
@ -126,7 +129,7 @@ pub fn parse_binary_with_incomplete_binary_format() {
|
||||
assert!(block.len() == 1);
|
||||
let expressions = &block[0];
|
||||
assert!(expressions.len() == 1);
|
||||
if let PipelineElement::Expression(expr) = &expressions[0] {
|
||||
if let PipelineElement::Expression(_, expr) = &expressions[0] {
|
||||
assert_eq!(expr.expr, Expr::Binary(vec![0b00000010]))
|
||||
} else {
|
||||
panic!("Not an expression")
|
||||
@ -144,7 +147,7 @@ pub fn parse_binary_with_octal_format() {
|
||||
assert!(block.len() == 1);
|
||||
let expressions = &block[0];
|
||||
assert!(expressions.len() == 1);
|
||||
if let PipelineElement::Expression(expr) = &expressions[0] {
|
||||
if let PipelineElement::Expression(_, expr) = &expressions[0] {
|
||||
assert_eq!(expr.expr, Expr::Binary(vec![0o250]))
|
||||
} else {
|
||||
panic!("Not an expression")
|
||||
@ -162,7 +165,7 @@ pub fn parse_binary_with_incomplete_octal_format() {
|
||||
assert!(block.len() == 1);
|
||||
let expressions = &block[0];
|
||||
assert!(expressions.len() == 1);
|
||||
if let PipelineElement::Expression(expr) = &expressions[0] {
|
||||
if let PipelineElement::Expression(_, expr) = &expressions[0] {
|
||||
assert_eq!(expr.expr, Expr::Binary(vec![0o2]))
|
||||
} else {
|
||||
panic!("Not an expression")
|
||||
@ -180,7 +183,7 @@ pub fn parse_binary_with_invalid_octal_format() {
|
||||
assert!(block.len() == 1);
|
||||
let expressions = &block[0];
|
||||
assert!(expressions.len() == 1);
|
||||
if let PipelineElement::Expression(expr) = &expressions[0] {
|
||||
if let PipelineElement::Expression(_, expr) = &expressions[0] {
|
||||
assert!(!matches!(&expr.expr, Expr::Binary(_)))
|
||||
} else {
|
||||
panic!("Not an expression")
|
||||
@ -200,7 +203,7 @@ pub fn parse_binary_with_multi_byte_char() {
|
||||
assert!(block.len() == 1);
|
||||
let expressions = &block[0];
|
||||
assert!(expressions.len() == 1);
|
||||
if let PipelineElement::Expression(expr) = &expressions[0] {
|
||||
if let PipelineElement::Expression(_, expr) = &expressions[0] {
|
||||
assert!(!matches!(&expr.expr, Expr::Binary(_)))
|
||||
} else {
|
||||
panic!("Not an expression")
|
||||
@ -218,7 +221,7 @@ pub fn parse_string() {
|
||||
assert!(block.len() == 1);
|
||||
let expressions = &block[0];
|
||||
assert!(expressions.len() == 1);
|
||||
if let PipelineElement::Expression(expr) = &expressions[0] {
|
||||
if let PipelineElement::Expression(_, expr) = &expressions[0] {
|
||||
assert_eq!(expr.expr, Expr::String("hello nushell".to_string()))
|
||||
} else {
|
||||
panic!("Not an expression")
|
||||
@ -242,7 +245,7 @@ pub fn parse_escaped_string() {
|
||||
assert!(block.len() == 1);
|
||||
let expressions = &block[0];
|
||||
assert!(expressions.len() == 1);
|
||||
if let PipelineElement::Expression(expr) = &expressions[0] {
|
||||
if let PipelineElement::Expression(_, expr) = &expressions[0] {
|
||||
assert_eq!(expr.expr, Expr::String("hello nushell".to_string()))
|
||||
} else {
|
||||
panic!("Not an expression")
|
||||
@ -265,10 +268,13 @@ pub fn parse_call() {
|
||||
let expressions = &block[0];
|
||||
assert_eq!(expressions.len(), 1);
|
||||
|
||||
if let PipelineElement::Expression(Expression {
|
||||
expr: Expr::Call(call),
|
||||
..
|
||||
}) = &expressions[0]
|
||||
if let PipelineElement::Expression(
|
||||
_,
|
||||
Expression {
|
||||
expr: Expr::Call(call),
|
||||
..
|
||||
},
|
||||
) = &expressions[0]
|
||||
{
|
||||
assert_eq!(call.decl_id, 0);
|
||||
}
|
||||
@ -371,10 +377,13 @@ fn test_nothing_comparisson_eq() {
|
||||
assert!(expressions.len() == 1);
|
||||
assert!(matches!(
|
||||
&expressions[0],
|
||||
PipelineElement::Expression(Expression {
|
||||
expr: Expr::BinaryOp(..),
|
||||
..
|
||||
})
|
||||
PipelineElement::Expression(
|
||||
_,
|
||||
Expression {
|
||||
expr: Expr::BinaryOp(..),
|
||||
..
|
||||
}
|
||||
)
|
||||
))
|
||||
}
|
||||
|
||||
@ -391,10 +400,13 @@ fn test_nothing_comparisson_neq() {
|
||||
assert!(expressions.len() == 1);
|
||||
assert!(matches!(
|
||||
&expressions[0],
|
||||
PipelineElement::Expression(Expression {
|
||||
expr: Expr::BinaryOp(..),
|
||||
..
|
||||
})
|
||||
PipelineElement::Expression(
|
||||
_,
|
||||
Expression {
|
||||
expr: Expr::BinaryOp(..),
|
||||
..
|
||||
}
|
||||
)
|
||||
))
|
||||
}
|
||||
|
||||
@ -416,18 +428,21 @@ mod range {
|
||||
assert!(expressions.len() == 1);
|
||||
assert!(matches!(
|
||||
expressions[0],
|
||||
PipelineElement::Expression(Expression {
|
||||
expr: Expr::Range(
|
||||
Some(_),
|
||||
None,
|
||||
Some(_),
|
||||
RangeOperator {
|
||||
inclusion: RangeInclusion::Inclusive,
|
||||
..
|
||||
}
|
||||
),
|
||||
..
|
||||
})
|
||||
PipelineElement::Expression(
|
||||
_,
|
||||
Expression {
|
||||
expr: Expr::Range(
|
||||
Some(_),
|
||||
None,
|
||||
Some(_),
|
||||
RangeOperator {
|
||||
inclusion: RangeInclusion::Inclusive,
|
||||
..
|
||||
}
|
||||
),
|
||||
..
|
||||
}
|
||||
)
|
||||
))
|
||||
}
|
||||
|
||||
@ -445,18 +460,21 @@ mod range {
|
||||
assert!(expressions.len() == 1);
|
||||
assert!(matches!(
|
||||
expressions[0],
|
||||
PipelineElement::Expression(Expression {
|
||||
expr: Expr::Range(
|
||||
Some(_),
|
||||
None,
|
||||
Some(_),
|
||||
RangeOperator {
|
||||
inclusion: RangeInclusion::RightExclusive,
|
||||
..
|
||||
}
|
||||
),
|
||||
..
|
||||
})
|
||||
PipelineElement::Expression(
|
||||
_,
|
||||
Expression {
|
||||
expr: Expr::Range(
|
||||
Some(_),
|
||||
None,
|
||||
Some(_),
|
||||
RangeOperator {
|
||||
inclusion: RangeInclusion::RightExclusive,
|
||||
..
|
||||
}
|
||||
),
|
||||
..
|
||||
}
|
||||
)
|
||||
))
|
||||
}
|
||||
|
||||
@ -474,18 +492,21 @@ mod range {
|
||||
assert!(expressions.len() == 1);
|
||||
assert!(matches!(
|
||||
expressions[0],
|
||||
PipelineElement::Expression(Expression {
|
||||
expr: Expr::Range(
|
||||
Some(_),
|
||||
None,
|
||||
Some(_),
|
||||
RangeOperator {
|
||||
inclusion: RangeInclusion::Inclusive,
|
||||
..
|
||||
}
|
||||
),
|
||||
..
|
||||
})
|
||||
PipelineElement::Expression(
|
||||
_,
|
||||
Expression {
|
||||
expr: Expr::Range(
|
||||
Some(_),
|
||||
None,
|
||||
Some(_),
|
||||
RangeOperator {
|
||||
inclusion: RangeInclusion::Inclusive,
|
||||
..
|
||||
}
|
||||
),
|
||||
..
|
||||
}
|
||||
)
|
||||
))
|
||||
}
|
||||
|
||||
@ -503,18 +524,21 @@ mod range {
|
||||
assert!(expressions.len() == 1);
|
||||
assert!(matches!(
|
||||
expressions[0],
|
||||
PipelineElement::Expression(Expression {
|
||||
expr: Expr::Range(
|
||||
Some(_),
|
||||
None,
|
||||
Some(_),
|
||||
RangeOperator {
|
||||
inclusion: RangeInclusion::RightExclusive,
|
||||
..
|
||||
}
|
||||
),
|
||||
..
|
||||
})
|
||||
PipelineElement::Expression(
|
||||
_,
|
||||
Expression {
|
||||
expr: Expr::Range(
|
||||
Some(_),
|
||||
None,
|
||||
Some(_),
|
||||
RangeOperator {
|
||||
inclusion: RangeInclusion::RightExclusive,
|
||||
..
|
||||
}
|
||||
),
|
||||
..
|
||||
}
|
||||
)
|
||||
))
|
||||
}
|
||||
|
||||
@ -534,18 +558,21 @@ mod range {
|
||||
assert!(expressions.len() == 1);
|
||||
assert!(matches!(
|
||||
expressions[0],
|
||||
PipelineElement::Expression(Expression {
|
||||
expr: Expr::Range(
|
||||
Some(_),
|
||||
None,
|
||||
Some(_),
|
||||
RangeOperator {
|
||||
inclusion: RangeInclusion::Inclusive,
|
||||
..
|
||||
}
|
||||
),
|
||||
..
|
||||
})
|
||||
PipelineElement::Expression(
|
||||
_,
|
||||
Expression {
|
||||
expr: Expr::Range(
|
||||
Some(_),
|
||||
None,
|
||||
Some(_),
|
||||
RangeOperator {
|
||||
inclusion: RangeInclusion::Inclusive,
|
||||
..
|
||||
}
|
||||
),
|
||||
..
|
||||
}
|
||||
)
|
||||
))
|
||||
}
|
||||
|
||||
@ -571,18 +598,21 @@ mod range {
|
||||
assert!(expressions.len() == 1);
|
||||
assert!(matches!(
|
||||
expressions[0],
|
||||
PipelineElement::Expression(Expression {
|
||||
expr: Expr::Range(
|
||||
Some(_),
|
||||
None,
|
||||
Some(_),
|
||||
RangeOperator {
|
||||
inclusion: RangeInclusion::RightExclusive,
|
||||
..
|
||||
}
|
||||
),
|
||||
..
|
||||
})
|
||||
PipelineElement::Expression(
|
||||
_,
|
||||
Expression {
|
||||
expr: Expr::Range(
|
||||
Some(_),
|
||||
None,
|
||||
Some(_),
|
||||
RangeOperator {
|
||||
inclusion: RangeInclusion::RightExclusive,
|
||||
..
|
||||
}
|
||||
),
|
||||
..
|
||||
}
|
||||
)
|
||||
))
|
||||
}
|
||||
|
||||
@ -600,18 +630,21 @@ mod range {
|
||||
assert!(expressions.len() == 1);
|
||||
assert!(matches!(
|
||||
expressions[0],
|
||||
PipelineElement::Expression(Expression {
|
||||
expr: Expr::Range(
|
||||
Some(_),
|
||||
None,
|
||||
None,
|
||||
RangeOperator {
|
||||
inclusion: RangeInclusion::Inclusive,
|
||||
..
|
||||
}
|
||||
),
|
||||
..
|
||||
})
|
||||
PipelineElement::Expression(
|
||||
_,
|
||||
Expression {
|
||||
expr: Expr::Range(
|
||||
Some(_),
|
||||
None,
|
||||
None,
|
||||
RangeOperator {
|
||||
inclusion: RangeInclusion::Inclusive,
|
||||
..
|
||||
}
|
||||
),
|
||||
..
|
||||
}
|
||||
)
|
||||
))
|
||||
}
|
||||
|
||||
@ -629,18 +662,21 @@ mod range {
|
||||
assert!(expressions.len() == 1);
|
||||
assert!(matches!(
|
||||
expressions[0],
|
||||
PipelineElement::Expression(Expression {
|
||||
expr: Expr::Range(
|
||||
None,
|
||||
None,
|
||||
Some(_),
|
||||
RangeOperator {
|
||||
inclusion: RangeInclusion::Inclusive,
|
||||
..
|
||||
}
|
||||
),
|
||||
..
|
||||
})
|
||||
PipelineElement::Expression(
|
||||
_,
|
||||
Expression {
|
||||
expr: Expr::Range(
|
||||
None,
|
||||
None,
|
||||
Some(_),
|
||||
RangeOperator {
|
||||
inclusion: RangeInclusion::Inclusive,
|
||||
..
|
||||
}
|
||||
),
|
||||
..
|
||||
}
|
||||
)
|
||||
))
|
||||
}
|
||||
|
||||
@ -658,18 +694,21 @@ mod range {
|
||||
assert!(expressions.len() == 1);
|
||||
assert!(matches!(
|
||||
expressions[0],
|
||||
PipelineElement::Expression(Expression {
|
||||
expr: Expr::Range(
|
||||
Some(_),
|
||||
None,
|
||||
Some(_),
|
||||
RangeOperator {
|
||||
inclusion: RangeInclusion::Inclusive,
|
||||
..
|
||||
}
|
||||
),
|
||||
..
|
||||
})
|
||||
PipelineElement::Expression(
|
||||
_,
|
||||
Expression {
|
||||
expr: Expr::Range(
|
||||
Some(_),
|
||||
None,
|
||||
Some(_),
|
||||
RangeOperator {
|
||||
inclusion: RangeInclusion::Inclusive,
|
||||
..
|
||||
}
|
||||
),
|
||||
..
|
||||
}
|
||||
)
|
||||
))
|
||||
}
|
||||
|
||||
@ -687,18 +726,21 @@ mod range {
|
||||
assert!(expressions.len() == 1);
|
||||
assert!(matches!(
|
||||
expressions[0],
|
||||
PipelineElement::Expression(Expression {
|
||||
expr: Expr::Range(
|
||||
Some(_),
|
||||
Some(_),
|
||||
Some(_),
|
||||
RangeOperator {
|
||||
inclusion: RangeInclusion::Inclusive,
|
||||
..
|
||||
}
|
||||
),
|
||||
..
|
||||
})
|
||||
PipelineElement::Expression(
|
||||
_,
|
||||
Expression {
|
||||
expr: Expr::Range(
|
||||
Some(_),
|
||||
Some(_),
|
||||
Some(_),
|
||||
RangeOperator {
|
||||
inclusion: RangeInclusion::Inclusive,
|
||||
..
|
||||
}
|
||||
),
|
||||
..
|
||||
}
|
||||
)
|
||||
))
|
||||
}
|
||||
|
||||
@ -1032,10 +1074,13 @@ mod input_types {
|
||||
assert!(expressions.len() == 3);
|
||||
|
||||
match &expressions[0] {
|
||||
PipelineElement::Expression(Expression {
|
||||
expr: Expr::Call(call),
|
||||
..
|
||||
}) => {
|
||||
PipelineElement::Expression(
|
||||
_,
|
||||
Expression {
|
||||
expr: Expr::Call(call),
|
||||
..
|
||||
},
|
||||
) => {
|
||||
let expected_id = working_set
|
||||
.find_decl(b"ls", &Type::Any)
|
||||
.expect("Error merging delta");
|
||||
@ -1045,10 +1090,13 @@ mod input_types {
|
||||
}
|
||||
|
||||
match &expressions[1] {
|
||||
PipelineElement::Expression(Expression {
|
||||
expr: Expr::Call(call),
|
||||
..
|
||||
}) => {
|
||||
PipelineElement::Expression(
|
||||
_,
|
||||
Expression {
|
||||
expr: Expr::Call(call),
|
||||
..
|
||||
},
|
||||
) => {
|
||||
let expected_id = working_set.find_decl(b"to-custom", &Type::Any).unwrap();
|
||||
assert_eq!(call.decl_id, expected_id)
|
||||
}
|
||||
@ -1056,10 +1104,13 @@ mod input_types {
|
||||
}
|
||||
|
||||
match &expressions[2] {
|
||||
PipelineElement::Expression(Expression {
|
||||
expr: Expr::Call(call),
|
||||
..
|
||||
}) => {
|
||||
PipelineElement::Expression(
|
||||
_,
|
||||
Expression {
|
||||
expr: Expr::Call(call),
|
||||
..
|
||||
},
|
||||
) => {
|
||||
let expected_id = working_set
|
||||
.find_decl(b"group-by", &Type::Custom("custom".into()))
|
||||
.unwrap();
|
||||
@ -1085,10 +1136,13 @@ mod input_types {
|
||||
|
||||
let expressions = &block[2];
|
||||
match &expressions[1] {
|
||||
PipelineElement::Expression(Expression {
|
||||
expr: Expr::Call(call),
|
||||
..
|
||||
}) => {
|
||||
PipelineElement::Expression(
|
||||
_,
|
||||
Expression {
|
||||
expr: Expr::Call(call),
|
||||
..
|
||||
},
|
||||
) => {
|
||||
let expected_id = working_set
|
||||
.find_decl(b"agg", &Type::Custom("custom".into()))
|
||||
.unwrap();
|
||||
@ -1113,10 +1167,13 @@ mod input_types {
|
||||
|
||||
let expressions = &block[1];
|
||||
match &expressions[1] {
|
||||
PipelineElement::Expression(Expression {
|
||||
expr: Expr::Call(call),
|
||||
..
|
||||
}) => {
|
||||
PipelineElement::Expression(
|
||||
_,
|
||||
Expression {
|
||||
expr: Expr::Call(call),
|
||||
..
|
||||
},
|
||||
) => {
|
||||
let expected_id = working_set
|
||||
.find_decl(b"agg", &Type::Custom("custom".into()))
|
||||
.unwrap();
|
||||
@ -1142,10 +1199,13 @@ mod input_types {
|
||||
|
||||
let expressions = &block[1];
|
||||
match &expressions[1] {
|
||||
PipelineElement::Expression(Expression {
|
||||
expr: Expr::Call(call),
|
||||
..
|
||||
}) => {
|
||||
PipelineElement::Expression(
|
||||
_,
|
||||
Expression {
|
||||
expr: Expr::Call(call),
|
||||
..
|
||||
},
|
||||
) => {
|
||||
let expected_id = working_set.find_decl(b"to-custom", &Type::Any).unwrap();
|
||||
assert_eq!(call.decl_id, expected_id)
|
||||
}
|
||||
@ -1154,10 +1214,13 @@ mod input_types {
|
||||
|
||||
let expressions = &block[2];
|
||||
match &expressions[1] {
|
||||
PipelineElement::Expression(Expression {
|
||||
expr: Expr::Call(call),
|
||||
..
|
||||
}) => {
|
||||
PipelineElement::Expression(
|
||||
_,
|
||||
Expression {
|
||||
expr: Expr::Call(call),
|
||||
..
|
||||
},
|
||||
) => {
|
||||
let expected_id = working_set.find_decl(b"to-custom", &Type::Any).unwrap();
|
||||
assert_eq!(call.decl_id, expected_id)
|
||||
}
|
||||
@ -1182,10 +1245,13 @@ mod input_types {
|
||||
assert!(expressions.len() == 2);
|
||||
|
||||
match &expressions[0] {
|
||||
PipelineElement::Expression(Expression {
|
||||
expr: Expr::Call(call),
|
||||
..
|
||||
}) => {
|
||||
PipelineElement::Expression(
|
||||
_,
|
||||
Expression {
|
||||
expr: Expr::Call(call),
|
||||
..
|
||||
},
|
||||
) => {
|
||||
let expected_id = working_set.find_decl(b"ls", &Type::Any).unwrap();
|
||||
assert_eq!(call.decl_id, expected_id)
|
||||
}
|
||||
@ -1193,10 +1259,13 @@ mod input_types {
|
||||
}
|
||||
|
||||
match &expressions[1] {
|
||||
PipelineElement::Expression(Expression {
|
||||
expr: Expr::Call(call),
|
||||
..
|
||||
}) => {
|
||||
PipelineElement::Expression(
|
||||
_,
|
||||
Expression {
|
||||
expr: Expr::Call(call),
|
||||
..
|
||||
},
|
||||
) => {
|
||||
let expected_id = working_set.find_decl(b"group-by", &Type::Any).unwrap();
|
||||
assert_eq!(call.decl_id, expected_id)
|
||||
}
|
||||
@ -1221,10 +1290,13 @@ mod input_types {
|
||||
|
||||
let expressions = &block[0];
|
||||
match &expressions[3] {
|
||||
PipelineElement::Expression(Expression {
|
||||
expr: Expr::Call(call),
|
||||
..
|
||||
}) => {
|
||||
PipelineElement::Expression(
|
||||
_,
|
||||
Expression {
|
||||
expr: Expr::Call(call),
|
||||
..
|
||||
},
|
||||
) => {
|
||||
let arg = &call.arguments[0];
|
||||
match arg {
|
||||
Argument::Positional(a) => match &a.expr {
|
||||
@ -1236,10 +1308,13 @@ mod input_types {
|
||||
assert!(expressions.len() == 2);
|
||||
|
||||
match &expressions[1] {
|
||||
PipelineElement::Expression(Expression {
|
||||
expr: Expr::Call(call),
|
||||
..
|
||||
}) => {
|
||||
PipelineElement::Expression(
|
||||
_,
|
||||
Expression {
|
||||
expr: Expr::Call(call),
|
||||
..
|
||||
},
|
||||
) => {
|
||||
let working_set = StateWorkingSet::new(&engine_state);
|
||||
let expected_id =
|
||||
working_set.find_decl(b"min", &Type::Any).unwrap();
|
||||
@ -1274,10 +1349,13 @@ mod input_types {
|
||||
|
||||
let expressions = &block[0];
|
||||
match &expressions[2] {
|
||||
PipelineElement::Expression(Expression {
|
||||
expr: Expr::Call(call),
|
||||
..
|
||||
}) => {
|
||||
PipelineElement::Expression(
|
||||
_,
|
||||
Expression {
|
||||
expr: Expr::Call(call),
|
||||
..
|
||||
},
|
||||
) => {
|
||||
let expected_id = working_set
|
||||
.find_decl(b"with-column", &Type::Custom("custom".into()))
|
||||
.unwrap();
|
||||
@ -1287,10 +1365,13 @@ mod input_types {
|
||||
}
|
||||
|
||||
match &expressions[3] {
|
||||
PipelineElement::Expression(Expression {
|
||||
expr: Expr::Call(call),
|
||||
..
|
||||
}) => {
|
||||
PipelineElement::Expression(
|
||||
_,
|
||||
Expression {
|
||||
expr: Expr::Call(call),
|
||||
..
|
||||
},
|
||||
) => {
|
||||
let expected_id = working_set
|
||||
.find_decl(b"collect", &Type::Custom("custom".into()))
|
||||
.unwrap();
|
||||
|
@ -2,38 +2,50 @@ use std::ops::{Index, IndexMut};
|
||||
|
||||
use crate::{ast::Expression, engine::StateWorkingSet, Span, VarId};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Redirection {
|
||||
Stdout,
|
||||
Stderr,
|
||||
StdoutAndStderr,
|
||||
}
|
||||
|
||||
// Note: Span in the below is for the span of the connector not the whole element
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum PipelineElement {
|
||||
Expression(Expression),
|
||||
Redirect(Expression),
|
||||
And(Expression),
|
||||
Or(Expression),
|
||||
Expression(Option<Span>, Expression),
|
||||
Redirection(Span, Redirection, Expression),
|
||||
And(Span, Expression),
|
||||
Or(Span, Expression),
|
||||
}
|
||||
|
||||
impl PipelineElement {
|
||||
pub fn span(&self) -> Span {
|
||||
match self {
|
||||
PipelineElement::Expression(expression)
|
||||
| PipelineElement::Redirect(expression)
|
||||
| PipelineElement::And(expression)
|
||||
| PipelineElement::Or(expression) => expression.span,
|
||||
PipelineElement::Expression(None, expression) => expression.span,
|
||||
PipelineElement::Expression(Some(span), expression)
|
||||
| PipelineElement::Redirection(span, _, expression)
|
||||
| PipelineElement::And(span, expression)
|
||||
| PipelineElement::Or(span, expression) => Span {
|
||||
start: span.start,
|
||||
end: expression.span.end,
|
||||
},
|
||||
}
|
||||
}
|
||||
pub fn has_in_variable(&self, working_set: &StateWorkingSet) -> bool {
|
||||
match self {
|
||||
PipelineElement::Expression(expression)
|
||||
| PipelineElement::Redirect(expression)
|
||||
| PipelineElement::And(expression)
|
||||
| PipelineElement::Or(expression) => expression.has_in_variable(working_set),
|
||||
PipelineElement::Expression(_, expression)
|
||||
| PipelineElement::Redirection(_, _, expression)
|
||||
| PipelineElement::And(_, expression)
|
||||
| PipelineElement::Or(_, expression) => expression.has_in_variable(working_set),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn replace_in_variable(&mut self, working_set: &mut StateWorkingSet, new_var_id: VarId) {
|
||||
match self {
|
||||
PipelineElement::Expression(expression)
|
||||
| PipelineElement::Redirect(expression)
|
||||
| PipelineElement::And(expression)
|
||||
| PipelineElement::Or(expression) => {
|
||||
PipelineElement::Expression(_, expression)
|
||||
| PipelineElement::Redirection(_, _, expression)
|
||||
| PipelineElement::And(_, expression)
|
||||
| PipelineElement::Or(_, expression) => {
|
||||
expression.replace_in_variable(working_set, new_var_id)
|
||||
}
|
||||
}
|
||||
@ -46,10 +58,10 @@ impl PipelineElement {
|
||||
new_span: Span,
|
||||
) {
|
||||
match self {
|
||||
PipelineElement::Expression(expression)
|
||||
| PipelineElement::Redirect(expression)
|
||||
| PipelineElement::And(expression)
|
||||
| PipelineElement::Or(expression) => {
|
||||
PipelineElement::Expression(_, expression)
|
||||
| PipelineElement::Redirection(_, _, expression)
|
||||
| PipelineElement::And(_, expression)
|
||||
| PipelineElement::Or(_, expression) => {
|
||||
expression.replace_span(working_set, replaced, new_span)
|
||||
}
|
||||
}
|
||||
@ -76,7 +88,10 @@ impl Pipeline {
|
||||
Self {
|
||||
elements: expressions
|
||||
.into_iter()
|
||||
.map(PipelineElement::Expression)
|
||||
.enumerate()
|
||||
.map(|(idx, x)| {
|
||||
PipelineElement::Expression(if idx == 0 { None } else { Some(x.span) }, x)
|
||||
})
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
@ -53,6 +53,20 @@ impl RawStream {
|
||||
|
||||
Ok(Spanned { item: output, span })
|
||||
}
|
||||
|
||||
pub fn chain(self, stream: RawStream) -> RawStream {
|
||||
RawStream {
|
||||
stream: Box::new(self.stream.chain(stream.stream)),
|
||||
leftover: self
|
||||
.leftover
|
||||
.into_iter()
|
||||
.chain(stream.leftover.into_iter())
|
||||
.collect(),
|
||||
ctrlc: self.ctrlc,
|
||||
is_binary: self.is_binary,
|
||||
span: self.span,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Debug for RawStream {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
|
11
src/main.rs
11
src/main.rs
@ -511,10 +511,13 @@ fn parse_commandline_args(
|
||||
|
||||
// We should have a successful parse now
|
||||
if let Some(pipeline) = block.pipelines.get(0) {
|
||||
if let Some(PipelineElement::Expression(Expression {
|
||||
expr: Expr::Call(call),
|
||||
..
|
||||
})) = pipeline.elements.get(0)
|
||||
if let Some(PipelineElement::Expression(
|
||||
_,
|
||||
Expression {
|
||||
expr: Expr::Call(call),
|
||||
..
|
||||
},
|
||||
)) = pipeline.elements.get(0)
|
||||
{
|
||||
let redirect_stdin = call.get_named_arg("stdin");
|
||||
let login_shell = call.get_named_arg("login");
|
||||
|
Loading…
Reference in New Issue
Block a user