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}