Skip to main content

nu_protocol/pipeline/
out_dest.rs

1use std::{fs::File, io, process::Stdio, sync::Arc};
2
3/// Describes where to direct a command's stdout or stderr.
4///
5/// Used both for external-process stdio wiring and for Nushell's internal
6/// pipeline destination (print vs pipe vs collect-to-value, etc.).
7#[derive(Debug, Clone)]
8pub enum OutDest {
9    /// Redirect the stdout and/or stderr of one command as the input for the next command in the pipeline.
10    ///
11    /// The output pipe will be available as the `stdout` of [`ChildProcess`](crate::process::ChildProcess).
12    ///
13    /// If stdout and stderr are both set to `Pipe`,
14    /// then they will combined into the `stdout` of [`ChildProcess`](crate::process::ChildProcess).
15    Pipe,
16    /// Redirect the stdout and/or stderr of one command as the input for the next command in the pipeline.
17    ///
18    /// The output stream(s) will be available in the `stdout` or `stderr` of [`ChildProcess`](crate::process::ChildProcess).
19    ///
20    /// This is similar to `Pipe` but will never combine stdout and stderr
21    /// or place an external command's stderr into `stdout` of [`ChildProcess`](crate::process::ChildProcess).
22    PipeSeparate,
23    /// Signifies the result of the pipeline will be immediately collected into a value after this command.
24    ///
25    /// So, it is fine to collect the stream ahead of time in the current command.
26    Value,
27    /// Ignore output.
28    ///
29    /// This will forward output to the null device for the platform.
30    Null,
31    /// Output to nushell's stdout or stderr (only for external commands).
32    ///
33    /// This causes external commands to inherit nushell's stdout or stderr. This also causes
34    /// [`ListStream`](crate::ListStream)s to be drained, but not to be printed.
35    Inherit,
36    /// Print to nushell's stdout or stderr.
37    ///
38    /// This is just like `Inherit`, except that [`ListStream`](crate::ListStream)s and
39    /// [`Value`](crate::Value)s are also printed.
40    ///
41    /// This is the only destination treated as "not redirected" by
42    /// [`OutDest::is_redirected`] / the `is-redirected` command.
43    Print,
44    /// Redirect output to a file.
45    File(Arc<File>), // Arc<File>, since we sometimes need to clone `OutDest` into iterators, etc.
46}
47
48impl OutDest {
49    /// Returns `true` when output is *not* sent through the interactive display path.
50    ///
51    /// Only [`OutDest::Print`] is treated as displayed. Every other variant means the
52    /// pipeline result is consumed elsewhere (next command, `let`/subexpression,
53    /// file, null device, or inherit-without-printing).
54    ///
55    /// This is **not** an OS-level TTY check. For whether process stdio is a terminal,
56    /// use the `is-terminal` command (`std::io::IsTerminal`).
57    ///
58    /// # Examples
59    ///
60    /// ```
61    /// # use nu_protocol::OutDest;
62    /// assert!(!OutDest::Print.is_redirected());
63    /// assert!(OutDest::Pipe.is_redirected());
64    /// assert!(OutDest::Value.is_redirected());
65    /// ```
66    #[must_use]
67    pub fn is_redirected(&self) -> bool {
68        !matches!(self, Self::Print)
69    }
70}
71
72impl From<File> for OutDest {
73    fn from(file: File) -> Self {
74        Arc::new(file).into()
75    }
76}
77
78impl From<Arc<File>> for OutDest {
79    fn from(file: Arc<File>) -> Self {
80        Self::File(file)
81    }
82}
83
84impl TryFrom<&OutDest> for Stdio {
85    type Error = io::Error;
86
87    fn try_from(out_dest: &OutDest) -> Result<Self, Self::Error> {
88        match out_dest {
89            OutDest::Pipe | OutDest::PipeSeparate | OutDest::Value => Ok(Self::piped()),
90            OutDest::Null => Ok(Self::null()),
91            OutDest::Print | OutDest::Inherit => Ok(Self::inherit()),
92            OutDest::File(file) => Ok(file.try_clone()?.into()),
93        }
94    }
95}