nu_protocol/engine/
stack_out_dest.rs1use 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 Pipe(OutDest),
17 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 pub pipe_stdout: Option<OutDest>,
34 pub pipe_stderr: Option<OutDest>,
36 pub stdout: OutDest,
40 pub stderr: OutDest,
44 pub parent_stdout: Option<OutDest>,
52 pub parent_stderr: Option<OutDest>,
60 pub invocation_stdout: Vec<OutDest>,
69}
70
71impl StackOutDest {
72 pub(crate) fn new() -> Self {
73 Self {
74 pipe_stdout: Some(OutDest::Print),
75 pipe_stderr: Some(OutDest::Print),
76 stdout: OutDest::Inherit,
77 stderr: OutDest::Inherit,
78 parent_stdout: None,
79 parent_stderr: None,
80 invocation_stdout: Vec::new(),
81 }
82 }
83
84 pub(crate) fn stdout(&self) -> &OutDest {
90 self.pipe_stdout.as_ref().unwrap_or(&self.stdout)
91 }
92
93 pub(crate) fn stderr(&self) -> &OutDest {
99 self.pipe_stderr.as_ref().unwrap_or(&self.stderr)
100 }
101
102 fn push_stdout(&mut self, stdout: OutDest) -> Option<OutDest> {
103 let stdout = mem::replace(&mut self.stdout, stdout);
104 self.parent_stdout.replace(stdout)
105 }
106
107 fn push_stderr(&mut self, stderr: OutDest) -> Option<OutDest> {
108 let stderr = mem::replace(&mut self.stderr, stderr);
109 self.parent_stderr.replace(stderr)
110 }
111}
112
113pub struct StackIoGuard<'a> {
114 stack: &'a mut Stack,
115 old_pipe_stdout: Option<OutDest>,
116 old_pipe_stderr: Option<OutDest>,
117 old_parent_stdout: Option<OutDest>,
118 old_parent_stderr: Option<OutDest>,
119}
120
121impl<'a> StackIoGuard<'a> {
122 pub(crate) fn new(
123 stack: &'a mut Stack,
124 stdout: Option<Redirection>,
125 stderr: Option<Redirection>,
126 ) -> Self {
127 let out_dest = &mut stack.out_dest;
128
129 let (old_pipe_stdout, old_parent_stdout) = match stdout {
130 Some(Redirection::Pipe(stdout)) => {
131 let old = out_dest.pipe_stdout.replace(stdout);
132 (old, out_dest.parent_stdout.take())
133 }
134 Some(Redirection::File(file)) => {
135 let file = OutDest::from(file);
136 (
137 out_dest.pipe_stdout.replace(file.clone()),
138 out_dest.push_stdout(file),
139 )
140 }
141 None => (out_dest.pipe_stdout.take(), out_dest.parent_stdout.take()),
142 };
143
144 let (old_pipe_stderr, old_parent_stderr) = match stderr {
145 Some(Redirection::Pipe(stderr)) => {
146 let old = out_dest.pipe_stderr.replace(stderr);
147 (old, out_dest.parent_stderr.take())
148 }
149 Some(Redirection::File(file)) => (
150 out_dest.pipe_stderr.take(),
151 out_dest.push_stderr(file.into()),
152 ),
153 None => (out_dest.pipe_stderr.take(), out_dest.parent_stderr.take()),
154 };
155
156 StackIoGuard {
157 stack,
158 old_pipe_stdout,
159 old_parent_stdout,
160 old_pipe_stderr,
161 old_parent_stderr,
162 }
163 }
164}
165
166impl Deref for StackIoGuard<'_> {
167 type Target = Stack;
168
169 fn deref(&self) -> &Self::Target {
170 self.stack
171 }
172}
173
174impl DerefMut for StackIoGuard<'_> {
175 fn deref_mut(&mut self) -> &mut Self::Target {
176 self.stack
177 }
178}
179
180impl Drop for StackIoGuard<'_> {
181 fn drop(&mut self) {
182 self.out_dest.pipe_stdout = self.old_pipe_stdout.take();
183 self.out_dest.pipe_stderr = self.old_pipe_stderr.take();
184
185 let old_stdout = self.old_parent_stdout.take();
186 if let Some(stdout) = mem::replace(&mut self.out_dest.parent_stdout, old_stdout) {
187 self.out_dest.stdout = stdout;
188 }
189
190 let old_stderr = self.old_parent_stderr.take();
191 if let Some(stderr) = mem::replace(&mut self.out_dest.parent_stderr, old_stderr) {
192 self.out_dest.stderr = stderr;
193 }
194 }
195}
196
197pub struct StackCollectValueGuard<'a> {
198 stack: &'a mut Stack,
199 old_pipe_stdout: Option<OutDest>,
200 old_pipe_stderr: Option<OutDest>,
201}
202
203impl<'a> StackCollectValueGuard<'a> {
204 pub(crate) fn new(stack: &'a mut Stack) -> Self {
205 let old_pipe_stdout = stack.out_dest.pipe_stdout.replace(OutDest::Value);
206 let old_pipe_stderr = stack.out_dest.pipe_stderr.take();
207 Self {
208 stack,
209 old_pipe_stdout,
210 old_pipe_stderr,
211 }
212 }
213}
214
215impl Deref for StackCollectValueGuard<'_> {
216 type Target = Stack;
217
218 fn deref(&self) -> &Self::Target {
219 &*self.stack
220 }
221}
222
223impl DerefMut for StackCollectValueGuard<'_> {
224 fn deref_mut(&mut self) -> &mut Self::Target {
225 self.stack
226 }
227}
228
229impl Drop for StackCollectValueGuard<'_> {
230 fn drop(&mut self) {
231 self.out_dest.pipe_stdout = self.old_pipe_stdout.take();
232 self.out_dest.pipe_stderr = self.old_pipe_stderr.take();
233 }
234}
235
236pub struct StackCallArgGuard<'a> {
237 stack: &'a mut Stack,
238 old_pipe_stdout: Option<OutDest>,
239 old_pipe_stderr: Option<OutDest>,
240 old_stdout: Option<OutDest>,
241 old_stderr: Option<OutDest>,
242}
243
244impl<'a> StackCallArgGuard<'a> {
245 pub(crate) fn new(stack: &'a mut Stack) -> Self {
246 let old_pipe_stdout = stack.out_dest.pipe_stdout.replace(OutDest::Value);
247 let old_pipe_stderr = stack.out_dest.pipe_stderr.take();
248
249 let old_stdout = stack
250 .out_dest
251 .parent_stdout
252 .take()
253 .map(|stdout| mem::replace(&mut stack.out_dest.stdout, stdout));
254
255 let old_stderr = stack
256 .out_dest
257 .parent_stderr
258 .take()
259 .map(|stderr| mem::replace(&mut stack.out_dest.stderr, stderr));
260
261 Self {
262 stack,
263 old_pipe_stdout,
264 old_pipe_stderr,
265 old_stdout,
266 old_stderr,
267 }
268 }
269}
270
271impl Deref for StackCallArgGuard<'_> {
272 type Target = Stack;
273
274 fn deref(&self) -> &Self::Target {
275 &*self.stack
276 }
277}
278
279impl DerefMut for StackCallArgGuard<'_> {
280 fn deref_mut(&mut self) -> &mut Self::Target {
281 self.stack
282 }
283}
284
285impl Drop for StackCallArgGuard<'_> {
286 fn drop(&mut self) {
287 self.out_dest.pipe_stdout = self.old_pipe_stdout.take();
288 self.out_dest.pipe_stderr = self.old_pipe_stderr.take();
289 if let Some(stdout) = self.old_stdout.take() {
290 self.out_dest.push_stdout(stdout);
291 }
292 if let Some(stderr) = self.old_stderr.take() {
293 self.out_dest.push_stderr(stderr);
294 }
295 }
296}
297
298pub struct StackWithInvocation {
313 stack: Stack,
314}
315
316impl StackWithInvocation {
317 pub(crate) fn new(mut stack: Stack, dest: OutDest) -> Self {
321 stack.out_dest.invocation_stdout.push(dest);
322 Self { stack }
323 }
324}
325
326impl Deref for StackWithInvocation {
327 type Target = Stack;
328
329 fn deref(&self) -> &Self::Target {
330 &self.stack
331 }
332}
333
334impl DerefMut for StackWithInvocation {
335 fn deref_mut(&mut self) -> &mut Self::Target {
336 &mut self.stack
337 }
338}
339
340impl Drop for StackWithInvocation {
341 fn drop(&mut self) {
342 self.stack.out_dest.invocation_stdout.pop();
344 }
345}