mirror of
https://github.com/nushell/nushell
synced 2024-11-10 15:14:14 +00:00
Fix external extra (#4777)
* Fix empty table from externals * Fix empty table from externals
This commit is contained in:
parent
35ff1076f3
commit
299fea8538
18 changed files with 138 additions and 41 deletions
|
@ -18,7 +18,11 @@ pub fn print_pipeline_data(
|
|||
|
||||
let stdout = std::io::stdout();
|
||||
|
||||
if let PipelineData::ExternalStream { stdout: stream, .. } = input {
|
||||
if let PipelineData::ExternalStream {
|
||||
stdout: Some(stream),
|
||||
..
|
||||
} = input
|
||||
{
|
||||
for s in stream {
|
||||
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)?;
|
||||
|
||||
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
|
||||
let output = stream.into_bytes()?;
|
||||
Ok(Value::Binary {
|
||||
|
|
|
@ -150,7 +150,15 @@ fn string_helper(
|
|||
}
|
||||
|
||||
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
|
||||
let output = stream.into_string()?;
|
||||
Ok(Value::String {
|
||||
|
|
|
@ -121,11 +121,11 @@ impl Command for Open {
|
|||
let buf_reader = BufReader::new(file);
|
||||
|
||||
let output = PipelineData::ExternalStream {
|
||||
stdout: RawStream::new(
|
||||
stdout: Some(RawStream::new(
|
||||
Box::new(BufferedReader { input: buf_reader }),
|
||||
ctrlc,
|
||||
call_span,
|
||||
),
|
||||
)),
|
||||
stderr: None,
|
||||
exit_code: None,
|
||||
span: call_span,
|
||||
|
|
|
@ -169,7 +169,11 @@ impl Command for Each {
|
|||
}
|
||||
})
|
||||
.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()
|
||||
.enumerate()
|
||||
.map(move |(idx, x)| {
|
||||
|
|
|
@ -213,7 +213,11 @@ impl Command for ParEach {
|
|||
.into_iter()
|
||||
.flatten()
|
||||
.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()
|
||||
.par_bridge()
|
||||
.map(move |(idx, x)| {
|
||||
|
|
|
@ -77,7 +77,7 @@ impl Command for Skip {
|
|||
|
||||
match input {
|
||||
PipelineData::ExternalStream {
|
||||
stdout: stream,
|
||||
stdout: Some(stream),
|
||||
span: bytes_span,
|
||||
metadata,
|
||||
..
|
||||
|
|
|
@ -357,13 +357,13 @@ fn response_to_buffer(
|
|||
let buffered_input = BufReader::new(response);
|
||||
|
||||
PipelineData::ExternalStream {
|
||||
stdout: RawStream::new(
|
||||
stdout: Some(RawStream::new(
|
||||
Box::new(BufferedReader {
|
||||
input: buffered_input,
|
||||
}),
|
||||
engine_state.ctrlc.clone(),
|
||||
span,
|
||||
),
|
||||
)),
|
||||
stderr: None,
|
||||
exit_code: None,
|
||||
span,
|
||||
|
|
|
@ -418,13 +418,13 @@ fn response_to_buffer(
|
|||
let buffered_input = BufReader::new(response);
|
||||
|
||||
PipelineData::ExternalStream {
|
||||
stdout: RawStream::new(
|
||||
stdout: Some(RawStream::new(
|
||||
Box::new(BufferedReader {
|
||||
input: buffered_input,
|
||||
}),
|
||||
engine_state.ctrlc.clone(),
|
||||
span,
|
||||
),
|
||||
)),
|
||||
stderr: None,
|
||||
exit_code: None,
|
||||
span,
|
||||
|
|
|
@ -44,7 +44,11 @@ impl Command for Decode {
|
|||
let encoding: Spanned<String> = call.req(engine_state, stack, 0)?;
|
||||
|
||||
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 encoding = match Encoding::for_label(encoding.item.as_bytes()) {
|
||||
|
|
|
@ -34,21 +34,24 @@ impl Command for Complete {
|
|||
exit_code,
|
||||
..
|
||||
} => {
|
||||
let mut cols = vec!["stdout".to_string()];
|
||||
let mut cols = vec![];
|
||||
let mut vals = vec![];
|
||||
|
||||
let stdout = stdout.into_bytes()?;
|
||||
if let Ok(st) = String::from_utf8(stdout.item.clone()) {
|
||||
vals.push(Value::String {
|
||||
val: st,
|
||||
span: stdout.span,
|
||||
})
|
||||
} else {
|
||||
vals.push(Value::Binary {
|
||||
val: stdout.item,
|
||||
span: stdout.span,
|
||||
})
|
||||
};
|
||||
if let Some(stdout) = stdout {
|
||||
cols.push("stdout".to_string());
|
||||
let stdout = stdout.into_bytes()?;
|
||||
if let Ok(st) = String::from_utf8(stdout.item.clone()) {
|
||||
vals.push(Value::String {
|
||||
val: st,
|
||||
span: stdout.span,
|
||||
})
|
||||
} else {
|
||||
vals.push(Value::Binary {
|
||||
val: stdout.item,
|
||||
span: stdout.span,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(stderr) = stderr {
|
||||
cols.push("stderr".to_string());
|
||||
|
|
|
@ -307,7 +307,15 @@ impl ExternalCommand {
|
|||
let exit_code_receiver = ValueReceiver::new(exit_code_rx);
|
||||
|
||||
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(
|
||||
Box::new(stderr_receiver),
|
||||
output_ctrlc.clone(),
|
||||
|
|
|
@ -67,7 +67,7 @@ impl Command for Table {
|
|||
PipelineData::ExternalStream { .. } => Ok(input),
|
||||
PipelineData::Value(Value::Binary { val, .. }, ..) => {
|
||||
Ok(PipelineData::ExternalStream {
|
||||
stdout: RawStream::new(
|
||||
stdout: Some(RawStream::new(
|
||||
Box::new(
|
||||
vec![Ok(format!("{}\n", nu_pretty_hex::pretty_hex(&val))
|
||||
.as_bytes()
|
||||
|
@ -76,7 +76,7 @@ impl Command for Table {
|
|||
),
|
||||
ctrlc,
|
||||
head,
|
||||
),
|
||||
)),
|
||||
stderr: None,
|
||||
exit_code: None,
|
||||
span: head,
|
||||
|
@ -269,7 +269,7 @@ fn handle_row_stream(
|
|||
let head = call.head;
|
||||
|
||||
Ok(PipelineData::ExternalStream {
|
||||
stdout: RawStream::new(
|
||||
stdout: Some(RawStream::new(
|
||||
Box::new(PagingTableCreator {
|
||||
row_offset,
|
||||
config,
|
||||
|
@ -279,7 +279,7 @@ fn handle_row_stream(
|
|||
}),
|
||||
ctrlc,
|
||||
head,
|
||||
),
|
||||
)),
|
||||
stderr: None,
|
||||
exit_code: None,
|
||||
span: head,
|
||||
|
|
|
@ -47,6 +47,7 @@ mod reverse;
|
|||
mod rm;
|
||||
mod roll;
|
||||
mod rotate;
|
||||
mod run_external;
|
||||
mod save;
|
||||
mod select;
|
||||
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>),
|
||||
ListStream(ListStream, Option<PipelineMetadata>),
|
||||
ExternalStream {
|
||||
stdout: RawStream,
|
||||
stdout: Option<RawStream>,
|
||||
stderr: Option<RawStream>,
|
||||
exit_code: Option<ListStream>,
|
||||
span: Span,
|
||||
|
@ -93,7 +93,11 @@ impl PipelineData {
|
|||
vals: s.collect(),
|
||||
span, // FIXME?
|
||||
},
|
||||
PipelineData::ExternalStream { stdout: mut s, .. } => {
|
||||
PipelineData::ExternalStream { stdout: None, .. } => Value::Nothing { span },
|
||||
PipelineData::ExternalStream {
|
||||
stdout: Some(mut s),
|
||||
..
|
||||
} => {
|
||||
let mut items = vec![];
|
||||
|
||||
for val in &mut s {
|
||||
|
@ -157,7 +161,10 @@ impl PipelineData {
|
|||
match self {
|
||||
PipelineData::Value(v, ..) => Ok(v.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![];
|
||||
|
||||
for val in s {
|
||||
|
@ -236,7 +243,13 @@ impl PipelineData {
|
|||
Ok(vals.into_iter().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()?;
|
||||
|
||||
if let Ok(st) = String::from_utf8(collected.clone().item) {
|
||||
|
@ -283,7 +296,13 @@ impl PipelineData {
|
|||
PipelineData::ListStream(stream, ..) => {
|
||||
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()?;
|
||||
|
||||
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))
|
||||
}
|
||||
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()?;
|
||||
|
||||
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(v, ..) => Some(std::mem::take(v)),
|
||||
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,
|
||||
Err(err) => Value::Error { error: err },
|
||||
}),
|
||||
|
|
|
@ -100,7 +100,12 @@ pub fn print_table_or_error(
|
|||
);
|
||||
|
||||
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 {
|
||||
let stdout = std::io::stdout();
|
||||
|
||||
|
@ -120,6 +125,10 @@ pub fn print_table_or_error(
|
|||
Err(err) => eprintln!("{}", err),
|
||||
};
|
||||
}
|
||||
|
||||
if let Some(exit_code) = exit_code {
|
||||
let _: Vec<_> = exit_code.into_iter().collect();
|
||||
}
|
||||
}
|
||||
Err(error) => {
|
||||
let working_set = StateWorkingSet::new(engine_state);
|
||||
|
|
|
@ -172,11 +172,11 @@ fn main() -> Result<()> {
|
|||
let buf_reader = BufReader::new(stdin);
|
||||
|
||||
PipelineData::ExternalStream {
|
||||
stdout: RawStream::new(
|
||||
stdout: Some(RawStream::new(
|
||||
Box::new(BufferedReader::new(buf_reader)),
|
||||
Some(ctrlc),
|
||||
redirect_stdin.span,
|
||||
),
|
||||
)),
|
||||
stderr: None,
|
||||
exit_code: None,
|
||||
span: redirect_stdin.span,
|
||||
|
|
Loading…
Reference in a new issue