forked from extern/nushell
Rename IoStream
to OutDest
(#12433)
# Description I spent a while trying to come up with a good name for what is currently `IoStream`. Looking back, this name is not the best, because it: 1. Implies that it is a stream, when it all it really does is specify the output destination for a stream/pipeline. 2. Implies that it handles input and output, when it really only handles output. So, this PR renames `IoStream` to `OutDest` instead, which should be more clear.
This commit is contained in:
@ -1,5 +1,5 @@
|
||||
use super::Pipeline;
|
||||
use crate::{engine::EngineState, IoStream, Signature, Span, Type, VarId};
|
||||
use crate::{engine::EngineState, OutDest, Signature, Span, Type, VarId};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
@ -20,12 +20,12 @@ impl Block {
|
||||
self.pipelines.is_empty()
|
||||
}
|
||||
|
||||
pub fn stdio_redirect(
|
||||
pub fn pipe_redirection(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
) -> (Option<IoStream>, Option<IoStream>) {
|
||||
) -> (Option<OutDest>, Option<OutDest>) {
|
||||
if let Some(first) = self.pipelines.first() {
|
||||
first.stdio_redirect(engine_state)
|
||||
first.pipe_redirection(engine_state)
|
||||
} else {
|
||||
(None, None)
|
||||
}
|
||||
|
@ -6,8 +6,8 @@ use super::{
|
||||
RangeOperator,
|
||||
};
|
||||
use crate::{
|
||||
ast::ImportPattern, ast::Unit, engine::EngineState, BlockId, IoStream, Signature, Span,
|
||||
Spanned, VarId,
|
||||
ast::ImportPattern, ast::Unit, engine::EngineState, BlockId, OutDest, Signature, Span, Spanned,
|
||||
VarId,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
@ -56,19 +56,19 @@ pub enum Expr {
|
||||
}
|
||||
|
||||
impl Expr {
|
||||
pub fn stdio_redirect(
|
||||
pub fn pipe_redirection(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
) -> (Option<IoStream>, Option<IoStream>) {
|
||||
) -> (Option<OutDest>, Option<OutDest>) {
|
||||
// Usages of `$in` will be wrapped by a `collect` call by the parser,
|
||||
// so we do not have to worry about that when considering
|
||||
// which of the expressions below may consume pipeline output.
|
||||
match self {
|
||||
Expr::Call(call) => engine_state.get_decl(call.decl_id).stdio_redirect(),
|
||||
Expr::Call(call) => engine_state.get_decl(call.decl_id).pipe_redirection(),
|
||||
Expr::Subexpression(block_id) | Expr::Block(block_id) => engine_state
|
||||
.get_block(*block_id)
|
||||
.stdio_redirect(engine_state),
|
||||
Expr::FullCellPath(cell_path) => cell_path.head.expr.stdio_redirect(engine_state),
|
||||
.pipe_redirection(engine_state),
|
||||
Expr::FullCellPath(cell_path) => cell_path.head.expr.pipe_redirection(engine_state),
|
||||
Expr::Bool(_)
|
||||
| Expr::Int(_)
|
||||
| Expr::Float(_)
|
||||
@ -89,7 +89,7 @@ impl Expr {
|
||||
| Expr::Nothing => {
|
||||
// These expressions do not use the output of the pipeline in any meaningful way,
|
||||
// so we can discard the previous output by redirecting it to `Null`.
|
||||
(Some(IoStream::Null), None)
|
||||
(Some(OutDest::Null), None)
|
||||
}
|
||||
Expr::VarDecl(_)
|
||||
| Expr::Operator(_)
|
||||
@ -103,7 +103,7 @@ impl Expr {
|
||||
| Expr::Garbage => {
|
||||
// These should be impossible to pipe to,
|
||||
// but even it is, the pipeline output is not used in any way.
|
||||
(Some(IoStream::Null), None)
|
||||
(Some(OutDest::Null), None)
|
||||
}
|
||||
Expr::RowCondition(_) | Expr::MatchBlock(_) => {
|
||||
// These should be impossible to pipe to,
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::{
|
||||
ast::Expression,
|
||||
engine::{EngineState, StateWorkingSet},
|
||||
IoStream, Span,
|
||||
OutDest, Span,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Display;
|
||||
@ -118,11 +118,11 @@ impl PipelineElement {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stdio_redirect(
|
||||
pub fn pipe_redirection(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
) -> (Option<IoStream>, Option<IoStream>) {
|
||||
self.expr.expr.stdio_redirect(engine_state)
|
||||
) -> (Option<OutDest>, Option<OutDest>) {
|
||||
self.expr.expr.pipe_redirection(engine_state)
|
||||
}
|
||||
}
|
||||
|
||||
@ -164,12 +164,12 @@ impl Pipeline {
|
||||
self.elements.is_empty()
|
||||
}
|
||||
|
||||
pub fn stdio_redirect(
|
||||
pub fn pipe_redirection(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
) -> (Option<IoStream>, Option<IoStream>) {
|
||||
) -> (Option<OutDest>, Option<OutDest>) {
|
||||
if let Some(first) = self.elements.first() {
|
||||
first.stdio_redirect(engine_state)
|
||||
first.pipe_redirection(engine_state)
|
||||
} else {
|
||||
(None, None)
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::{ast::Call, Alias, BlockId, Example, IoStream, PipelineData, ShellError, Signature};
|
||||
use crate::{ast::Call, Alias, BlockId, Example, OutDest, PipelineData, ShellError, Signature};
|
||||
|
||||
use super::{EngineState, Stack, StateWorkingSet};
|
||||
|
||||
@ -134,7 +134,7 @@ pub trait Command: Send + Sync + CommandClone {
|
||||
}
|
||||
}
|
||||
|
||||
fn stdio_redirect(&self) -> (Option<IoStream>, Option<IoStream>) {
|
||||
fn pipe_redirection(&self) -> (Option<OutDest>, Option<OutDest>) {
|
||||
(None, None)
|
||||
}
|
||||
}
|
||||
|
@ -6,9 +6,9 @@ mod engine_state;
|
||||
mod overlay;
|
||||
mod pattern_match;
|
||||
mod stack;
|
||||
mod stack_out_dest;
|
||||
mod state_delta;
|
||||
mod state_working_set;
|
||||
mod stdio;
|
||||
mod usage;
|
||||
mod variable;
|
||||
|
||||
@ -21,7 +21,7 @@ pub use engine_state::*;
|
||||
pub use overlay::*;
|
||||
pub use pattern_match::*;
|
||||
pub use stack::*;
|
||||
pub use stack_out_dest::*;
|
||||
pub use state_delta::*;
|
||||
pub use state_working_set::*;
|
||||
pub use stdio::*;
|
||||
pub use variable::*;
|
||||
|
@ -1,9 +1,9 @@
|
||||
use crate::{
|
||||
engine::{
|
||||
EngineState, Redirection, StackCallArgGuard, StackCaptureGuard, StackIoGuard, StackStdio,
|
||||
EngineState, Redirection, StackCallArgGuard, StackCaptureGuard, StackIoGuard, StackOutDest,
|
||||
DEFAULT_OVERLAY_NAME,
|
||||
},
|
||||
IoStream, ShellError, Span, Value, VarId, ENV_VARIABLE_ID, NU_VARIABLE_ID,
|
||||
OutDest, ShellError, Span, Value, VarId, ENV_VARIABLE_ID, NU_VARIABLE_ID,
|
||||
};
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
@ -44,7 +44,7 @@ pub struct Stack {
|
||||
pub parent_stack: Option<Arc<Stack>>,
|
||||
/// Variables that have been deleted (this is used to hide values from parent stack lookups)
|
||||
pub parent_deletions: Vec<VarId>,
|
||||
pub(crate) stdio: StackStdio,
|
||||
pub(crate) out_dest: StackOutDest,
|
||||
}
|
||||
|
||||
impl Default for Stack {
|
||||
@ -56,7 +56,7 @@ impl Default for Stack {
|
||||
impl Stack {
|
||||
/// Create a new stack.
|
||||
///
|
||||
/// Stdio will be set to [`IoStream::Inherit`]. So, if the last command is an external command,
|
||||
/// stdout and stderr will be set to [`OutDest::Inherit`]. So, if the last command is an external command,
|
||||
/// then its output will be forwarded to the terminal/stdio streams.
|
||||
///
|
||||
/// Use [`Stack::capture`] afterwards if you need to evaluate an expression to a [`Value`](crate::Value)
|
||||
@ -70,7 +70,7 @@ impl Stack {
|
||||
recursion_count: 0,
|
||||
parent_stack: None,
|
||||
parent_deletions: vec![],
|
||||
stdio: StackStdio::new(),
|
||||
out_dest: StackOutDest::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -89,6 +89,7 @@ impl Stack {
|
||||
(*arc).clone()
|
||||
})
|
||||
}
|
||||
|
||||
/// Create a new child stack from a parent.
|
||||
///
|
||||
/// Changes from this child can be merged back into the parent with
|
||||
@ -102,7 +103,7 @@ impl Stack {
|
||||
recursion_count: parent.recursion_count,
|
||||
vars: vec![],
|
||||
parent_deletions: vec![],
|
||||
stdio: parent.stdio.clone(),
|
||||
out_dest: parent.out_dest.clone(),
|
||||
parent_stack: Some(parent),
|
||||
}
|
||||
}
|
||||
@ -257,10 +258,10 @@ impl Stack {
|
||||
}
|
||||
|
||||
pub fn captures_to_stack(&self, captures: Vec<(VarId, Value)>) -> Stack {
|
||||
self.captures_to_stack_preserve_stdio(captures).capture()
|
||||
self.captures_to_stack_preserve_out_dest(captures).capture()
|
||||
}
|
||||
|
||||
pub fn captures_to_stack_preserve_stdio(&self, captures: Vec<(VarId, Value)>) -> Stack {
|
||||
pub fn captures_to_stack_preserve_out_dest(&self, captures: Vec<(VarId, Value)>) -> Stack {
|
||||
// FIXME: this is probably slow
|
||||
let mut env_vars = self.env_vars.clone();
|
||||
env_vars.push(HashMap::new());
|
||||
@ -273,7 +274,7 @@ impl Stack {
|
||||
recursion_count: self.recursion_count,
|
||||
parent_stack: None,
|
||||
parent_deletions: vec![],
|
||||
stdio: self.stdio.clone(),
|
||||
out_dest: self.out_dest.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -303,7 +304,7 @@ impl Stack {
|
||||
recursion_count: self.recursion_count,
|
||||
parent_stack: None,
|
||||
parent_deletions: vec![],
|
||||
stdio: self.stdio.clone(),
|
||||
out_dest: self.out_dest.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -510,45 +511,45 @@ impl Stack {
|
||||
self.active_overlays.retain(|o| o != name);
|
||||
}
|
||||
|
||||
/// Returns the [`IoStream`] to use for the current command's stdout.
|
||||
/// Returns the [`OutDest`] to use for the current command's stdout.
|
||||
///
|
||||
/// This will be the pipe redirection if one is set,
|
||||
/// otherwise it will be the current file redirection,
|
||||
/// otherwise it will be the process's stdout indicated by [`IoStream::Inherit`].
|
||||
pub fn stdout(&self) -> &IoStream {
|
||||
self.stdio.stdout()
|
||||
/// otherwise it will be the process's stdout indicated by [`OutDest::Inherit`].
|
||||
pub fn stdout(&self) -> &OutDest {
|
||||
self.out_dest.stdout()
|
||||
}
|
||||
|
||||
/// Returns the [`IoStream`] to use for the current command's stderr.
|
||||
/// Returns the [`OutDest`] to use for the current command's stderr.
|
||||
///
|
||||
/// This will be the pipe redirection if one is set,
|
||||
/// otherwise it will be the current file redirection,
|
||||
/// otherwise it will be the process's stderr indicated by [`IoStream::Inherit`].
|
||||
pub fn stderr(&self) -> &IoStream {
|
||||
self.stdio.stderr()
|
||||
/// otherwise it will be the process's stderr indicated by [`OutDest::Inherit`].
|
||||
pub fn stderr(&self) -> &OutDest {
|
||||
self.out_dest.stderr()
|
||||
}
|
||||
|
||||
/// Returns the [`IoStream`] to use for the last command's stdout.
|
||||
pub fn pipe_stdout(&self) -> Option<&IoStream> {
|
||||
self.stdio.pipe_stdout.as_ref()
|
||||
/// Returns the [`OutDest`] of the pipe redirection applied to the current command's stdout.
|
||||
pub fn pipe_stdout(&self) -> Option<&OutDest> {
|
||||
self.out_dest.pipe_stdout.as_ref()
|
||||
}
|
||||
|
||||
/// Returns the [`IoStream`] to use for the last command's stderr.
|
||||
pub fn pipe_stderr(&self) -> Option<&IoStream> {
|
||||
self.stdio.pipe_stderr.as_ref()
|
||||
/// Returns the [`OutDest`] of the pipe redirection applied to the current command's stderr.
|
||||
pub fn pipe_stderr(&self) -> Option<&OutDest> {
|
||||
self.out_dest.pipe_stderr.as_ref()
|
||||
}
|
||||
|
||||
/// Temporarily set the pipe stdout redirection to [`IoStream::Capture`].
|
||||
/// Temporarily set the pipe stdout redirection to [`OutDest::Capture`].
|
||||
///
|
||||
/// This is used before evaluating an expression into a `Value`.
|
||||
pub fn start_capture(&mut self) -> StackCaptureGuard {
|
||||
StackCaptureGuard::new(self)
|
||||
}
|
||||
|
||||
/// Temporarily use the stdio redirections in the parent scope.
|
||||
/// Temporarily use the output redirections in the parent scope.
|
||||
///
|
||||
/// This is used before evaluating an argument to a call.
|
||||
pub fn use_call_arg_stdio(&mut self) -> StackCallArgGuard {
|
||||
pub fn use_call_arg_out_dest(&mut self) -> StackCallArgGuard {
|
||||
StackCallArgGuard::new(self)
|
||||
}
|
||||
|
||||
@ -561,34 +562,34 @@ impl Stack {
|
||||
StackIoGuard::new(self, stdout, stderr)
|
||||
}
|
||||
|
||||
/// Mark stdout for the last command as [`IoStream::Capture`].
|
||||
/// Mark stdout for the last command as [`OutDest::Capture`].
|
||||
///
|
||||
/// This will irreversibly alter the stdio redirections, and so it only makes sense to use this on an owned `Stack`
|
||||
/// This will irreversibly alter the output redirections, and so it only makes sense to use this on an owned `Stack`
|
||||
/// (which is why this function does not take `&mut self`).
|
||||
///
|
||||
/// See [`Stack::start_capture`] which can temporarily set stdout as [`IoStream::Capture`] for a mutable `Stack` reference.
|
||||
/// See [`Stack::start_capture`] which can temporarily set stdout as [`OutDest::Capture`] for a mutable `Stack` reference.
|
||||
pub fn capture(mut self) -> Self {
|
||||
self.stdio.pipe_stdout = Some(IoStream::Capture);
|
||||
self.stdio.pipe_stderr = None;
|
||||
self.out_dest.pipe_stdout = Some(OutDest::Capture);
|
||||
self.out_dest.pipe_stderr = None;
|
||||
self
|
||||
}
|
||||
|
||||
/// Clears any pipe and file redirections and resets stdout and stderr to [`IoStream::Inherit`].
|
||||
/// Clears any pipe and file redirections and resets stdout and stderr to [`OutDest::Inherit`].
|
||||
///
|
||||
/// This will irreversibly reset the stdio redirections, and so it only makes sense to use this on an owned `Stack`
|
||||
/// This will irreversibly reset the output redirections, and so it only makes sense to use this on an owned `Stack`
|
||||
/// (which is why this function does not take `&mut self`).
|
||||
pub fn reset_stdio(mut self) -> Self {
|
||||
self.stdio = StackStdio::new();
|
||||
pub fn reset_out_dest(mut self) -> Self {
|
||||
self.out_dest = StackOutDest::new();
|
||||
self
|
||||
}
|
||||
|
||||
/// Clears any pipe redirections, keeping the current stdout and stderr.
|
||||
///
|
||||
/// This will irreversibly reset some of the stdio redirections, and so it only makes sense to use this on an owned `Stack`
|
||||
/// This will irreversibly reset some of the output redirections, and so it only makes sense to use this on an owned `Stack`
|
||||
/// (which is why this function does not take `&mut self`).
|
||||
pub fn reset_pipes(mut self) -> Self {
|
||||
self.stdio.pipe_stdout = None;
|
||||
self.stdio.pipe_stderr = None;
|
||||
self.out_dest.pipe_stdout = None;
|
||||
self.out_dest.pipe_stderr = None;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::{engine::Stack, IoStream};
|
||||
use crate::{engine::Stack, OutDest};
|
||||
use std::{
|
||||
fs::File,
|
||||
mem,
|
||||
@ -12,8 +12,8 @@ pub enum Redirection {
|
||||
///
|
||||
/// This will only affect the last command of a block.
|
||||
/// This is created by pipes and pipe redirections (`|`, `e>|`, `o+e>|`, etc.),
|
||||
/// or set by the next command in the pipeline (e.g., `ignore` sets stdout to [`IoStream::Null`]).
|
||||
Pipe(IoStream),
|
||||
/// or set by the next command in the pipeline (e.g., `ignore` sets stdout to [`OutDest::Null`]).
|
||||
Pipe(OutDest),
|
||||
/// A file redirection.
|
||||
///
|
||||
/// This will affect all commands in the block.
|
||||
@ -28,19 +28,19 @@ impl Redirection {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct StackStdio {
|
||||
pub(crate) struct StackOutDest {
|
||||
/// The stream to use for the next command's stdout.
|
||||
pub pipe_stdout: Option<IoStream>,
|
||||
pub pipe_stdout: Option<OutDest>,
|
||||
/// The stream to use for the next command's stderr.
|
||||
pub pipe_stderr: Option<IoStream>,
|
||||
pub pipe_stderr: Option<OutDest>,
|
||||
/// The stream used for the command stdout if `pipe_stdout` is `None`.
|
||||
///
|
||||
/// This should only ever be `File` or `Inherit`.
|
||||
pub stdout: IoStream,
|
||||
pub stdout: OutDest,
|
||||
/// The stream used for the command stderr if `pipe_stderr` is `None`.
|
||||
///
|
||||
/// This should only ever be `File` or `Inherit`.
|
||||
pub stderr: IoStream,
|
||||
pub stderr: OutDest,
|
||||
/// The previous stdout used before the current `stdout` was set.
|
||||
///
|
||||
/// This is used only when evaluating arguments to commands,
|
||||
@ -48,7 +48,7 @@ pub(crate) struct StackStdio {
|
||||
/// after redirections have already been applied to the command/stack.
|
||||
///
|
||||
/// This should only ever be `File` or `Inherit`.
|
||||
pub parent_stdout: Option<IoStream>,
|
||||
pub parent_stdout: Option<OutDest>,
|
||||
/// The previous stderr used before the current `stderr` was set.
|
||||
///
|
||||
/// This is used only when evaluating arguments to commands,
|
||||
@ -56,45 +56,45 @@ pub(crate) struct StackStdio {
|
||||
/// after redirections have already been applied to the command/stack.
|
||||
///
|
||||
/// This should only ever be `File` or `Inherit`.
|
||||
pub parent_stderr: Option<IoStream>,
|
||||
pub parent_stderr: Option<OutDest>,
|
||||
}
|
||||
|
||||
impl StackStdio {
|
||||
impl StackOutDest {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self {
|
||||
pipe_stdout: None,
|
||||
pipe_stderr: None,
|
||||
stdout: IoStream::Inherit,
|
||||
stderr: IoStream::Inherit,
|
||||
stdout: OutDest::Inherit,
|
||||
stderr: OutDest::Inherit,
|
||||
parent_stdout: None,
|
||||
parent_stderr: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the [`IoStream`] to use for current command's stdout.
|
||||
/// Returns the [`OutDest`] to use for current command's stdout.
|
||||
///
|
||||
/// This will be the pipe redirection if one is set,
|
||||
/// otherwise it will be the current file redirection,
|
||||
/// otherwise it will be the process's stdout indicated by [`IoStream::Inherit`].
|
||||
pub(crate) fn stdout(&self) -> &IoStream {
|
||||
/// otherwise it will be the process's stdout indicated by [`OutDest::Inherit`].
|
||||
pub(crate) fn stdout(&self) -> &OutDest {
|
||||
self.pipe_stdout.as_ref().unwrap_or(&self.stdout)
|
||||
}
|
||||
|
||||
/// Returns the [`IoStream`] to use for current command's stderr.
|
||||
/// Returns the [`OutDest`] to use for current command's stderr.
|
||||
///
|
||||
/// This will be the pipe redirection if one is set,
|
||||
/// otherwise it will be the current file redirection,
|
||||
/// otherwise it will be the process's stderr indicated by [`IoStream::Inherit`].
|
||||
pub(crate) fn stderr(&self) -> &IoStream {
|
||||
/// otherwise it will be the process's stderr indicated by [`OutDest::Inherit`].
|
||||
pub(crate) fn stderr(&self) -> &OutDest {
|
||||
self.pipe_stderr.as_ref().unwrap_or(&self.stderr)
|
||||
}
|
||||
|
||||
fn push_stdout(&mut self, stdout: IoStream) -> Option<IoStream> {
|
||||
fn push_stdout(&mut self, stdout: OutDest) -> Option<OutDest> {
|
||||
let stdout = mem::replace(&mut self.stdout, stdout);
|
||||
mem::replace(&mut self.parent_stdout, Some(stdout))
|
||||
}
|
||||
|
||||
fn push_stderr(&mut self, stderr: IoStream) -> Option<IoStream> {
|
||||
fn push_stderr(&mut self, stderr: OutDest) -> Option<OutDest> {
|
||||
let stderr = mem::replace(&mut self.stderr, stderr);
|
||||
mem::replace(&mut self.parent_stderr, Some(stderr))
|
||||
}
|
||||
@ -102,10 +102,10 @@ impl StackStdio {
|
||||
|
||||
pub struct StackIoGuard<'a> {
|
||||
stack: &'a mut Stack,
|
||||
old_pipe_stdout: Option<IoStream>,
|
||||
old_pipe_stderr: Option<IoStream>,
|
||||
old_parent_stdout: Option<IoStream>,
|
||||
old_parent_stderr: Option<IoStream>,
|
||||
old_pipe_stdout: Option<OutDest>,
|
||||
old_pipe_stderr: Option<OutDest>,
|
||||
old_parent_stdout: Option<OutDest>,
|
||||
old_parent_stderr: Option<OutDest>,
|
||||
}
|
||||
|
||||
impl<'a> StackIoGuard<'a> {
|
||||
@ -114,32 +114,33 @@ impl<'a> StackIoGuard<'a> {
|
||||
stdout: Option<Redirection>,
|
||||
stderr: Option<Redirection>,
|
||||
) -> Self {
|
||||
let stdio = &mut stack.stdio;
|
||||
let out_dest = &mut stack.out_dest;
|
||||
|
||||
let (old_pipe_stdout, old_parent_stdout) = match stdout {
|
||||
Some(Redirection::Pipe(stdout)) => {
|
||||
let old = mem::replace(&mut stdio.pipe_stdout, Some(stdout));
|
||||
(old, stdio.parent_stdout.take())
|
||||
let old = mem::replace(&mut out_dest.pipe_stdout, Some(stdout));
|
||||
(old, out_dest.parent_stdout.take())
|
||||
}
|
||||
Some(Redirection::File(file)) => {
|
||||
let file = IoStream::from(file);
|
||||
let file = OutDest::from(file);
|
||||
(
|
||||
mem::replace(&mut stdio.pipe_stdout, Some(file.clone())),
|
||||
stdio.push_stdout(file),
|
||||
mem::replace(&mut out_dest.pipe_stdout, Some(file.clone())),
|
||||
out_dest.push_stdout(file),
|
||||
)
|
||||
}
|
||||
None => (stdio.pipe_stdout.take(), stdio.parent_stdout.take()),
|
||||
None => (out_dest.pipe_stdout.take(), out_dest.parent_stdout.take()),
|
||||
};
|
||||
|
||||
let (old_pipe_stderr, old_parent_stderr) = match stderr {
|
||||
Some(Redirection::Pipe(stderr)) => {
|
||||
let old = mem::replace(&mut stdio.pipe_stderr, Some(stderr));
|
||||
(old, stdio.parent_stderr.take())
|
||||
let old = mem::replace(&mut out_dest.pipe_stderr, Some(stderr));
|
||||
(old, out_dest.parent_stderr.take())
|
||||
}
|
||||
Some(Redirection::File(file)) => {
|
||||
(stdio.pipe_stderr.take(), stdio.push_stderr(file.into()))
|
||||
}
|
||||
None => (stdio.pipe_stderr.take(), stdio.parent_stderr.take()),
|
||||
Some(Redirection::File(file)) => (
|
||||
out_dest.pipe_stderr.take(),
|
||||
out_dest.push_stderr(file.into()),
|
||||
),
|
||||
None => (out_dest.pipe_stderr.take(), out_dest.parent_stderr.take()),
|
||||
};
|
||||
|
||||
StackIoGuard {
|
||||
@ -168,31 +169,31 @@ impl<'a> DerefMut for StackIoGuard<'a> {
|
||||
|
||||
impl Drop for StackIoGuard<'_> {
|
||||
fn drop(&mut self) {
|
||||
self.stdio.pipe_stdout = self.old_pipe_stdout.take();
|
||||
self.stdio.pipe_stderr = self.old_pipe_stderr.take();
|
||||
self.out_dest.pipe_stdout = self.old_pipe_stdout.take();
|
||||
self.out_dest.pipe_stderr = self.old_pipe_stderr.take();
|
||||
|
||||
let old_stdout = self.old_parent_stdout.take();
|
||||
if let Some(stdout) = mem::replace(&mut self.stdio.parent_stdout, old_stdout) {
|
||||
self.stdio.stdout = stdout;
|
||||
if let Some(stdout) = mem::replace(&mut self.out_dest.parent_stdout, old_stdout) {
|
||||
self.out_dest.stdout = stdout;
|
||||
}
|
||||
|
||||
let old_stderr = self.old_parent_stderr.take();
|
||||
if let Some(stderr) = mem::replace(&mut self.stdio.parent_stderr, old_stderr) {
|
||||
self.stdio.stderr = stderr;
|
||||
if let Some(stderr) = mem::replace(&mut self.out_dest.parent_stderr, old_stderr) {
|
||||
self.out_dest.stderr = stderr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct StackCaptureGuard<'a> {
|
||||
stack: &'a mut Stack,
|
||||
old_pipe_stdout: Option<IoStream>,
|
||||
old_pipe_stderr: Option<IoStream>,
|
||||
old_pipe_stdout: Option<OutDest>,
|
||||
old_pipe_stderr: Option<OutDest>,
|
||||
}
|
||||
|
||||
impl<'a> StackCaptureGuard<'a> {
|
||||
pub(crate) fn new(stack: &'a mut Stack) -> Self {
|
||||
let old_pipe_stdout = mem::replace(&mut stack.stdio.pipe_stdout, Some(IoStream::Capture));
|
||||
let old_pipe_stderr = stack.stdio.pipe_stderr.take();
|
||||
let old_pipe_stdout = mem::replace(&mut stack.out_dest.pipe_stdout, Some(OutDest::Capture));
|
||||
let old_pipe_stderr = stack.out_dest.pipe_stderr.take();
|
||||
Self {
|
||||
stack,
|
||||
old_pipe_stdout,
|
||||
@ -217,35 +218,35 @@ impl<'a> DerefMut for StackCaptureGuard<'a> {
|
||||
|
||||
impl Drop for StackCaptureGuard<'_> {
|
||||
fn drop(&mut self) {
|
||||
self.stdio.pipe_stdout = self.old_pipe_stdout.take();
|
||||
self.stdio.pipe_stderr = self.old_pipe_stderr.take();
|
||||
self.out_dest.pipe_stdout = self.old_pipe_stdout.take();
|
||||
self.out_dest.pipe_stderr = self.old_pipe_stderr.take();
|
||||
}
|
||||
}
|
||||
|
||||
pub struct StackCallArgGuard<'a> {
|
||||
stack: &'a mut Stack,
|
||||
old_pipe_stdout: Option<IoStream>,
|
||||
old_pipe_stderr: Option<IoStream>,
|
||||
old_stdout: Option<IoStream>,
|
||||
old_stderr: Option<IoStream>,
|
||||
old_pipe_stdout: Option<OutDest>,
|
||||
old_pipe_stderr: Option<OutDest>,
|
||||
old_stdout: Option<OutDest>,
|
||||
old_stderr: Option<OutDest>,
|
||||
}
|
||||
|
||||
impl<'a> StackCallArgGuard<'a> {
|
||||
pub(crate) fn new(stack: &'a mut Stack) -> Self {
|
||||
let old_pipe_stdout = mem::replace(&mut stack.stdio.pipe_stdout, Some(IoStream::Capture));
|
||||
let old_pipe_stderr = stack.stdio.pipe_stderr.take();
|
||||
let old_pipe_stdout = mem::replace(&mut stack.out_dest.pipe_stdout, Some(OutDest::Capture));
|
||||
let old_pipe_stderr = stack.out_dest.pipe_stderr.take();
|
||||
|
||||
let old_stdout = stack
|
||||
.stdio
|
||||
.out_dest
|
||||
.parent_stdout
|
||||
.take()
|
||||
.map(|stdout| mem::replace(&mut stack.stdio.stdout, stdout));
|
||||
.map(|stdout| mem::replace(&mut stack.out_dest.stdout, stdout));
|
||||
|
||||
let old_stderr = stack
|
||||
.stdio
|
||||
.out_dest
|
||||
.parent_stderr
|
||||
.take()
|
||||
.map(|stderr| mem::replace(&mut stack.stdio.stderr, stderr));
|
||||
.map(|stderr| mem::replace(&mut stack.out_dest.stderr, stderr));
|
||||
|
||||
Self {
|
||||
stack,
|
||||
@ -273,13 +274,13 @@ impl<'a> DerefMut for StackCallArgGuard<'a> {
|
||||
|
||||
impl Drop for StackCallArgGuard<'_> {
|
||||
fn drop(&mut self) {
|
||||
self.stdio.pipe_stdout = self.old_pipe_stdout.take();
|
||||
self.stdio.pipe_stderr = self.old_pipe_stderr.take();
|
||||
self.out_dest.pipe_stdout = self.old_pipe_stdout.take();
|
||||
self.out_dest.pipe_stderr = self.old_pipe_stderr.take();
|
||||
if let Some(stdout) = self.old_stdout.take() {
|
||||
self.stdio.push_stdout(stdout);
|
||||
self.out_dest.push_stdout(stdout);
|
||||
}
|
||||
if let Some(stderr) = self.old_stderr.take() {
|
||||
self.stdio.push_stderr(stderr);
|
||||
self.out_dest.push_stderr(stderr);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
use std::{fs::File, io, process::Stdio, sync::Arc};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum IoStream {
|
||||
/// Redirect the `stdout` and/or `stderr` of one command as the input for the next command in the pipeline.
|
||||
///
|
||||
/// The output pipe will be available in `PipelineData::ExternalStream::stdout`.
|
||||
///
|
||||
/// If both `stdout` and `stderr` are set to `Pipe`,
|
||||
/// then they will combined into `ExternalStream::stdout`.
|
||||
Pipe,
|
||||
/// Capture output to later be collected into a [`Value`](crate::Value), `Vec`, or used in some
|
||||
/// other way.
|
||||
///
|
||||
/// The output stream(s) will be available in
|
||||
/// `PipelineData::ExternalStream::stdout` or `PipelineData::ExternalStream::stderr`.
|
||||
///
|
||||
/// This is similar to `Pipe` but will never combine `stdout` and `stderr`
|
||||
/// or place an external command's `stderr` into `PipelineData::ExternalStream::stdout`.
|
||||
Capture,
|
||||
/// Ignore output.
|
||||
Null,
|
||||
/// Output to nushell's `stdout` or `stderr`.
|
||||
///
|
||||
/// This causes external commands to inherit nushell's `stdout` or `stderr`.
|
||||
Inherit,
|
||||
/// Redirect output to a file.
|
||||
File(Arc<File>), // Arc<File>, since we sometimes need to clone `IoStream` into iterators, etc.
|
||||
}
|
||||
|
||||
impl From<File> for IoStream {
|
||||
fn from(file: File) -> Self {
|
||||
Arc::new(file).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Arc<File>> for IoStream {
|
||||
fn from(file: Arc<File>) -> Self {
|
||||
Self::File(file)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&IoStream> for Stdio {
|
||||
type Error = io::Error;
|
||||
|
||||
fn try_from(stream: &IoStream) -> Result<Self, Self::Error> {
|
||||
match stream {
|
||||
IoStream::Pipe | IoStream::Capture => Ok(Self::piped()),
|
||||
IoStream::Null => Ok(Self::null()),
|
||||
IoStream::Inherit => Ok(Self::inherit()),
|
||||
IoStream::File(file) => Ok(file.try_clone()?.into()),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
mod io_stream;
|
||||
mod metadata;
|
||||
mod out_dest;
|
||||
mod stream;
|
||||
|
||||
pub use io_stream::*;
|
||||
pub use metadata::*;
|
||||
pub use out_dest::*;
|
||||
pub use stream::*;
|
||||
|
||||
use crate::{
|
||||
@ -208,14 +208,14 @@ impl PipelineData {
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes all values or redirects all output to the current stdio streams in `stack`.
|
||||
/// Writes all values or redirects all output to the current [`OutDest`]s in `stack`.
|
||||
///
|
||||
/// For [`IoStream::Pipe`] and [`IoStream::Capture`], this will return the `PipelineData` as is
|
||||
/// For [`OutDest::Pipe`] and [`OutDest::Capture`], this will return the `PipelineData` as is
|
||||
/// without consuming input and without writing anything.
|
||||
///
|
||||
/// For the other [`IoStream`]s, the given `PipelineData` will be completely consumed
|
||||
/// For the other [`OutDest`]s, the given `PipelineData` will be completely consumed
|
||||
/// and `PipelineData::Empty` will be returned.
|
||||
pub fn write_to_io_streams(
|
||||
pub fn write_to_out_dests(
|
||||
self,
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
@ -234,10 +234,10 @@ impl PipelineData {
|
||||
) => {
|
||||
fn needs_redirect(
|
||||
stream: Option<RawStream>,
|
||||
io_stream: &IoStream,
|
||||
out_dest: &OutDest,
|
||||
) -> Result<RawStream, Option<RawStream>> {
|
||||
match (stream, io_stream) {
|
||||
(Some(stream), IoStream::Pipe | IoStream::Capture) => Err(Some(stream)),
|
||||
match (stream, out_dest) {
|
||||
(Some(stream), OutDest::Pipe | OutDest::Capture) => Err(Some(stream)),
|
||||
(Some(stream), _) => Ok(stream),
|
||||
(None, _) => Err(None),
|
||||
}
|
||||
@ -296,22 +296,22 @@ impl PipelineData {
|
||||
trim_end_newline,
|
||||
})
|
||||
}
|
||||
(data, IoStream::Pipe | IoStream::Capture) => Ok(data),
|
||||
(data, OutDest::Pipe | OutDest::Capture) => Ok(data),
|
||||
(PipelineData::Empty, _) => Ok(PipelineData::Empty),
|
||||
(PipelineData::Value(_, _), IoStream::Null) => Ok(PipelineData::Empty),
|
||||
(PipelineData::ListStream(stream, _), IoStream::Null) => {
|
||||
(PipelineData::Value(_, _), OutDest::Null) => Ok(PipelineData::Empty),
|
||||
(PipelineData::ListStream(stream, _), OutDest::Null) => {
|
||||
// we need to drain the stream in case there are external commands in the pipeline
|
||||
stream.drain()?;
|
||||
Ok(PipelineData::Empty)
|
||||
}
|
||||
(PipelineData::Value(value, _), IoStream::File(file)) => {
|
||||
(PipelineData::Value(value, _), OutDest::File(file)) => {
|
||||
let bytes = value_to_bytes(value)?;
|
||||
let mut file = file.try_clone()?;
|
||||
file.write_all(&bytes)?;
|
||||
file.flush()?;
|
||||
Ok(PipelineData::Empty)
|
||||
}
|
||||
(PipelineData::ListStream(stream, _), IoStream::File(file)) => {
|
||||
(PipelineData::ListStream(stream, _), OutDest::File(file)) => {
|
||||
let mut file = file.try_clone()?;
|
||||
// use BufWriter here?
|
||||
for value in stream {
|
||||
@ -324,7 +324,7 @@ impl PipelineData {
|
||||
}
|
||||
(
|
||||
data @ (PipelineData::Value(_, _) | PipelineData::ListStream(_, _)),
|
||||
IoStream::Inherit,
|
||||
OutDest::Inherit,
|
||||
) => {
|
||||
let config = engine_state.get_config();
|
||||
|
||||
@ -1036,26 +1036,26 @@ fn drain_exit_code(exit_code: ListStream) -> Result<i64, ShellError> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Only call this if `output_stream` is not `IoStream::Pipe` or `IoStream::Capture`.
|
||||
fn consume_child_output(child_output: RawStream, output_stream: &IoStream) -> io::Result<()> {
|
||||
/// Only call this if `output_stream` is not `OutDest::Pipe` or `OutDest::Capture`.
|
||||
fn consume_child_output(child_output: RawStream, output_stream: &OutDest) -> io::Result<()> {
|
||||
let mut output = ReadRawStream::new(child_output);
|
||||
match output_stream {
|
||||
IoStream::Pipe | IoStream::Capture => {
|
||||
OutDest::Pipe | OutDest::Capture => {
|
||||
// The point of `consume_child_output` is to redirect output *right now*,
|
||||
// but IoStream::Pipe means to redirect output
|
||||
// but OutDest::Pipe means to redirect output
|
||||
// into an OS pipe for *future use* (as input for another command).
|
||||
// So, this branch makes no sense, and will simply drop `output` instead of draining it.
|
||||
// This could trigger a `SIGPIPE` for the external command,
|
||||
// since there will be no reader for its pipe.
|
||||
debug_assert!(false)
|
||||
}
|
||||
IoStream::Null => {
|
||||
OutDest::Null => {
|
||||
io::copy(&mut output, &mut io::sink())?;
|
||||
}
|
||||
IoStream::Inherit => {
|
||||
OutDest::Inherit => {
|
||||
io::copy(&mut output, &mut io::stdout())?;
|
||||
}
|
||||
IoStream::File(file) => {
|
||||
OutDest::File(file) => {
|
||||
io::copy(&mut output, &mut file.try_clone()?)?;
|
||||
}
|
||||
}
|
||||
|
55
crates/nu-protocol/src/pipeline_data/out_dest.rs
Normal file
55
crates/nu-protocol/src/pipeline_data/out_dest.rs
Normal file
@ -0,0 +1,55 @@
|
||||
use std::{fs::File, io, process::Stdio, sync::Arc};
|
||||
|
||||
/// Describes where to direct the stdout or stderr output stream of external command to.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum OutDest {
|
||||
/// Redirect the stdout and/or stderr of one command as the input for the next command in the pipeline.
|
||||
///
|
||||
/// The output pipe will be available as the `stdout` of `PipelineData::ExternalStream`.
|
||||
///
|
||||
/// If stdout and stderr are both set to `Pipe`,
|
||||
/// then they will combined into the `stdout` of `PipelineData::ExternalStream`.
|
||||
Pipe,
|
||||
/// Capture output to later be collected into a [`Value`](crate::Value), `Vec`, or used in some other way.
|
||||
///
|
||||
/// The output stream(s) will be available in the `stdout` or `stderr` of `PipelineData::ExternalStream`.
|
||||
///
|
||||
/// This is similar to `Pipe` but will never combine stdout and stderr
|
||||
/// or place an external command's stderr into `stdout` of `PipelineData::ExternalStream`.
|
||||
Capture,
|
||||
/// Ignore output.
|
||||
///
|
||||
/// This will forward output to the null device for the platform.
|
||||
Null,
|
||||
/// Output to nushell's stdout or stderr.
|
||||
///
|
||||
/// This causes external commands to inherit nushell's stdout or stderr.
|
||||
Inherit,
|
||||
/// Redirect output to a file.
|
||||
File(Arc<File>), // Arc<File>, since we sometimes need to clone `OutDest` into iterators, etc.
|
||||
}
|
||||
|
||||
impl From<File> for OutDest {
|
||||
fn from(file: File) -> Self {
|
||||
Arc::new(file).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Arc<File>> for OutDest {
|
||||
fn from(file: Arc<File>) -> Self {
|
||||
Self::File(file)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&OutDest> for Stdio {
|
||||
type Error = io::Error;
|
||||
|
||||
fn try_from(out_dest: &OutDest) -> Result<Self, Self::Error> {
|
||||
match out_dest {
|
||||
OutDest::Pipe | OutDest::Capture => Ok(Self::piped()),
|
||||
OutDest::Null => Ok(Self::null()),
|
||||
OutDest::Inherit => Ok(Self::inherit()),
|
||||
OutDest::File(file) => Ok(file.try_clone()?.into()),
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user