forked from extern/nushell
Fix external extra (#4777)
* Fix empty table from externals * Fix empty table from externals
This commit is contained in:
parent
35ff1076f3
commit
299fea8538
@ -18,7 +18,11 @@ pub fn print_pipeline_data(
|
|||||||
|
|
||||||
let stdout = std::io::stdout();
|
let stdout = std::io::stdout();
|
||||||
|
|
||||||
if let PipelineData::ExternalStream { stdout: stream, .. } = input {
|
if let PipelineData::ExternalStream {
|
||||||
|
stdout: Some(stream),
|
||||||
|
..
|
||||||
|
} = input
|
||||||
|
{
|
||||||
for s in stream {
|
for s in stream {
|
||||||
let _ = stdout.lock().write_all(s?.as_binary()?);
|
let _ = stdout.lock().write_all(s?.as_binary()?);
|
||||||
}
|
}
|
||||||
|
@ -99,7 +99,15 @@ fn into_binary(
|
|||||||
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
||||||
|
|
||||||
match input {
|
match input {
|
||||||
PipelineData::ExternalStream { stdout: stream, .. } => {
|
PipelineData::ExternalStream { stdout: None, .. } => Ok(Value::Binary {
|
||||||
|
val: vec![],
|
||||||
|
span: head,
|
||||||
|
}
|
||||||
|
.into_pipeline_data()),
|
||||||
|
PipelineData::ExternalStream {
|
||||||
|
stdout: Some(stream),
|
||||||
|
..
|
||||||
|
} => {
|
||||||
// TODO: in the future, we may want this to stream out, converting each to bytes
|
// TODO: in the future, we may want this to stream out, converting each to bytes
|
||||||
let output = stream.into_bytes()?;
|
let output = stream.into_bytes()?;
|
||||||
Ok(Value::Binary {
|
Ok(Value::Binary {
|
||||||
|
@ -150,7 +150,15 @@ fn string_helper(
|
|||||||
}
|
}
|
||||||
|
|
||||||
match input {
|
match input {
|
||||||
PipelineData::ExternalStream { stdout: stream, .. } => {
|
PipelineData::ExternalStream { stdout: None, .. } => Ok(Value::String {
|
||||||
|
val: String::new(),
|
||||||
|
span: head,
|
||||||
|
}
|
||||||
|
.into_pipeline_data()),
|
||||||
|
PipelineData::ExternalStream {
|
||||||
|
stdout: Some(stream),
|
||||||
|
..
|
||||||
|
} => {
|
||||||
// TODO: in the future, we may want this to stream out, converting each to bytes
|
// TODO: in the future, we may want this to stream out, converting each to bytes
|
||||||
let output = stream.into_string()?;
|
let output = stream.into_string()?;
|
||||||
Ok(Value::String {
|
Ok(Value::String {
|
||||||
|
@ -121,11 +121,11 @@ impl Command for Open {
|
|||||||
let buf_reader = BufReader::new(file);
|
let buf_reader = BufReader::new(file);
|
||||||
|
|
||||||
let output = PipelineData::ExternalStream {
|
let output = PipelineData::ExternalStream {
|
||||||
stdout: RawStream::new(
|
stdout: Some(RawStream::new(
|
||||||
Box::new(BufferedReader { input: buf_reader }),
|
Box::new(BufferedReader { input: buf_reader }),
|
||||||
ctrlc,
|
ctrlc,
|
||||||
call_span,
|
call_span,
|
||||||
),
|
)),
|
||||||
stderr: None,
|
stderr: None,
|
||||||
exit_code: None,
|
exit_code: None,
|
||||||
span: call_span,
|
span: call_span,
|
||||||
|
@ -169,7 +169,11 @@ impl Command for Each {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.into_pipeline_data(ctrlc)),
|
.into_pipeline_data(ctrlc)),
|
||||||
PipelineData::ExternalStream { stdout: stream, .. } => Ok(stream
|
PipelineData::ExternalStream { stdout: None, .. } => Ok(PipelineData::new(call.head)),
|
||||||
|
PipelineData::ExternalStream {
|
||||||
|
stdout: Some(stream),
|
||||||
|
..
|
||||||
|
} => Ok(stream
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(move |(idx, x)| {
|
.map(move |(idx, x)| {
|
||||||
|
@ -213,7 +213,11 @@ impl Command for ParEach {
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.flatten()
|
.flatten()
|
||||||
.into_pipeline_data(ctrlc)),
|
.into_pipeline_data(ctrlc)),
|
||||||
PipelineData::ExternalStream { stdout: stream, .. } => Ok(stream
|
PipelineData::ExternalStream { stdout: None, .. } => Ok(PipelineData::new(call.head)),
|
||||||
|
PipelineData::ExternalStream {
|
||||||
|
stdout: Some(stream),
|
||||||
|
..
|
||||||
|
} => Ok(stream
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.par_bridge()
|
.par_bridge()
|
||||||
.map(move |(idx, x)| {
|
.map(move |(idx, x)| {
|
||||||
|
@ -77,7 +77,7 @@ impl Command for Skip {
|
|||||||
|
|
||||||
match input {
|
match input {
|
||||||
PipelineData::ExternalStream {
|
PipelineData::ExternalStream {
|
||||||
stdout: stream,
|
stdout: Some(stream),
|
||||||
span: bytes_span,
|
span: bytes_span,
|
||||||
metadata,
|
metadata,
|
||||||
..
|
..
|
||||||
|
@ -357,13 +357,13 @@ fn response_to_buffer(
|
|||||||
let buffered_input = BufReader::new(response);
|
let buffered_input = BufReader::new(response);
|
||||||
|
|
||||||
PipelineData::ExternalStream {
|
PipelineData::ExternalStream {
|
||||||
stdout: RawStream::new(
|
stdout: Some(RawStream::new(
|
||||||
Box::new(BufferedReader {
|
Box::new(BufferedReader {
|
||||||
input: buffered_input,
|
input: buffered_input,
|
||||||
}),
|
}),
|
||||||
engine_state.ctrlc.clone(),
|
engine_state.ctrlc.clone(),
|
||||||
span,
|
span,
|
||||||
),
|
)),
|
||||||
stderr: None,
|
stderr: None,
|
||||||
exit_code: None,
|
exit_code: None,
|
||||||
span,
|
span,
|
||||||
|
@ -418,13 +418,13 @@ fn response_to_buffer(
|
|||||||
let buffered_input = BufReader::new(response);
|
let buffered_input = BufReader::new(response);
|
||||||
|
|
||||||
PipelineData::ExternalStream {
|
PipelineData::ExternalStream {
|
||||||
stdout: RawStream::new(
|
stdout: Some(RawStream::new(
|
||||||
Box::new(BufferedReader {
|
Box::new(BufferedReader {
|
||||||
input: buffered_input,
|
input: buffered_input,
|
||||||
}),
|
}),
|
||||||
engine_state.ctrlc.clone(),
|
engine_state.ctrlc.clone(),
|
||||||
span,
|
span,
|
||||||
),
|
)),
|
||||||
stderr: None,
|
stderr: None,
|
||||||
exit_code: None,
|
exit_code: None,
|
||||||
span,
|
span,
|
||||||
|
@ -44,7 +44,11 @@ impl Command for Decode {
|
|||||||
let encoding: Spanned<String> = call.req(engine_state, stack, 0)?;
|
let encoding: Spanned<String> = call.req(engine_state, stack, 0)?;
|
||||||
|
|
||||||
match input {
|
match input {
|
||||||
PipelineData::ExternalStream { stdout: stream, .. } => {
|
PipelineData::ExternalStream { stdout: None, .. } => Ok(PipelineData::new(call.head)),
|
||||||
|
PipelineData::ExternalStream {
|
||||||
|
stdout: Some(stream),
|
||||||
|
..
|
||||||
|
} => {
|
||||||
let bytes: Vec<u8> = stream.into_bytes()?.item;
|
let bytes: Vec<u8> = stream.into_bytes()?.item;
|
||||||
|
|
||||||
let encoding = match Encoding::for_label(encoding.item.as_bytes()) {
|
let encoding = match Encoding::for_label(encoding.item.as_bytes()) {
|
||||||
|
@ -34,21 +34,24 @@ impl Command for Complete {
|
|||||||
exit_code,
|
exit_code,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
let mut cols = vec!["stdout".to_string()];
|
let mut cols = vec![];
|
||||||
let mut vals = vec![];
|
let mut vals = vec![];
|
||||||
|
|
||||||
let stdout = stdout.into_bytes()?;
|
if let Some(stdout) = stdout {
|
||||||
if let Ok(st) = String::from_utf8(stdout.item.clone()) {
|
cols.push("stdout".to_string());
|
||||||
vals.push(Value::String {
|
let stdout = stdout.into_bytes()?;
|
||||||
val: st,
|
if let Ok(st) = String::from_utf8(stdout.item.clone()) {
|
||||||
span: stdout.span,
|
vals.push(Value::String {
|
||||||
})
|
val: st,
|
||||||
} else {
|
span: stdout.span,
|
||||||
vals.push(Value::Binary {
|
})
|
||||||
val: stdout.item,
|
} else {
|
||||||
span: stdout.span,
|
vals.push(Value::Binary {
|
||||||
})
|
val: stdout.item,
|
||||||
};
|
span: stdout.span,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(stderr) = stderr {
|
if let Some(stderr) = stderr {
|
||||||
cols.push("stderr".to_string());
|
cols.push("stderr".to_string());
|
||||||
|
@ -307,7 +307,15 @@ impl ExternalCommand {
|
|||||||
let exit_code_receiver = ValueReceiver::new(exit_code_rx);
|
let exit_code_receiver = ValueReceiver::new(exit_code_rx);
|
||||||
|
|
||||||
Ok(PipelineData::ExternalStream {
|
Ok(PipelineData::ExternalStream {
|
||||||
stdout: RawStream::new(Box::new(stdout_receiver), output_ctrlc.clone(), head),
|
stdout: if redirect_stdout {
|
||||||
|
Some(RawStream::new(
|
||||||
|
Box::new(stdout_receiver),
|
||||||
|
output_ctrlc.clone(),
|
||||||
|
head,
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
stderr: Some(RawStream::new(
|
stderr: Some(RawStream::new(
|
||||||
Box::new(stderr_receiver),
|
Box::new(stderr_receiver),
|
||||||
output_ctrlc.clone(),
|
output_ctrlc.clone(),
|
||||||
|
@ -67,7 +67,7 @@ impl Command for Table {
|
|||||||
PipelineData::ExternalStream { .. } => Ok(input),
|
PipelineData::ExternalStream { .. } => Ok(input),
|
||||||
PipelineData::Value(Value::Binary { val, .. }, ..) => {
|
PipelineData::Value(Value::Binary { val, .. }, ..) => {
|
||||||
Ok(PipelineData::ExternalStream {
|
Ok(PipelineData::ExternalStream {
|
||||||
stdout: RawStream::new(
|
stdout: Some(RawStream::new(
|
||||||
Box::new(
|
Box::new(
|
||||||
vec![Ok(format!("{}\n", nu_pretty_hex::pretty_hex(&val))
|
vec![Ok(format!("{}\n", nu_pretty_hex::pretty_hex(&val))
|
||||||
.as_bytes()
|
.as_bytes()
|
||||||
@ -76,7 +76,7 @@ impl Command for Table {
|
|||||||
),
|
),
|
||||||
ctrlc,
|
ctrlc,
|
||||||
head,
|
head,
|
||||||
),
|
)),
|
||||||
stderr: None,
|
stderr: None,
|
||||||
exit_code: None,
|
exit_code: None,
|
||||||
span: head,
|
span: head,
|
||||||
@ -269,7 +269,7 @@ fn handle_row_stream(
|
|||||||
let head = call.head;
|
let head = call.head;
|
||||||
|
|
||||||
Ok(PipelineData::ExternalStream {
|
Ok(PipelineData::ExternalStream {
|
||||||
stdout: RawStream::new(
|
stdout: Some(RawStream::new(
|
||||||
Box::new(PagingTableCreator {
|
Box::new(PagingTableCreator {
|
||||||
row_offset,
|
row_offset,
|
||||||
config,
|
config,
|
||||||
@ -279,7 +279,7 @@ fn handle_row_stream(
|
|||||||
}),
|
}),
|
||||||
ctrlc,
|
ctrlc,
|
||||||
head,
|
head,
|
||||||
),
|
)),
|
||||||
stderr: None,
|
stderr: None,
|
||||||
exit_code: None,
|
exit_code: None,
|
||||||
span: head,
|
span: head,
|
||||||
|
@ -47,6 +47,7 @@ mod reverse;
|
|||||||
mod rm;
|
mod rm;
|
||||||
mod roll;
|
mod roll;
|
||||||
mod rotate;
|
mod rotate;
|
||||||
|
mod run_external;
|
||||||
mod save;
|
mod save;
|
||||||
mod select;
|
mod select;
|
||||||
mod semicolon;
|
mod semicolon;
|
||||||
|
15
crates/nu-command/tests/commands/run_external.rs
Normal file
15
crates/nu-command/tests/commands/run_external.rs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
use nu_test_support::{nu, pipeline};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn better_empty_redirection() {
|
||||||
|
let actual = nu!(
|
||||||
|
cwd: "tests/fixtures/formats", pipeline(
|
||||||
|
r#"
|
||||||
|
ls | each { |it| nu --testbin cococo $it.name }
|
||||||
|
"#
|
||||||
|
));
|
||||||
|
|
||||||
|
eprintln!("out: {}", actual.out);
|
||||||
|
|
||||||
|
assert!(!actual.out.contains('2'));
|
||||||
|
}
|
@ -36,7 +36,7 @@ pub enum PipelineData {
|
|||||||
Value(Value, Option<PipelineMetadata>),
|
Value(Value, Option<PipelineMetadata>),
|
||||||
ListStream(ListStream, Option<PipelineMetadata>),
|
ListStream(ListStream, Option<PipelineMetadata>),
|
||||||
ExternalStream {
|
ExternalStream {
|
||||||
stdout: RawStream,
|
stdout: Option<RawStream>,
|
||||||
stderr: Option<RawStream>,
|
stderr: Option<RawStream>,
|
||||||
exit_code: Option<ListStream>,
|
exit_code: Option<ListStream>,
|
||||||
span: Span,
|
span: Span,
|
||||||
@ -93,7 +93,11 @@ impl PipelineData {
|
|||||||
vals: s.collect(),
|
vals: s.collect(),
|
||||||
span, // FIXME?
|
span, // FIXME?
|
||||||
},
|
},
|
||||||
PipelineData::ExternalStream { stdout: mut s, .. } => {
|
PipelineData::ExternalStream { stdout: None, .. } => Value::Nothing { span },
|
||||||
|
PipelineData::ExternalStream {
|
||||||
|
stdout: Some(mut s),
|
||||||
|
..
|
||||||
|
} => {
|
||||||
let mut items = vec![];
|
let mut items = vec![];
|
||||||
|
|
||||||
for val in &mut s {
|
for val in &mut s {
|
||||||
@ -157,7 +161,10 @@ impl PipelineData {
|
|||||||
match self {
|
match self {
|
||||||
PipelineData::Value(v, ..) => Ok(v.into_string(separator, config)),
|
PipelineData::Value(v, ..) => Ok(v.into_string(separator, config)),
|
||||||
PipelineData::ListStream(s, ..) => Ok(s.into_string(separator, config)),
|
PipelineData::ListStream(s, ..) => Ok(s.into_string(separator, config)),
|
||||||
PipelineData::ExternalStream { stdout: s, .. } => {
|
PipelineData::ExternalStream { stdout: None, .. } => Ok(String::new()),
|
||||||
|
PipelineData::ExternalStream {
|
||||||
|
stdout: Some(s), ..
|
||||||
|
} => {
|
||||||
let mut items = vec![];
|
let mut items = vec![];
|
||||||
|
|
||||||
for val in s {
|
for val in s {
|
||||||
@ -236,7 +243,13 @@ impl PipelineData {
|
|||||||
Ok(vals.into_iter().map(f).into_pipeline_data(ctrlc))
|
Ok(vals.into_iter().map(f).into_pipeline_data(ctrlc))
|
||||||
}
|
}
|
||||||
PipelineData::ListStream(stream, ..) => Ok(stream.map(f).into_pipeline_data(ctrlc)),
|
PipelineData::ListStream(stream, ..) => Ok(stream.map(f).into_pipeline_data(ctrlc)),
|
||||||
PipelineData::ExternalStream { stdout: stream, .. } => {
|
PipelineData::ExternalStream { stdout: None, .. } => {
|
||||||
|
Ok(PipelineData::new(Span { start: 0, end: 0 }))
|
||||||
|
}
|
||||||
|
PipelineData::ExternalStream {
|
||||||
|
stdout: Some(stream),
|
||||||
|
..
|
||||||
|
} => {
|
||||||
let collected = stream.into_bytes()?;
|
let collected = stream.into_bytes()?;
|
||||||
|
|
||||||
if let Ok(st) = String::from_utf8(collected.clone().item) {
|
if let Ok(st) = String::from_utf8(collected.clone().item) {
|
||||||
@ -283,7 +296,13 @@ impl PipelineData {
|
|||||||
PipelineData::ListStream(stream, ..) => {
|
PipelineData::ListStream(stream, ..) => {
|
||||||
Ok(stream.flat_map(f).into_pipeline_data(ctrlc))
|
Ok(stream.flat_map(f).into_pipeline_data(ctrlc))
|
||||||
}
|
}
|
||||||
PipelineData::ExternalStream { stdout: stream, .. } => {
|
PipelineData::ExternalStream { stdout: None, .. } => {
|
||||||
|
Ok(PipelineData::new(Span { start: 0, end: 0 }))
|
||||||
|
}
|
||||||
|
PipelineData::ExternalStream {
|
||||||
|
stdout: Some(stream),
|
||||||
|
..
|
||||||
|
} => {
|
||||||
let collected = stream.into_bytes()?;
|
let collected = stream.into_bytes()?;
|
||||||
|
|
||||||
if let Ok(st) = String::from_utf8(collected.clone().item) {
|
if let Ok(st) = String::from_utf8(collected.clone().item) {
|
||||||
@ -324,7 +343,13 @@ impl PipelineData {
|
|||||||
Ok(vals.into_iter().filter(f).into_pipeline_data(ctrlc))
|
Ok(vals.into_iter().filter(f).into_pipeline_data(ctrlc))
|
||||||
}
|
}
|
||||||
PipelineData::ListStream(stream, ..) => Ok(stream.filter(f).into_pipeline_data(ctrlc)),
|
PipelineData::ListStream(stream, ..) => Ok(stream.filter(f).into_pipeline_data(ctrlc)),
|
||||||
PipelineData::ExternalStream { stdout: stream, .. } => {
|
PipelineData::ExternalStream { stdout: None, .. } => {
|
||||||
|
Ok(PipelineData::new(Span { start: 0, end: 0 }))
|
||||||
|
}
|
||||||
|
PipelineData::ExternalStream {
|
||||||
|
stdout: Some(stream),
|
||||||
|
..
|
||||||
|
} => {
|
||||||
let collected = stream.into_bytes()?;
|
let collected = stream.into_bytes()?;
|
||||||
|
|
||||||
if let Ok(st) = String::from_utf8(collected.clone().item) {
|
if let Ok(st) = String::from_utf8(collected.clone().item) {
|
||||||
@ -414,7 +439,11 @@ impl Iterator for PipelineIterator {
|
|||||||
PipelineData::Value(Value::Nothing { .. }, ..) => None,
|
PipelineData::Value(Value::Nothing { .. }, ..) => None,
|
||||||
PipelineData::Value(v, ..) => Some(std::mem::take(v)),
|
PipelineData::Value(v, ..) => Some(std::mem::take(v)),
|
||||||
PipelineData::ListStream(stream, ..) => stream.next(),
|
PipelineData::ListStream(stream, ..) => stream.next(),
|
||||||
PipelineData::ExternalStream { stdout: stream, .. } => stream.next().map(|x| match x {
|
PipelineData::ExternalStream { stdout: None, .. } => None,
|
||||||
|
PipelineData::ExternalStream {
|
||||||
|
stdout: Some(stream),
|
||||||
|
..
|
||||||
|
} => stream.next().map(|x| match x {
|
||||||
Ok(x) => x,
|
Ok(x) => x,
|
||||||
Err(err) => Value::Error { error: err },
|
Err(err) => Value::Error { error: err },
|
||||||
}),
|
}),
|
||||||
|
@ -100,7 +100,12 @@ pub fn print_table_or_error(
|
|||||||
);
|
);
|
||||||
|
|
||||||
match table {
|
match table {
|
||||||
Ok(table) => {
|
Ok(mut table) => {
|
||||||
|
let exit_code = match &mut table {
|
||||||
|
PipelineData::ExternalStream { exit_code, .. } => exit_code.take(),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
for item in table {
|
for item in table {
|
||||||
let stdout = std::io::stdout();
|
let stdout = std::io::stdout();
|
||||||
|
|
||||||
@ -120,6 +125,10 @@ pub fn print_table_or_error(
|
|||||||
Err(err) => eprintln!("{}", err),
|
Err(err) => eprintln!("{}", err),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(exit_code) = exit_code {
|
||||||
|
let _: Vec<_> = exit_code.into_iter().collect();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
let working_set = StateWorkingSet::new(engine_state);
|
let working_set = StateWorkingSet::new(engine_state);
|
||||||
|
@ -172,11 +172,11 @@ fn main() -> Result<()> {
|
|||||||
let buf_reader = BufReader::new(stdin);
|
let buf_reader = BufReader::new(stdin);
|
||||||
|
|
||||||
PipelineData::ExternalStream {
|
PipelineData::ExternalStream {
|
||||||
stdout: RawStream::new(
|
stdout: Some(RawStream::new(
|
||||||
Box::new(BufferedReader::new(buf_reader)),
|
Box::new(BufferedReader::new(buf_reader)),
|
||||||
Some(ctrlc),
|
Some(ctrlc),
|
||||||
redirect_stdin.span,
|
redirect_stdin.span,
|
||||||
),
|
)),
|
||||||
stderr: None,
|
stderr: None,
|
||||||
exit_code: None,
|
exit_code: None,
|
||||||
span: redirect_stdin.span,
|
span: redirect_stdin.span,
|
||||||
|
Loading…
Reference in New Issue
Block a user