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:
Ian Manske 2024-04-09 16:48:32 +00:00 committed by GitHub
parent 6536fa5ff7
commit d7ba8872bf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
28 changed files with 263 additions and 260 deletions

View File

@ -25,7 +25,7 @@ impl NuCompleter {
pub fn new(engine_state: Arc<EngineState>, stack: Stack) -> Self {
Self {
engine_state,
stack: stack.reset_stdio().capture(),
stack: stack.reset_out_dest().capture(),
}
}

View File

@ -24,7 +24,7 @@ impl CustomCompletion {
pub fn new(engine_state: Arc<EngineState>, stack: Stack, decl_id: usize, line: String) -> Self {
Self {
engine_state,
stack: stack.reset_stdio().capture(),
stack: stack.reset_out_dest().capture(),
decl_id,
line,
sort_by: SortBy::None,

View File

@ -28,7 +28,7 @@ impl NuMenuCompleter {
Self {
block_id,
span,
stack: stack.reset_stdio().capture(),
stack: stack.reset_out_dest().capture(),
engine_state,
only_buffer_difference,
}

View File

@ -40,7 +40,7 @@ impl Command for Collect {
let block = engine_state.get_block(capture_block.block_id).clone();
let mut stack_captures =
stack.captures_to_stack_preserve_stdio(capture_block.captures.clone());
stack.captures_to_stack_preserve_out_dest(capture_block.captures.clone());
let metadata = input.metadata();
let input: Value = input.into_value(call.head);

View File

@ -1,5 +1,5 @@
use nu_engine::{command_prelude::*, get_eval_block_with_early_return, redirect_env};
use nu_protocol::{engine::Closure, IoStream, ListStream, RawStream};
use nu_protocol::{engine::Closure, ListStream, OutDest, RawStream};
use std::thread;
#[derive(Clone)]
@ -72,7 +72,7 @@ impl Command for Do {
let capture_errors = call.has_flag(engine_state, caller_stack, "capture-errors")?;
let has_env = call.has_flag(engine_state, caller_stack, "env")?;
let mut callee_stack = caller_stack.captures_to_stack_preserve_stdio(block.captures);
let mut callee_stack = caller_stack.captures_to_stack_preserve_out_dest(block.captures);
let block = engine_state.get_block(block.block_id);
bind_args_to(&mut callee_stack, &block.signature, rest, call.head)?;
@ -191,7 +191,7 @@ impl Command for Do {
metadata,
trim_end_newline,
}) if ignore_program_errors
&& !matches!(caller_stack.stdout(), IoStream::Pipe | IoStream::Capture) =>
&& !matches!(caller_stack.stdout(), OutDest::Pipe | OutDest::Capture) =>
{
Ok(PipelineData::ExternalStream {
stdout,

View File

@ -1,5 +1,5 @@
use nu_engine::command_prelude::*;
use nu_protocol::{engine::StateWorkingSet, IoStream};
use nu_protocol::{engine::StateWorkingSet, OutDest};
#[derive(Clone)]
pub struct Ignore;
@ -56,8 +56,8 @@ impl Command for Ignore {
}]
}
fn stdio_redirect(&self) -> (Option<IoStream>, Option<IoStream>) {
(Some(IoStream::Null), None)
fn pipe_redirection(&self) -> (Option<OutDest>, Option<OutDest>) {
(Some(OutDest::Null), None)
}
}

View File

@ -80,7 +80,7 @@ impl Command for LazyMake {
}
}
let stack = stack.clone().reset_stdio().capture();
let stack = stack.clone().reset_out_dest().capture();
Ok(Value::lazy_record(
Box::new(NuLazyRecord {

View File

@ -1,5 +1,5 @@
use crate::ExternalCommand;
use nu_protocol::{IoStream, Span, Spanned};
use nu_protocol::{OutDest, Span, Spanned};
use std::{collections::HashMap, path::PathBuf};
pub(crate) fn gen_command(
@ -29,8 +29,8 @@ pub(crate) fn gen_command(
name,
args,
arg_keep_raw: vec![false; number_of_args],
out: IoStream::Inherit,
err: IoStream::Inherit,
out: OutDest::Inherit,
err: OutDest::Inherit,
env_vars: env_vars_str,
}
}

View File

@ -79,7 +79,7 @@ fn with_env(
let capture_block: Closure = call.req(engine_state, stack, 1)?;
let block = engine_state.get_block(capture_block.block_id);
let mut stack = stack.captures_to_stack_preserve_stdio(capture_block.captures);
let mut stack = stack.captures_to_stack_preserve_out_dest(capture_block.captures);
let mut env: HashMap<String, Value> = HashMap::new();

View File

@ -3,7 +3,7 @@ use nu_engine::{command_prelude::*, current_dir};
use nu_path::expand_path_with;
use nu_protocol::{
ast::{Expr, Expression},
DataSource, IoStream, PipelineMetadata, RawStream,
DataSource, OutDest, PipelineMetadata, RawStream,
};
use std::{
fs::File,
@ -261,8 +261,8 @@ impl Command for Save {
]
}
fn stdio_redirect(&self) -> (Option<IoStream>, Option<IoStream>) {
(Some(IoStream::Capture), Some(IoStream::Capture))
fn pipe_redirection(&self) -> (Option<OutDest>, Option<OutDest>) {
(Some(OutDest::Capture), Some(OutDest::Capture))
}
}

View File

@ -1,5 +1,5 @@
use nu_engine::{command_prelude::*, get_eval_block_with_early_return};
use nu_protocol::{engine::Closure, IoStream, RawStream};
use nu_protocol::{engine::Closure, OutDest, RawStream};
use std::{sync::mpsc, thread};
#[derive(Clone)]
@ -73,7 +73,7 @@ use it in your pipeline."#
let closure_engine_state = engine_state.clone();
let mut closure_stack = stack
.captures_to_stack_preserve_stdio(captures)
.captures_to_stack_preserve_out_dest(captures)
.reset_pipes();
let metadata = input.metadata();
@ -198,8 +198,8 @@ use it in your pipeline."#
}
}
fn stdio_redirect(&self) -> (Option<IoStream>, Option<IoStream>) {
(Some(IoStream::Capture), Some(IoStream::Capture))
fn pipe_redirection(&self) -> (Option<OutDest>, Option<OutDest>) {
(Some(OutDest::Capture), Some(OutDest::Capture))
}
}

View File

@ -1,5 +1,5 @@
use nu_engine::command_prelude::*;
use nu_protocol::IoStream;
use nu_protocol::OutDest;
use std::thread;
#[derive(Clone)]
@ -118,7 +118,7 @@ impl Command for Complete {
}]
}
fn stdio_redirect(&self) -> (Option<IoStream>, Option<IoStream>) {
(Some(IoStream::Capture), Some(IoStream::Capture))
fn pipe_redirection(&self) -> (Option<OutDest>, Option<OutDest>) {
(Some(OutDest::Capture), Some(OutDest::Capture))
}
}

View File

@ -1,6 +1,6 @@
use super::run_external::create_external_command;
use nu_engine::{command_prelude::*, current_dir};
use nu_protocol::IoStream;
use nu_protocol::OutDest;
#[derive(Clone)]
pub struct Exec;
@ -59,8 +59,8 @@ fn exec(
call: &Call,
) -> Result<PipelineData, ShellError> {
let mut external_command = create_external_command(engine_state, stack, call)?;
external_command.out = IoStream::Inherit;
external_command.err = IoStream::Inherit;
external_command.out = OutDest::Inherit;
external_command.err = OutDest::Inherit;
let cwd = current_dir(engine_state, stack)?;
let mut command = external_command.spawn_simple_command(&cwd.to_string_lossy())?;

View File

@ -1,6 +1,6 @@
use nu_cmd_base::hook::eval_hook;
use nu_engine::{command_prelude::*, env_to_strings, get_eval_expression};
use nu_protocol::{ast::Expr, did_you_mean, IoStream, ListStream, NuGlob, RawStream};
use nu_protocol::{ast::Expr, did_you_mean, ListStream, NuGlob, OutDest, RawStream};
use nu_system::ForegroundChild;
use nu_utils::IgnoreCaseExt;
use os_pipe::PipeReader;
@ -225,8 +225,8 @@ pub struct ExternalCommand {
pub name: Spanned<String>,
pub args: Vec<Spanned<String>>,
pub arg_keep_raw: Vec<bool>,
pub out: IoStream,
pub err: IoStream,
pub out: OutDest,
pub err: OutDest,
pub env_vars: HashMap<String, String>,
}
@ -523,7 +523,7 @@ impl ExternalCommand {
RawStream::new(Box::new(ByteLines::new(err)), ctrlc.clone(), head, None)
});
if matches!(self.err, IoStream::Pipe) {
if matches!(self.err, OutDest::Pipe) {
(stderr, stdout)
} else {
(stdout, stderr)
@ -634,7 +634,7 @@ impl ExternalCommand {
// If the external is not the last command, its output will get piped
// either as a string or binary
let reader = if matches!(self.out, IoStream::Pipe) && matches!(self.err, IoStream::Pipe) {
let reader = if matches!(self.out, OutDest::Pipe) && matches!(self.err, OutDest::Pipe) {
let (reader, writer) = os_pipe::pipe()?;
let writer_clone = writer.try_clone()?;
process.stdout(writer);

View File

@ -69,7 +69,7 @@ impl CallExt for Call {
if flag_name == name.0.item {
return if let Some(expr) = &name.2 {
// Check --flag=false
let stack = &mut stack.use_call_arg_stdio();
let stack = &mut stack.use_call_arg_out_dest();
let result = eval_expression::<WithoutDebug>(engine_state, stack, expr)?;
match result {
Value::Bool { val, .. } => Ok(val),
@ -96,7 +96,7 @@ impl CallExt for Call {
name: &str,
) -> Result<Option<T>, ShellError> {
if let Some(expr) = self.get_flag_expr(name) {
let stack = &mut stack.use_call_arg_stdio();
let stack = &mut stack.use_call_arg_out_dest();
let result = eval_expression::<WithoutDebug>(engine_state, stack, expr)?;
FromValue::from_value(result).map(Some)
} else {
@ -110,7 +110,7 @@ impl CallExt for Call {
stack: &mut Stack,
starting_pos: usize,
) -> Result<Vec<T>, ShellError> {
let stack = &mut stack.use_call_arg_stdio();
let stack = &mut stack.use_call_arg_out_dest();
let mut output = vec![];
for result in self.rest_iter_flattened(starting_pos, |expr| {
@ -129,7 +129,7 @@ impl CallExt for Call {
pos: usize,
) -> Result<Option<T>, ShellError> {
if let Some(expr) = self.positional_nth(pos) {
let stack = &mut stack.use_call_arg_stdio();
let stack = &mut stack.use_call_arg_out_dest();
let result = eval_expression::<WithoutDebug>(engine_state, stack, expr)?;
FromValue::from_value(result).map(Some)
} else {
@ -157,7 +157,7 @@ impl CallExt for Call {
pos: usize,
) -> Result<T, ShellError> {
if let Some(expr) = self.positional_nth(pos) {
let stack = &mut stack.use_call_arg_stdio();
let stack = &mut stack.use_call_arg_out_dest();
let result = eval_expression::<WithoutDebug>(engine_state, stack, expr)?;
FromValue::from_value(result)
} else if self.positional_len() == 0 {
@ -177,7 +177,7 @@ impl CallExt for Call {
name: &str,
) -> Result<T, ShellError> {
if let Some(expr) = self.get_parser_info(name) {
let stack = &mut stack.use_call_arg_stdio();
let stack = &mut stack.use_call_arg_out_dest();
let result = eval_expression::<WithoutDebug>(engine_state, stack, expr)?;
FromValue::from_value(result)
} else if self.parser_info.is_empty() {

View File

@ -8,7 +8,7 @@ use nu_protocol::{
debugger::DebugContext,
engine::{Closure, EngineState, Redirection, Stack},
eval_base::Eval,
Config, FromValue, IntoPipelineData, IoStream, PipelineData, ShellError, Span, Spanned, Type,
Config, FromValue, IntoPipelineData, OutDest, PipelineData, ShellError, Span, Spanned, Type,
Value, VarId, ENV_VARIABLE_ID,
};
use std::{borrow::Cow, fs::OpenOptions, path::PathBuf};
@ -302,8 +302,8 @@ pub fn eval_expression_with_input<D: DebugContext>(
// If input is PipelineData::ExternalStream,
// then `might_consume_external_result` will consume `stderr` if `stdout` is `None`.
// This should not happen if the user wants to capture stderr.
if !matches!(stack.stdout(), IoStream::Pipe | IoStream::Capture)
&& matches!(stack.stderr(), IoStream::Capture)
if !matches!(stack.stdout(), OutDest::Pipe | OutDest::Capture)
&& matches!(stack.stderr(), OutDest::Capture)
{
Ok((input, false))
} else {
@ -320,7 +320,7 @@ fn eval_redirection<D: DebugContext>(
engine_state: &EngineState,
stack: &mut Stack,
target: &RedirectionTarget,
next_out: Option<IoStream>,
next_out: Option<OutDest>,
) -> Result<Redirection, ShellError> {
match target {
RedirectionTarget::File { expr, append, .. } => {
@ -337,7 +337,7 @@ fn eval_redirection<D: DebugContext>(
}
Ok(Redirection::file(options.create(true).open(path)?))
}
RedirectionTarget::Pipe { .. } => Ok(Redirection::Pipe(next_out.unwrap_or(IoStream::Pipe))),
RedirectionTarget::Pipe { .. } => Ok(Redirection::Pipe(next_out.unwrap_or(OutDest::Pipe))),
}
}
@ -345,7 +345,7 @@ fn eval_element_redirection<D: DebugContext>(
engine_state: &EngineState,
stack: &mut Stack,
element_redirection: Option<&PipelineRedirection>,
pipe_redirection: (Option<IoStream>, Option<IoStream>),
pipe_redirection: (Option<OutDest>, Option<OutDest>),
) -> Result<(Option<Redirection>, Option<Redirection>), ShellError> {
let (next_out, next_err) = pipe_redirection;
@ -363,7 +363,7 @@ fn eval_element_redirection<D: DebugContext>(
target,
} => {
let stderr = eval_redirection::<D>(engine_state, stack, target, None)?;
if matches!(stderr, Redirection::Pipe(IoStream::Pipe)) {
if matches!(stderr, Redirection::Pipe(OutDest::Pipe)) {
// e>| redirection, don't override current stack `stdout`
Ok((
None,
@ -438,10 +438,10 @@ fn eval_element_with_input_inner<D: DebugContext>(
}
}
let data = if matches!(stack.pipe_stdout(), Some(IoStream::File(_)))
&& !matches!(stack.pipe_stderr(), Some(IoStream::Pipe))
let data = if matches!(stack.pipe_stdout(), Some(OutDest::File(_)))
&& !matches!(stack.pipe_stderr(), Some(OutDest::Pipe))
{
data.write_to_io_streams(engine_state, stack)?
data.write_to_out_dests(engine_state, stack)?
} else {
data
};
@ -496,12 +496,12 @@ pub fn eval_block<D: DebugContext>(
for (i, element) in elements.iter().enumerate() {
let next = elements.get(i + 1).unwrap_or(last);
let (next_out, next_err) = next.stdio_redirect(engine_state);
let (next_out, next_err) = next.pipe_redirection(engine_state);
let (stdout, stderr) = eval_element_redirection::<D>(
engine_state,
stack,
element.redirection.as_ref(),
(next_out.or(Some(IoStream::Pipe)), next_err),
(next_out.or(Some(OutDest::Pipe)), next_err),
)?;
let stack = &mut stack.push_redirection(stdout, stderr);
let (output, failed) =

View File

@ -3,7 +3,7 @@ use nu_parser::parse;
use nu_protocol::{
debugger::WithoutDebug,
engine::{EngineState, Redirection, Stack, StateWorkingSet},
IoStream, PipelineData, ShellError, Value,
OutDest, PipelineData, ShellError, Value,
};
use std::sync::Arc;
@ -96,8 +96,8 @@ fn eval_source2(
}
let stack = &mut stack.push_redirection(
Some(Redirection::Pipe(IoStream::Capture)),
Some(Redirection::Pipe(IoStream::Capture)),
Some(Redirection::Pipe(OutDest::Capture)),
Some(Redirection::Pipe(OutDest::Capture)),
);
eval_block::<WithoutDebug>(engine_state, stack, &block, input)
}

View File

@ -3,7 +3,7 @@ use nu_engine::{get_eval_block_with_early_return, get_full_help};
use nu_protocol::{
ast::Call,
engine::{Closure, EngineState, Redirection, Stack},
Config, IntoSpanned, IoStream, PipelineData, PluginIdentity, ShellError, Span, Spanned, Value,
Config, IntoSpanned, OutDest, PipelineData, PluginIdentity, ShellError, Span, Spanned, Value,
};
use std::{
borrow::Cow,
@ -188,13 +188,13 @@ impl<'a> PluginExecutionContext for PluginExecutionCommandContext<'a> {
.reset_pipes();
let stdout = if redirect_stdout {
Some(Redirection::Pipe(IoStream::Capture))
Some(Redirection::Pipe(OutDest::Capture))
} else {
None
};
let stderr = if redirect_stderr {
Some(Redirection::Pipe(IoStream::Capture))
Some(Redirection::Pipe(OutDest::Capture))
} else {
None
};

View File

@ -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)
}

View File

@ -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,

View File

@ -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)
}

View File

@ -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)
}
}

View File

@ -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::*;

View File

@ -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
}
}

View File

@ -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);
}
}
}

View File

@ -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()),
}
}
}

View File

@ -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()?)?;
}
}

View 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()),
}
}
}