nu_protocol/engine/
stack.rs

1use crate::{
2    Config, ENV_VARIABLE_ID, IntoValue, NU_VARIABLE_ID, OutDest, ShellError, Span, Value, VarId,
3    engine::{
4        ArgumentStack, DEFAULT_OVERLAY_NAME, EngineState, ErrorHandlerStack, Redirection,
5        StackCallArgGuard, StackCollectValueGuard, StackIoGuard, StackOutDest,
6    },
7};
8use nu_utils::IgnoreCaseExt;
9use std::{
10    collections::{HashMap, HashSet},
11    fs::File,
12    path::{Component, MAIN_SEPARATOR},
13    sync::Arc,
14};
15
16/// Environment variables per overlay
17pub type EnvVars = HashMap<String, HashMap<String, Value>>;
18
19/// A runtime value stack used during evaluation
20///
21/// A note on implementation:
22///
23/// We previously set up the stack in a traditional way, where stack frames had parents which would
24/// represent other frames that you might return to when exiting a function.
25///
26/// While experimenting with blocks, we found that we needed to have closure captures of variables
27/// seen outside of the blocks, so that they blocks could be run in a way that was both thread-safe
28/// and followed the restrictions for closures applied to iterators. The end result left us with
29/// closure-captured single stack frames that blocks could see.
30///
31/// Blocks make up the only scope and stack definition abstraction in Nushell. As a result, we were
32/// creating closure captures at any point we wanted to have a Block value we could safely evaluate
33/// in any context. This meant that the parents were going largely unused, with captured variables
34/// taking their place. The end result is this, where we no longer have separate frames, but instead
35/// use the Stack as a way of representing the local and closure-captured state.
36#[derive(Debug, Clone)]
37pub struct Stack {
38    /// Variables
39    pub vars: Vec<(VarId, Value)>,
40    /// Environment variables arranged as a stack to be able to recover values from parent scopes
41    pub env_vars: Vec<Arc<EnvVars>>,
42    /// Tells which environment variables from engine state are hidden, per overlay.
43    pub env_hidden: Arc<HashMap<String, HashSet<String>>>,
44    /// List of active overlays
45    pub active_overlays: Vec<String>,
46    /// Argument stack for IR evaluation
47    pub arguments: ArgumentStack,
48    /// Error handler stack for IR evaluation
49    pub error_handlers: ErrorHandlerStack,
50    pub recursion_count: u64,
51    pub parent_stack: Option<Arc<Stack>>,
52    /// Variables that have been deleted (this is used to hide values from parent stack lookups)
53    pub parent_deletions: Vec<VarId>,
54    /// Locally updated config. Use [`.get_config()`](Self::get_config) to access correctly.
55    pub config: Option<Arc<Config>>,
56    pub(crate) out_dest: StackOutDest,
57}
58
59impl Default for Stack {
60    fn default() -> Self {
61        Self::new()
62    }
63}
64
65impl Stack {
66    /// Create a new stack.
67    ///
68    /// stdout and stderr will be set to [`OutDest::Inherit`]. So, if the last command is an external command,
69    /// then its output will be forwarded to the terminal/stdio streams.
70    ///
71    /// Use [`Stack::collect_value`] afterwards if you need to evaluate an expression to a [`Value`]
72    /// (as opposed to a [`PipelineData`](crate::PipelineData)).
73    pub fn new() -> Self {
74        Self {
75            vars: Vec::new(),
76            env_vars: Vec::new(),
77            env_hidden: Arc::new(HashMap::new()),
78            active_overlays: vec![DEFAULT_OVERLAY_NAME.to_string()],
79            arguments: ArgumentStack::new(),
80            error_handlers: ErrorHandlerStack::new(),
81            recursion_count: 0,
82            parent_stack: None,
83            parent_deletions: vec![],
84            config: None,
85            out_dest: StackOutDest::new(),
86        }
87    }
88
89    /// Create a new child stack from a parent.
90    ///
91    /// Changes from this child can be merged back into the parent with
92    /// [`Stack::with_changes_from_child`]
93    pub fn with_parent(parent: Arc<Stack>) -> Stack {
94        Stack {
95            // here we are still cloning environment variable-related information
96            env_vars: parent.env_vars.clone(),
97            env_hidden: parent.env_hidden.clone(),
98            active_overlays: parent.active_overlays.clone(),
99            arguments: ArgumentStack::new(),
100            error_handlers: ErrorHandlerStack::new(),
101            recursion_count: parent.recursion_count,
102            vars: vec![],
103            parent_deletions: vec![],
104            config: parent.config.clone(),
105            out_dest: parent.out_dest.clone(),
106            parent_stack: Some(parent),
107        }
108    }
109
110    /// Take an [`Arc`] parent, and a child, and apply all the changes from a child back to the parent.
111    ///
112    /// Here it is assumed that `child` was created by a call to [`Stack::with_parent`] with `parent`.
113    ///
114    /// For this to be performant and not clone `parent`, `child` should be the only other
115    /// referencer of `parent`.
116    pub fn with_changes_from_child(parent: Arc<Stack>, child: Stack) -> Stack {
117        // we're going to drop the link to the parent stack on our new stack
118        // so that we can unwrap the Arc as a unique reference
119        drop(child.parent_stack);
120        let mut unique_stack = Arc::unwrap_or_clone(parent);
121
122        unique_stack
123            .vars
124            .retain(|(var, _)| !child.parent_deletions.contains(var));
125        for (var, value) in child.vars {
126            unique_stack.add_var(var, value);
127        }
128        unique_stack.env_vars = child.env_vars;
129        unique_stack.env_hidden = child.env_hidden;
130        unique_stack.active_overlays = child.active_overlays;
131        unique_stack.config = child.config;
132        unique_stack
133    }
134
135    pub fn with_env(
136        &mut self,
137        env_vars: &[Arc<EnvVars>],
138        env_hidden: &Arc<HashMap<String, HashSet<String>>>,
139    ) {
140        // Do not clone the environment if it hasn't changed
141        if self.env_vars.iter().any(|scope| !scope.is_empty()) {
142            env_vars.clone_into(&mut self.env_vars);
143        }
144
145        if !self.env_hidden.is_empty() {
146            self.env_hidden.clone_from(env_hidden);
147        }
148    }
149
150    /// Lookup a variable, returning None if it is not present
151    fn lookup_var(&self, var_id: VarId) -> Option<Value> {
152        for (id, val) in &self.vars {
153            if var_id == *id {
154                return Some(val.clone());
155            }
156        }
157
158        if let Some(stack) = &self.parent_stack {
159            if !self.parent_deletions.contains(&var_id) {
160                return stack.lookup_var(var_id);
161            }
162        }
163        None
164    }
165
166    /// Lookup a variable, erroring if it is not found
167    ///
168    /// The passed-in span will be used to tag the value
169    pub fn get_var(&self, var_id: VarId, span: Span) -> Result<Value, ShellError> {
170        match self.lookup_var(var_id) {
171            Some(v) => Ok(v.with_span(span)),
172            None => Err(ShellError::VariableNotFoundAtRuntime { span }),
173        }
174    }
175
176    /// Lookup a variable, erroring if it is not found
177    ///
178    /// While the passed-in span will be used for errors, the returned value
179    /// has the span from where it was originally defined
180    pub fn get_var_with_origin(&self, var_id: VarId, span: Span) -> Result<Value, ShellError> {
181        match self.lookup_var(var_id) {
182            Some(v) => Ok(v),
183            None => {
184                if var_id == NU_VARIABLE_ID || var_id == ENV_VARIABLE_ID {
185                    return Err(ShellError::GenericError {
186                        error: "Built-in variables `$env` and `$nu` have no metadata".into(),
187                        msg: "no metadata available".into(),
188                        span: Some(span),
189                        help: None,
190                        inner: vec![],
191                    });
192                }
193                Err(ShellError::VariableNotFoundAtRuntime { span })
194            }
195        }
196    }
197
198    /// Get the local config if set, otherwise the config from the engine state.
199    ///
200    /// This is the canonical way to get [`Config`] when [`Stack`] is available.
201    pub fn get_config(&self, engine_state: &EngineState) -> Arc<Config> {
202        self.config
203            .clone()
204            .unwrap_or_else(|| engine_state.config.clone())
205    }
206
207    /// Update the local config with the config stored in the `config` environment variable. Run
208    /// this after assigning to `$env.config`.
209    ///
210    /// The config will be updated with successfully parsed values even if an error occurs.
211    pub fn update_config(&mut self, engine_state: &EngineState) -> Result<(), ShellError> {
212        if let Some(value) = self.get_env_var(engine_state, "config") {
213            let old = self.get_config(engine_state);
214            let mut config = (*old).clone();
215            let error = config.update_from_value(&old, value);
216            // The config value is modified by the update, so we should add it again
217            self.add_env_var("config".into(), config.clone().into_value(value.span()));
218            self.config = Some(config.into());
219            match error {
220                None => Ok(()),
221                Some(err) => Err(err),
222            }
223        } else {
224            self.config = None;
225            Ok(())
226        }
227    }
228
229    pub fn add_var(&mut self, var_id: VarId, value: Value) {
230        //self.vars.insert(var_id, value);
231        for (id, val) in &mut self.vars {
232            if *id == var_id {
233                *val = value;
234                return;
235            }
236        }
237        self.vars.push((var_id, value));
238    }
239
240    pub fn remove_var(&mut self, var_id: VarId) {
241        for (idx, (id, _)) in self.vars.iter().enumerate() {
242            if *id == var_id {
243                self.vars.remove(idx);
244                break;
245            }
246        }
247        // even if we did have it in the original layer, we need to make sure to remove it here
248        // as well (since the previous update might have simply hid the parent value)
249        if self.parent_stack.is_some() {
250            self.parent_deletions.push(var_id);
251        }
252    }
253
254    pub fn add_env_var(&mut self, var: String, value: Value) {
255        if let Some(last_overlay) = self.active_overlays.last() {
256            if let Some(env_hidden) = Arc::make_mut(&mut self.env_hidden).get_mut(last_overlay) {
257                // if the env var was hidden, let's activate it again
258                env_hidden.remove(&var);
259            }
260
261            if let Some(scope) = self.env_vars.last_mut() {
262                let scope = Arc::make_mut(scope);
263                if let Some(env_vars) = scope.get_mut(last_overlay) {
264                    env_vars.insert(var, value);
265                } else {
266                    scope.insert(last_overlay.into(), [(var, value)].into_iter().collect());
267                }
268            } else {
269                self.env_vars.push(Arc::new(
270                    [(last_overlay.into(), [(var, value)].into_iter().collect())]
271                        .into_iter()
272                        .collect(),
273                ));
274            }
275        } else {
276            // TODO: Remove panic
277            panic!("internal error: no active overlay");
278        }
279    }
280
281    pub fn set_last_exit_code(&mut self, code: i32, span: Span) {
282        self.add_env_var("LAST_EXIT_CODE".into(), Value::int(code.into(), span));
283    }
284
285    pub fn set_last_error(&mut self, error: &ShellError) {
286        if let Some(code) = error.external_exit_code() {
287            self.set_last_exit_code(code.item, code.span);
288        } else if let Some(code) = error.exit_code() {
289            self.set_last_exit_code(code, Span::unknown());
290        }
291    }
292
293    pub fn last_overlay_name(&self) -> Result<String, ShellError> {
294        self.active_overlays
295            .last()
296            .cloned()
297            .ok_or_else(|| ShellError::NushellFailed {
298                msg: "No active overlay".into(),
299            })
300    }
301
302    pub fn captures_to_stack(&self, captures: Vec<(VarId, Value)>) -> Stack {
303        self.captures_to_stack_preserve_out_dest(captures)
304            .collect_value()
305    }
306
307    pub fn captures_to_stack_preserve_out_dest(&self, captures: Vec<(VarId, Value)>) -> Stack {
308        let mut env_vars = self.env_vars.clone();
309        env_vars.push(Arc::new(HashMap::new()));
310
311        Stack {
312            vars: captures,
313            env_vars,
314            env_hidden: self.env_hidden.clone(),
315            active_overlays: self.active_overlays.clone(),
316            arguments: ArgumentStack::new(),
317            error_handlers: ErrorHandlerStack::new(),
318            recursion_count: self.recursion_count,
319            parent_stack: None,
320            parent_deletions: vec![],
321            config: self.config.clone(),
322            out_dest: self.out_dest.clone(),
323        }
324    }
325
326    pub fn gather_captures(&self, engine_state: &EngineState, captures: &[(VarId, Span)]) -> Stack {
327        let mut vars = Vec::with_capacity(captures.len());
328
329        let fake_span = Span::new(0, 0);
330
331        for (capture, _) in captures {
332            // Note: this assumes we have calculated captures correctly and that commands
333            // that take in a var decl will manually set this into scope when running the blocks
334            if let Ok(value) = self.get_var(*capture, fake_span) {
335                vars.push((*capture, value));
336            } else if let Some(const_val) = &engine_state.get_var(*capture).const_val {
337                vars.push((*capture, const_val.clone()));
338            }
339        }
340
341        let mut env_vars = self.env_vars.clone();
342        env_vars.push(Arc::new(HashMap::new()));
343
344        Stack {
345            vars,
346            env_vars,
347            env_hidden: self.env_hidden.clone(),
348            active_overlays: self.active_overlays.clone(),
349            arguments: ArgumentStack::new(),
350            error_handlers: ErrorHandlerStack::new(),
351            recursion_count: self.recursion_count,
352            parent_stack: None,
353            parent_deletions: vec![],
354            config: self.config.clone(),
355            out_dest: self.out_dest.clone(),
356        }
357    }
358
359    /// Flatten the env var scope frames into one frame
360    pub fn get_env_vars(&self, engine_state: &EngineState) -> HashMap<String, Value> {
361        let mut result = HashMap::new();
362
363        for active_overlay in self.active_overlays.iter() {
364            if let Some(env_vars) = engine_state.env_vars.get(active_overlay) {
365                result.extend(
366                    env_vars
367                        .iter()
368                        .filter(|(k, _)| {
369                            if let Some(env_hidden) = self.env_hidden.get(active_overlay) {
370                                !env_hidden.contains(*k)
371                            } else {
372                                // nothing has been hidden in this overlay
373                                true
374                            }
375                        })
376                        .map(|(k, v)| (k.clone(), v.clone()))
377                        .collect::<HashMap<String, Value>>(),
378                );
379            }
380        }
381
382        result.extend(self.get_stack_env_vars());
383
384        result
385    }
386
387    /// Get flattened environment variables only from the stack
388    pub fn get_stack_env_vars(&self) -> HashMap<String, Value> {
389        let mut result = HashMap::new();
390
391        for scope in &self.env_vars {
392            for active_overlay in self.active_overlays.iter() {
393                if let Some(env_vars) = scope.get(active_overlay) {
394                    result.extend(env_vars.clone());
395                }
396            }
397        }
398
399        result
400    }
401
402    /// Get flattened environment variables only from the stack and one overlay
403    pub fn get_stack_overlay_env_vars(&self, overlay_name: &str) -> HashMap<String, Value> {
404        let mut result = HashMap::new();
405
406        for scope in &self.env_vars {
407            if let Some(active_overlay) = self.active_overlays.iter().find(|n| n == &overlay_name) {
408                if let Some(env_vars) = scope.get(active_overlay) {
409                    result.extend(env_vars.clone());
410                }
411            }
412        }
413
414        result
415    }
416
417    /// Same as get_env_vars, but returns only the names as a HashSet
418    pub fn get_env_var_names(&self, engine_state: &EngineState) -> HashSet<String> {
419        let mut result = HashSet::new();
420
421        for active_overlay in self.active_overlays.iter() {
422            if let Some(env_vars) = engine_state.env_vars.get(active_overlay) {
423                result.extend(
424                    env_vars
425                        .keys()
426                        .filter(|k| {
427                            if let Some(env_hidden) = self.env_hidden.get(active_overlay) {
428                                !env_hidden.contains(*k)
429                            } else {
430                                // nothing has been hidden in this overlay
431                                true
432                            }
433                        })
434                        .cloned()
435                        .collect::<HashSet<String>>(),
436                );
437            }
438        }
439
440        for scope in &self.env_vars {
441            for active_overlay in self.active_overlays.iter() {
442                if let Some(env_vars) = scope.get(active_overlay) {
443                    result.extend(env_vars.keys().cloned().collect::<HashSet<String>>());
444                }
445            }
446        }
447
448        result
449    }
450
451    pub fn get_env_var<'a>(
452        &'a self,
453        engine_state: &'a EngineState,
454        name: &str,
455    ) -> Option<&'a Value> {
456        for scope in self.env_vars.iter().rev() {
457            for active_overlay in self.active_overlays.iter().rev() {
458                if let Some(env_vars) = scope.get(active_overlay) {
459                    if let Some(v) = env_vars.get(name) {
460                        return Some(v);
461                    }
462                }
463            }
464        }
465
466        for active_overlay in self.active_overlays.iter().rev() {
467            let is_hidden = if let Some(env_hidden) = self.env_hidden.get(active_overlay) {
468                env_hidden.contains(name)
469            } else {
470                false
471            };
472
473            if !is_hidden {
474                if let Some(env_vars) = engine_state.env_vars.get(active_overlay) {
475                    if let Some(v) = env_vars.get(name) {
476                        return Some(v);
477                    }
478                }
479            }
480        }
481        None
482    }
483
484    // Case-Insensitive version of get_env_var
485    // Returns Some((name, value)) if found, None otherwise.
486    // When updating environment variables, make sure to use
487    // the same case (from the returned "name") as the original
488    // environment variable name.
489    pub fn get_env_var_insensitive<'a>(
490        &'a self,
491        engine_state: &'a EngineState,
492        name: &str,
493    ) -> Option<(&'a String, &'a Value)> {
494        for scope in self.env_vars.iter().rev() {
495            for active_overlay in self.active_overlays.iter().rev() {
496                if let Some(env_vars) = scope.get(active_overlay) {
497                    if let Some(v) = env_vars.iter().find(|(k, _)| k.eq_ignore_case(name)) {
498                        return Some((v.0, v.1));
499                    }
500                }
501            }
502        }
503
504        for active_overlay in self.active_overlays.iter().rev() {
505            let is_hidden = if let Some(env_hidden) = self.env_hidden.get(active_overlay) {
506                env_hidden.iter().any(|k| k.eq_ignore_case(name))
507            } else {
508                false
509            };
510
511            if !is_hidden {
512                if let Some(env_vars) = engine_state.env_vars.get(active_overlay) {
513                    if let Some(v) = env_vars.iter().find(|(k, _)| k.eq_ignore_case(name)) {
514                        return Some((v.0, v.1));
515                    }
516                }
517            }
518        }
519        None
520    }
521
522    pub fn has_env_var(&self, engine_state: &EngineState, name: &str) -> bool {
523        for scope in self.env_vars.iter().rev() {
524            for active_overlay in self.active_overlays.iter().rev() {
525                if let Some(env_vars) = scope.get(active_overlay) {
526                    if env_vars.contains_key(name) {
527                        return true;
528                    }
529                }
530            }
531        }
532
533        for active_overlay in self.active_overlays.iter().rev() {
534            let is_hidden = if let Some(env_hidden) = self.env_hidden.get(active_overlay) {
535                env_hidden.contains(name)
536            } else {
537                false
538            };
539
540            if !is_hidden {
541                if let Some(env_vars) = engine_state.env_vars.get(active_overlay) {
542                    if env_vars.contains_key(name) {
543                        return true;
544                    }
545                }
546            }
547        }
548
549        false
550    }
551
552    pub fn remove_env_var(&mut self, engine_state: &EngineState, name: &str) -> bool {
553        for scope in self.env_vars.iter_mut().rev() {
554            let scope = Arc::make_mut(scope);
555            for active_overlay in self.active_overlays.iter().rev() {
556                if let Some(env_vars) = scope.get_mut(active_overlay) {
557                    if env_vars.remove(name).is_some() {
558                        return true;
559                    }
560                }
561            }
562        }
563
564        for active_overlay in self.active_overlays.iter().rev() {
565            if let Some(env_vars) = engine_state.env_vars.get(active_overlay) {
566                if env_vars.get(name).is_some() {
567                    let env_hidden = Arc::make_mut(&mut self.env_hidden);
568                    if let Some(env_hidden_in_overlay) = env_hidden.get_mut(active_overlay) {
569                        env_hidden_in_overlay.insert(name.into());
570                    } else {
571                        env_hidden
572                            .insert(active_overlay.into(), [name.into()].into_iter().collect());
573                    }
574
575                    return true;
576                }
577            }
578        }
579
580        false
581    }
582
583    pub fn has_env_overlay(&self, name: &str, engine_state: &EngineState) -> bool {
584        for scope in self.env_vars.iter().rev() {
585            if scope.contains_key(name) {
586                return true;
587            }
588        }
589
590        engine_state.env_vars.contains_key(name)
591    }
592
593    pub fn is_overlay_active(&self, name: &str) -> bool {
594        self.active_overlays.iter().any(|n| n == name)
595    }
596
597    pub fn add_overlay(&mut self, name: String) {
598        self.active_overlays.retain(|o| o != &name);
599        self.active_overlays.push(name);
600    }
601
602    pub fn remove_overlay(&mut self, name: &str) {
603        self.active_overlays.retain(|o| o != name);
604    }
605
606    /// Returns the [`OutDest`] to use for the current command's stdout.
607    ///
608    /// This will be the pipe redirection if one is set,
609    /// otherwise it will be the current file redirection,
610    /// otherwise it will be the process's stdout indicated by [`OutDest::Inherit`].
611    pub fn stdout(&self) -> &OutDest {
612        self.out_dest.stdout()
613    }
614
615    /// Returns the [`OutDest`] to use for the current command's stderr.
616    ///
617    /// This will be the pipe redirection if one is set,
618    /// otherwise it will be the current file redirection,
619    /// otherwise it will be the process's stderr indicated by [`OutDest::Inherit`].
620    pub fn stderr(&self) -> &OutDest {
621        self.out_dest.stderr()
622    }
623
624    /// Returns the [`OutDest`] of the pipe redirection applied to the current command's stdout.
625    pub fn pipe_stdout(&self) -> Option<&OutDest> {
626        self.out_dest.pipe_stdout.as_ref()
627    }
628
629    /// Returns the [`OutDest`] of the pipe redirection applied to the current command's stderr.
630    pub fn pipe_stderr(&self) -> Option<&OutDest> {
631        self.out_dest.pipe_stderr.as_ref()
632    }
633
634    /// Temporarily set the pipe stdout redirection to [`OutDest::Value`].
635    ///
636    /// This is used before evaluating an expression into a `Value`.
637    pub fn start_collect_value(&mut self) -> StackCollectValueGuard {
638        StackCollectValueGuard::new(self)
639    }
640
641    /// Temporarily use the output redirections in the parent scope.
642    ///
643    /// This is used before evaluating an argument to a call.
644    pub fn use_call_arg_out_dest(&mut self) -> StackCallArgGuard {
645        StackCallArgGuard::new(self)
646    }
647
648    /// Temporarily apply redirections to stdout and/or stderr.
649    pub fn push_redirection(
650        &mut self,
651        stdout: Option<Redirection>,
652        stderr: Option<Redirection>,
653    ) -> StackIoGuard {
654        StackIoGuard::new(self, stdout, stderr)
655    }
656
657    /// Mark stdout for the last command as [`OutDest::Value`].
658    ///
659    /// This will irreversibly alter the output redirections, and so it only makes sense to use this on an owned `Stack`
660    /// (which is why this function does not take `&mut self`).
661    ///
662    /// See [`Stack::start_collect_value`] which can temporarily set stdout as [`OutDest::Value`] for a mutable `Stack` reference.
663    pub fn collect_value(mut self) -> Self {
664        self.out_dest.pipe_stdout = Some(OutDest::Value);
665        self.out_dest.pipe_stderr = None;
666        self
667    }
668
669    /// Clears any pipe and file redirections and resets stdout and stderr to [`OutDest::Inherit`].
670    ///
671    /// This will irreversibly reset the output redirections, and so it only makes sense to use this on an owned `Stack`
672    /// (which is why this function does not take `&mut self`).
673    pub fn reset_out_dest(mut self) -> Self {
674        self.out_dest = StackOutDest::new();
675        self
676    }
677
678    /// Clears any pipe redirections, keeping the current stdout and stderr.
679    ///
680    /// This will irreversibly reset some of the output redirections, and so it only makes sense to use this on an owned `Stack`
681    /// (which is why this function does not take `&mut self`).
682    pub fn reset_pipes(mut self) -> Self {
683        self.out_dest.pipe_stdout = None;
684        self.out_dest.pipe_stderr = None;
685        self
686    }
687
688    /// Replaces the default stdout of the stack with a given file.
689    ///
690    /// This method configures the default stdout to redirect to a specified file.
691    /// It is primarily useful for applications using `nu` as a language, where the stdout of
692    /// external commands that are not explicitly piped can be redirected to a file.
693    ///
694    /// # Using Pipes
695    ///
696    /// For use in third-party applications pipes might be very useful as they allow using the
697    /// stdout of external commands for different uses.
698    /// For example the [`os_pipe`](https://docs.rs/os_pipe) crate provides a elegant way to to
699    /// access the stdout.
700    ///
701    /// ```
702    /// # use std::{fs::File, io::{self, Read}, thread, error};
703    /// # use nu_protocol::engine::Stack;
704    /// #
705    /// let (mut reader, writer) = os_pipe::pipe().unwrap();
706    /// // Use a thread to avoid blocking the execution of the called command.
707    /// let reader = thread::spawn(move || {
708    ///     let mut buf: Vec<u8> = Vec::new();
709    ///     reader.read_to_end(&mut buf)?;
710    ///     Ok::<_, io::Error>(buf)
711    /// });
712    ///
713    /// #[cfg(windows)]
714    /// let file = std::os::windows::io::OwnedHandle::from(writer).into();
715    /// #[cfg(unix)]
716    /// let file = std::os::unix::io::OwnedFd::from(writer).into();
717    ///
718    /// let stack = Stack::new().stdout_file(file);
719    ///
720    /// // Execute some nu code.
721    ///
722    /// drop(stack); // drop the stack so that the writer will be dropped too
723    /// let buf = reader.join().unwrap().unwrap();
724    /// // Do with your buffer whatever you want.
725    /// ```
726    pub fn stdout_file(mut self, file: File) -> Self {
727        self.out_dest.stdout = OutDest::File(Arc::new(file));
728        self
729    }
730
731    /// Replaces the default stderr of the stack with a given file.
732    ///
733    /// For more info, see [`stdout_file`](Self::stdout_file).
734    pub fn stderr_file(mut self, file: File) -> Self {
735        self.out_dest.stderr = OutDest::File(Arc::new(file));
736        self
737    }
738
739    /// Set the PWD environment variable to `path`.
740    ///
741    /// This method accepts `path` with trailing slashes, but they're removed
742    /// before writing the value into PWD.
743    pub fn set_cwd(&mut self, path: impl AsRef<std::path::Path>) -> Result<(), ShellError> {
744        // Helper function to create a simple generic error.
745        // Its messages are not especially helpful, but these errors don't occur often, so it's probably fine.
746        fn error(msg: &str) -> Result<(), ShellError> {
747            Err(ShellError::GenericError {
748                error: msg.into(),
749                msg: "".into(),
750                span: None,
751                help: None,
752                inner: vec![],
753            })
754        }
755
756        let path = path.as_ref();
757
758        if !path.is_absolute() {
759            if matches!(path.components().next(), Some(Component::Prefix(_))) {
760                return Err(ShellError::GenericError {
761                    error: "Cannot set $env.PWD to a prefix-only path".to_string(),
762                    msg: "".into(),
763                    span: None,
764                    help: Some(format!(
765                        "Try to use {}{MAIN_SEPARATOR} instead",
766                        path.display()
767                    )),
768                    inner: vec![],
769                });
770            }
771
772            error("Cannot set $env.PWD to a non-absolute path")
773        } else if !path.exists() {
774            error("Cannot set $env.PWD to a non-existent directory")
775        } else if !path.is_dir() {
776            error("Cannot set $env.PWD to a non-directory")
777        } else {
778            // Strip trailing slashes, if any.
779            let path = nu_path::strip_trailing_slash(path);
780            let value = Value::string(path.to_string_lossy(), Span::unknown());
781            self.add_env_var("PWD".into(), value);
782            Ok(())
783        }
784    }
785}
786
787#[cfg(test)]
788mod test {
789    use std::sync::Arc;
790
791    use crate::{Span, Value, VarId, engine::EngineState};
792
793    use super::Stack;
794
795    #[test]
796    fn test_children_see_inner_values() {
797        let mut original = Stack::new();
798        original.add_var(VarId::new(0), Value::test_string("hello"));
799
800        let cloned = Stack::with_parent(Arc::new(original));
801        assert_eq!(
802            cloned.get_var(VarId::new(0), Span::test_data()),
803            Ok(Value::test_string("hello"))
804        );
805    }
806
807    #[test]
808    fn test_children_dont_see_deleted_values() {
809        let mut original = Stack::new();
810        original.add_var(VarId::new(0), Value::test_string("hello"));
811
812        let mut cloned = Stack::with_parent(Arc::new(original));
813        cloned.remove_var(VarId::new(0));
814
815        assert_eq!(
816            cloned.get_var(VarId::new(0), Span::test_data()),
817            Err(crate::ShellError::VariableNotFoundAtRuntime {
818                span: Span::test_data()
819            })
820        );
821    }
822
823    #[test]
824    fn test_children_changes_override_parent() {
825        let mut original = Stack::new();
826        original.add_var(VarId::new(0), Value::test_string("hello"));
827
828        let mut cloned = Stack::with_parent(Arc::new(original));
829        cloned.add_var(VarId::new(0), Value::test_string("there"));
830        assert_eq!(
831            cloned.get_var(VarId::new(0), Span::test_data()),
832            Ok(Value::test_string("there"))
833        );
834
835        cloned.remove_var(VarId::new(0));
836        // the underlying value shouldn't magically re-appear
837        assert_eq!(
838            cloned.get_var(VarId::new(0), Span::test_data()),
839            Err(crate::ShellError::VariableNotFoundAtRuntime {
840                span: Span::test_data()
841            })
842        );
843    }
844    #[test]
845    fn test_children_changes_persist_in_offspring() {
846        let mut original = Stack::new();
847        original.add_var(VarId::new(0), Value::test_string("hello"));
848
849        let mut cloned = Stack::with_parent(Arc::new(original));
850        cloned.add_var(VarId::new(1), Value::test_string("there"));
851
852        cloned.remove_var(VarId::new(0));
853        let cloned = Stack::with_parent(Arc::new(cloned));
854
855        assert_eq!(
856            cloned.get_var(VarId::new(0), Span::test_data()),
857            Err(crate::ShellError::VariableNotFoundAtRuntime {
858                span: Span::test_data()
859            })
860        );
861
862        assert_eq!(
863            cloned.get_var(VarId::new(1), Span::test_data()),
864            Ok(Value::test_string("there"))
865        );
866    }
867
868    #[test]
869    fn test_merging_children_back_to_parent() {
870        let mut original = Stack::new();
871        let engine_state = EngineState::new();
872        original.add_var(VarId::new(0), Value::test_string("hello"));
873
874        let original_arc = Arc::new(original);
875        let mut cloned = Stack::with_parent(original_arc.clone());
876        cloned.add_var(VarId::new(1), Value::test_string("there"));
877
878        cloned.remove_var(VarId::new(0));
879
880        cloned.add_env_var(
881            "ADDED_IN_CHILD".to_string(),
882            Value::test_string("New Env Var"),
883        );
884
885        let original = Stack::with_changes_from_child(original_arc, cloned);
886
887        assert_eq!(
888            original.get_var(VarId::new(0), Span::test_data()),
889            Err(crate::ShellError::VariableNotFoundAtRuntime {
890                span: Span::test_data()
891            })
892        );
893
894        assert_eq!(
895            original.get_var(VarId::new(1), Span::test_data()),
896            Ok(Value::test_string("there"))
897        );
898
899        assert_eq!(
900            original
901                .get_env_var(&engine_state, "ADDED_IN_CHILD")
902                .cloned(),
903            Some(Value::test_string("New Env Var")),
904        );
905    }
906}