nu_protocol/engine/
stack_out_dest.rs

1use crate::{OutDest, engine::Stack};
2use std::{
3    fs::File,
4    mem,
5    ops::{Deref, DerefMut},
6    sync::Arc,
7};
8
9#[derive(Debug, Clone)]
10pub enum Redirection {
11    /// A pipe redirection.
12    ///
13    /// This will only affect the last command of a block.
14    /// This is created by pipes and pipe redirections (`|`, `e>|`, `o+e>|`, etc.),
15    /// or set by the next command in the pipeline (e.g., `ignore` sets stdout to [`OutDest::Null`]).
16    Pipe(OutDest),
17    /// A file redirection.
18    ///
19    /// This will affect all commands in the block.
20    /// This is only created by file redirections (`o>`, `e>`, `o+e>`, etc.).
21    File(Arc<File>),
22}
23
24impl Redirection {
25    pub fn file(file: File) -> Self {
26        Self::File(Arc::new(file))
27    }
28}
29
30#[derive(Debug, Clone)]
31pub(crate) struct StackOutDest {
32    /// The stream to use for the next command's stdout.
33    pub pipe_stdout: Option<OutDest>,
34    /// The stream to use for the next command's stderr.
35    pub pipe_stderr: Option<OutDest>,
36    /// The stream used for the command stdout if `pipe_stdout` is `None`.
37    ///
38    /// This should only ever be `File` or `Inherit`.
39    pub stdout: OutDest,
40    /// The stream used for the command stderr if `pipe_stderr` is `None`.
41    ///
42    /// This should only ever be `File` or `Inherit`.
43    pub stderr: OutDest,
44    /// The previous stdout used before the current `stdout` was set.
45    ///
46    /// This is used only when evaluating arguments to commands,
47    /// since the arguments are lazily evaluated inside each command
48    /// after redirections have already been applied to the command/stack.
49    ///
50    /// This should only ever be `File` or `Inherit`.
51    pub parent_stdout: Option<OutDest>,
52    /// The previous stderr used before the current `stderr` was set.
53    ///
54    /// This is used only when evaluating arguments to commands,
55    /// since the arguments are lazily evaluated inside each command
56    /// after redirections have already been applied to the command/stack.
57    ///
58    /// This should only ever be `File` or `Inherit`.
59    pub parent_stderr: Option<OutDest>,
60}
61
62impl StackOutDest {
63    pub(crate) fn new() -> Self {
64        Self {
65            pipe_stdout: Some(OutDest::Print),
66            pipe_stderr: Some(OutDest::Print),
67            stdout: OutDest::Inherit,
68            stderr: OutDest::Inherit,
69            parent_stdout: None,
70            parent_stderr: None,
71        }
72    }
73
74    /// Returns the [`OutDest`] to use for current command's stdout.
75    ///
76    /// This will be the pipe redirection if one is set,
77    /// otherwise it will be the current file redirection,
78    /// otherwise it will be the process's stdout indicated by [`OutDest::Inherit`].
79    pub(crate) fn stdout(&self) -> &OutDest {
80        self.pipe_stdout.as_ref().unwrap_or(&self.stdout)
81    }
82
83    /// Returns the [`OutDest`] to use for current command's stderr.
84    ///
85    /// This will be the pipe redirection if one is set,
86    /// otherwise it will be the current file redirection,
87    /// otherwise it will be the process's stderr indicated by [`OutDest::Inherit`].
88    pub(crate) fn stderr(&self) -> &OutDest {
89        self.pipe_stderr.as_ref().unwrap_or(&self.stderr)
90    }
91
92    fn push_stdout(&mut self, stdout: OutDest) -> Option<OutDest> {
93        let stdout = mem::replace(&mut self.stdout, stdout);
94        self.parent_stdout.replace(stdout)
95    }
96
97    fn push_stderr(&mut self, stderr: OutDest) -> Option<OutDest> {
98        let stderr = mem::replace(&mut self.stderr, stderr);
99        self.parent_stderr.replace(stderr)
100    }
101}
102
103pub struct StackIoGuard<'a> {
104    stack: &'a mut Stack,
105    old_pipe_stdout: Option<OutDest>,
106    old_pipe_stderr: Option<OutDest>,
107    old_parent_stdout: Option<OutDest>,
108    old_parent_stderr: Option<OutDest>,
109}
110
111impl<'a> StackIoGuard<'a> {
112    pub(crate) fn new(
113        stack: &'a mut Stack,
114        stdout: Option<Redirection>,
115        stderr: Option<Redirection>,
116    ) -> Self {
117        let out_dest = &mut stack.out_dest;
118
119        let (old_pipe_stdout, old_parent_stdout) = match stdout {
120            Some(Redirection::Pipe(stdout)) => {
121                let old = out_dest.pipe_stdout.replace(stdout);
122                (old, out_dest.parent_stdout.take())
123            }
124            Some(Redirection::File(file)) => {
125                let file = OutDest::from(file);
126                (
127                    out_dest.pipe_stdout.replace(file.clone()),
128                    out_dest.push_stdout(file),
129                )
130            }
131            None => (out_dest.pipe_stdout.take(), out_dest.parent_stdout.take()),
132        };
133
134        let (old_pipe_stderr, old_parent_stderr) = match stderr {
135            Some(Redirection::Pipe(stderr)) => {
136                let old = out_dest.pipe_stderr.replace(stderr);
137                (old, out_dest.parent_stderr.take())
138            }
139            Some(Redirection::File(file)) => (
140                out_dest.pipe_stderr.take(),
141                out_dest.push_stderr(file.into()),
142            ),
143            None => (out_dest.pipe_stderr.take(), out_dest.parent_stderr.take()),
144        };
145
146        StackIoGuard {
147            stack,
148            old_pipe_stdout,
149            old_parent_stdout,
150            old_pipe_stderr,
151            old_parent_stderr,
152        }
153    }
154}
155
156impl Deref for StackIoGuard<'_> {
157    type Target = Stack;
158
159    fn deref(&self) -> &Self::Target {
160        self.stack
161    }
162}
163
164impl DerefMut for StackIoGuard<'_> {
165    fn deref_mut(&mut self) -> &mut Self::Target {
166        self.stack
167    }
168}
169
170impl Drop for StackIoGuard<'_> {
171    fn drop(&mut self) {
172        self.out_dest.pipe_stdout = self.old_pipe_stdout.take();
173        self.out_dest.pipe_stderr = self.old_pipe_stderr.take();
174
175        let old_stdout = self.old_parent_stdout.take();
176        if let Some(stdout) = mem::replace(&mut self.out_dest.parent_stdout, old_stdout) {
177            self.out_dest.stdout = stdout;
178        }
179
180        let old_stderr = self.old_parent_stderr.take();
181        if let Some(stderr) = mem::replace(&mut self.out_dest.parent_stderr, old_stderr) {
182            self.out_dest.stderr = stderr;
183        }
184    }
185}
186
187pub struct StackCollectValueGuard<'a> {
188    stack: &'a mut Stack,
189    old_pipe_stdout: Option<OutDest>,
190    old_pipe_stderr: Option<OutDest>,
191}
192
193impl<'a> StackCollectValueGuard<'a> {
194    pub(crate) fn new(stack: &'a mut Stack) -> Self {
195        let old_pipe_stdout = stack.out_dest.pipe_stdout.replace(OutDest::Value);
196        let old_pipe_stderr = stack.out_dest.pipe_stderr.take();
197        Self {
198            stack,
199            old_pipe_stdout,
200            old_pipe_stderr,
201        }
202    }
203}
204
205impl Deref for StackCollectValueGuard<'_> {
206    type Target = Stack;
207
208    fn deref(&self) -> &Self::Target {
209        &*self.stack
210    }
211}
212
213impl DerefMut for StackCollectValueGuard<'_> {
214    fn deref_mut(&mut self) -> &mut Self::Target {
215        self.stack
216    }
217}
218
219impl Drop for StackCollectValueGuard<'_> {
220    fn drop(&mut self) {
221        self.out_dest.pipe_stdout = self.old_pipe_stdout.take();
222        self.out_dest.pipe_stderr = self.old_pipe_stderr.take();
223    }
224}
225
226pub struct StackCallArgGuard<'a> {
227    stack: &'a mut Stack,
228    old_pipe_stdout: Option<OutDest>,
229    old_pipe_stderr: Option<OutDest>,
230    old_stdout: Option<OutDest>,
231    old_stderr: Option<OutDest>,
232}
233
234impl<'a> StackCallArgGuard<'a> {
235    pub(crate) fn new(stack: &'a mut Stack) -> Self {
236        let old_pipe_stdout = stack.out_dest.pipe_stdout.replace(OutDest::Value);
237        let old_pipe_stderr = stack.out_dest.pipe_stderr.take();
238
239        let old_stdout = stack
240            .out_dest
241            .parent_stdout
242            .take()
243            .map(|stdout| mem::replace(&mut stack.out_dest.stdout, stdout));
244
245        let old_stderr = stack
246            .out_dest
247            .parent_stderr
248            .take()
249            .map(|stderr| mem::replace(&mut stack.out_dest.stderr, stderr));
250
251        Self {
252            stack,
253            old_pipe_stdout,
254            old_pipe_stderr,
255            old_stdout,
256            old_stderr,
257        }
258    }
259}
260
261impl Deref for StackCallArgGuard<'_> {
262    type Target = Stack;
263
264    fn deref(&self) -> &Self::Target {
265        &*self.stack
266    }
267}
268
269impl DerefMut for StackCallArgGuard<'_> {
270    fn deref_mut(&mut self) -> &mut Self::Target {
271        self.stack
272    }
273}
274
275impl Drop for StackCallArgGuard<'_> {
276    fn drop(&mut self) {
277        self.out_dest.pipe_stdout = self.old_pipe_stdout.take();
278        self.out_dest.pipe_stderr = self.old_pipe_stderr.take();
279        if let Some(stdout) = self.old_stdout.take() {
280            self.out_dest.push_stdout(stdout);
281        }
282        if let Some(stderr) = self.old_stderr.take() {
283            self.out_dest.push_stderr(stderr);
284        }
285    }
286}