1use crate::{
2 engine::{
3 ArgumentStack, EngineState, ErrorHandlerStack, Redirection, StackCallArgGuard,
4 StackCollectValueGuard, StackIoGuard, StackOutDest, DEFAULT_OVERLAY_NAME,
5 },
6 Config, IntoValue, OutDest, ShellError, Span, Value, VarId, ENV_VARIABLE_ID, NU_VARIABLE_ID,
7};
8use nu_utils::IgnoreCaseExt;
9use std::{
10 collections::{HashMap, HashSet},
11 fs::File,
12 sync::Arc,
13};
14
15pub type EnvVars = HashMap<String, HashMap<String, Value>>;
17
18#[derive(Debug, Clone)]
36pub struct Stack {
37 pub vars: Vec<(VarId, Value)>,
39 pub env_vars: Vec<Arc<EnvVars>>,
41 pub env_hidden: Arc<HashMap<String, HashSet<String>>>,
43 pub active_overlays: Vec<String>,
45 pub arguments: ArgumentStack,
47 pub error_handlers: ErrorHandlerStack,
49 pub recursion_count: u64,
50 pub parent_stack: Option<Arc<Stack>>,
51 pub parent_deletions: Vec<VarId>,
53 pub config: Option<Arc<Config>>,
55 pub(crate) out_dest: StackOutDest,
56}
57
58impl Default for Stack {
59 fn default() -> Self {
60 Self::new()
61 }
62}
63
64impl Stack {
65 pub fn new() -> Self {
73 Self {
74 vars: Vec::new(),
75 env_vars: Vec::new(),
76 env_hidden: Arc::new(HashMap::new()),
77 active_overlays: vec![DEFAULT_OVERLAY_NAME.to_string()],
78 arguments: ArgumentStack::new(),
79 error_handlers: ErrorHandlerStack::new(),
80 recursion_count: 0,
81 parent_stack: None,
82 parent_deletions: vec![],
83 config: None,
84 out_dest: StackOutDest::new(),
85 }
86 }
87
88 pub fn with_parent(parent: Arc<Stack>) -> Stack {
93 Stack {
94 env_vars: parent.env_vars.clone(),
96 env_hidden: parent.env_hidden.clone(),
97 active_overlays: parent.active_overlays.clone(),
98 arguments: ArgumentStack::new(),
99 error_handlers: ErrorHandlerStack::new(),
100 recursion_count: parent.recursion_count,
101 vars: vec![],
102 parent_deletions: vec![],
103 config: parent.config.clone(),
104 out_dest: parent.out_dest.clone(),
105 parent_stack: Some(parent),
106 }
107 }
108
109 pub fn with_changes_from_child(parent: Arc<Stack>, child: Stack) -> Stack {
116 drop(child.parent_stack);
119 let mut unique_stack = Arc::unwrap_or_clone(parent);
120
121 unique_stack
122 .vars
123 .retain(|(var, _)| !child.parent_deletions.contains(var));
124 for (var, value) in child.vars {
125 unique_stack.add_var(var, value);
126 }
127 unique_stack.env_vars = child.env_vars;
128 unique_stack.env_hidden = child.env_hidden;
129 unique_stack.active_overlays = child.active_overlays;
130 unique_stack.config = child.config;
131 unique_stack
132 }
133
134 pub fn with_env(
135 &mut self,
136 env_vars: &[Arc<EnvVars>],
137 env_hidden: &Arc<HashMap<String, HashSet<String>>>,
138 ) {
139 if self.env_vars.iter().any(|scope| !scope.is_empty()) {
141 env_vars.clone_into(&mut self.env_vars);
142 }
143
144 if !self.env_hidden.is_empty() {
145 self.env_hidden.clone_from(env_hidden);
146 }
147 }
148
149 fn lookup_var(&self, var_id: VarId) -> Option<Value> {
151 for (id, val) in &self.vars {
152 if var_id == *id {
153 return Some(val.clone());
154 }
155 }
156
157 if let Some(stack) = &self.parent_stack {
158 if !self.parent_deletions.contains(&var_id) {
159 return stack.lookup_var(var_id);
160 }
161 }
162 None
163 }
164
165 pub fn get_var(&self, var_id: VarId, span: Span) -> Result<Value, ShellError> {
169 match self.lookup_var(var_id) {
170 Some(v) => Ok(v.with_span(span)),
171 None => Err(ShellError::VariableNotFoundAtRuntime { span }),
172 }
173 }
174
175 pub fn get_var_with_origin(&self, var_id: VarId, span: Span) -> Result<Value, ShellError> {
180 match self.lookup_var(var_id) {
181 Some(v) => Ok(v),
182 None => {
183 if var_id == NU_VARIABLE_ID || var_id == ENV_VARIABLE_ID {
184 return Err(ShellError::GenericError {
185 error: "Built-in variables `$env` and `$nu` have no metadata".into(),
186 msg: "no metadata available".into(),
187 span: Some(span),
188 help: None,
189 inner: vec![],
190 });
191 }
192 Err(ShellError::VariableNotFoundAtRuntime { span })
193 }
194 }
195 }
196
197 pub fn get_config(&self, engine_state: &EngineState) -> Arc<Config> {
201 self.config
202 .clone()
203 .unwrap_or_else(|| engine_state.config.clone())
204 }
205
206 pub fn update_config(&mut self, engine_state: &EngineState) -> Result<(), ShellError> {
211 if let Some(value) = self.get_env_var(engine_state, "config") {
212 let old = self.get_config(engine_state);
213 let mut config = (*old).clone();
214 let error = config.update_from_value(&old, value);
215 self.add_env_var("config".into(), config.clone().into_value(value.span()));
217 self.config = Some(config.into());
218 match error {
219 None => Ok(()),
220 Some(err) => Err(err),
221 }
222 } else {
223 self.config = None;
224 Ok(())
225 }
226 }
227
228 pub fn add_var(&mut self, var_id: VarId, value: Value) {
229 for (id, val) in &mut self.vars {
231 if *id == var_id {
232 *val = value;
233 return;
234 }
235 }
236 self.vars.push((var_id, value));
237 }
238
239 pub fn remove_var(&mut self, var_id: VarId) {
240 for (idx, (id, _)) in self.vars.iter().enumerate() {
241 if *id == var_id {
242 self.vars.remove(idx);
243 break;
244 }
245 }
246 if self.parent_stack.is_some() {
249 self.parent_deletions.push(var_id);
250 }
251 }
252
253 pub fn add_env_var(&mut self, var: String, value: Value) {
254 if let Some(last_overlay) = self.active_overlays.last() {
255 if let Some(env_hidden) = Arc::make_mut(&mut self.env_hidden).get_mut(last_overlay) {
256 env_hidden.remove(&var);
258 }
259
260 if let Some(scope) = self.env_vars.last_mut() {
261 let scope = Arc::make_mut(scope);
262 if let Some(env_vars) = scope.get_mut(last_overlay) {
263 env_vars.insert(var, value);
264 } else {
265 scope.insert(last_overlay.into(), [(var, value)].into_iter().collect());
266 }
267 } else {
268 self.env_vars.push(Arc::new(
269 [(last_overlay.into(), [(var, value)].into_iter().collect())]
270 .into_iter()
271 .collect(),
272 ));
273 }
274 } else {
275 panic!("internal error: no active overlay");
277 }
278 }
279
280 pub fn set_last_exit_code(&mut self, code: i32, span: Span) {
281 self.add_env_var("LAST_EXIT_CODE".into(), Value::int(code.into(), span));
282 }
283
284 pub fn set_last_error(&mut self, error: &ShellError) {
285 if let Some(code) = error.external_exit_code() {
286 self.set_last_exit_code(code.item, code.span);
287 } else if let Some(code) = error.exit_code() {
288 self.set_last_exit_code(code, Span::unknown());
289 }
290 }
291
292 pub fn last_overlay_name(&self) -> Result<String, ShellError> {
293 self.active_overlays
294 .last()
295 .cloned()
296 .ok_or_else(|| ShellError::NushellFailed {
297 msg: "No active overlay".into(),
298 })
299 }
300
301 pub fn captures_to_stack(&self, captures: Vec<(VarId, Value)>) -> Stack {
302 self.captures_to_stack_preserve_out_dest(captures)
303 .collect_value()
304 }
305
306 pub fn captures_to_stack_preserve_out_dest(&self, captures: Vec<(VarId, Value)>) -> Stack {
307 let mut env_vars = self.env_vars.clone();
308 env_vars.push(Arc::new(HashMap::new()));
309
310 Stack {
311 vars: captures,
312 env_vars,
313 env_hidden: self.env_hidden.clone(),
314 active_overlays: self.active_overlays.clone(),
315 arguments: ArgumentStack::new(),
316 error_handlers: ErrorHandlerStack::new(),
317 recursion_count: self.recursion_count,
318 parent_stack: None,
319 parent_deletions: vec![],
320 config: self.config.clone(),
321 out_dest: self.out_dest.clone(),
322 }
323 }
324
325 pub fn gather_captures(&self, engine_state: &EngineState, captures: &[(VarId, Span)]) -> Stack {
326 let mut vars = Vec::with_capacity(captures.len());
327
328 let fake_span = Span::new(0, 0);
329
330 for (capture, _) in captures {
331 if let Ok(value) = self.get_var(*capture, fake_span) {
334 vars.push((*capture, value));
335 } else if let Some(const_val) = &engine_state.get_var(*capture).const_val {
336 vars.push((*capture, const_val.clone()));
337 }
338 }
339
340 let mut env_vars = self.env_vars.clone();
341 env_vars.push(Arc::new(HashMap::new()));
342
343 Stack {
344 vars,
345 env_vars,
346 env_hidden: self.env_hidden.clone(),
347 active_overlays: self.active_overlays.clone(),
348 arguments: ArgumentStack::new(),
349 error_handlers: ErrorHandlerStack::new(),
350 recursion_count: self.recursion_count,
351 parent_stack: None,
352 parent_deletions: vec![],
353 config: self.config.clone(),
354 out_dest: self.out_dest.clone(),
355 }
356 }
357
358 pub fn get_env_vars(&self, engine_state: &EngineState) -> HashMap<String, Value> {
360 let mut result = HashMap::new();
361
362 for active_overlay in self.active_overlays.iter() {
363 if let Some(env_vars) = engine_state.env_vars.get(active_overlay) {
364 result.extend(
365 env_vars
366 .iter()
367 .filter(|(k, _)| {
368 if let Some(env_hidden) = self.env_hidden.get(active_overlay) {
369 !env_hidden.contains(*k)
370 } else {
371 true
373 }
374 })
375 .map(|(k, v)| (k.clone(), v.clone()))
376 .collect::<HashMap<String, Value>>(),
377 );
378 }
379 }
380
381 result.extend(self.get_stack_env_vars());
382
383 result
384 }
385
386 pub fn get_stack_env_vars(&self) -> HashMap<String, Value> {
388 let mut result = HashMap::new();
389
390 for scope in &self.env_vars {
391 for active_overlay in self.active_overlays.iter() {
392 if let Some(env_vars) = scope.get(active_overlay) {
393 result.extend(env_vars.clone());
394 }
395 }
396 }
397
398 result
399 }
400
401 pub fn get_stack_overlay_env_vars(&self, overlay_name: &str) -> HashMap<String, Value> {
403 let mut result = HashMap::new();
404
405 for scope in &self.env_vars {
406 if let Some(active_overlay) = self.active_overlays.iter().find(|n| n == &overlay_name) {
407 if let Some(env_vars) = scope.get(active_overlay) {
408 result.extend(env_vars.clone());
409 }
410 }
411 }
412
413 result
414 }
415
416 pub fn get_env_var_names(&self, engine_state: &EngineState) -> HashSet<String> {
418 let mut result = HashSet::new();
419
420 for active_overlay in self.active_overlays.iter() {
421 if let Some(env_vars) = engine_state.env_vars.get(active_overlay) {
422 result.extend(
423 env_vars
424 .keys()
425 .filter(|k| {
426 if let Some(env_hidden) = self.env_hidden.get(active_overlay) {
427 !env_hidden.contains(*k)
428 } else {
429 true
431 }
432 })
433 .cloned()
434 .collect::<HashSet<String>>(),
435 );
436 }
437 }
438
439 for scope in &self.env_vars {
440 for active_overlay in self.active_overlays.iter() {
441 if let Some(env_vars) = scope.get(active_overlay) {
442 result.extend(env_vars.keys().cloned().collect::<HashSet<String>>());
443 }
444 }
445 }
446
447 result
448 }
449
450 pub fn get_env_var<'a>(
451 &'a self,
452 engine_state: &'a EngineState,
453 name: &str,
454 ) -> Option<&'a Value> {
455 for scope in self.env_vars.iter().rev() {
456 for active_overlay in self.active_overlays.iter().rev() {
457 if let Some(env_vars) = scope.get(active_overlay) {
458 if let Some(v) = env_vars.get(name) {
459 return Some(v);
460 }
461 }
462 }
463 }
464
465 for active_overlay in self.active_overlays.iter().rev() {
466 let is_hidden = if let Some(env_hidden) = self.env_hidden.get(active_overlay) {
467 env_hidden.contains(name)
468 } else {
469 false
470 };
471
472 if !is_hidden {
473 if let Some(env_vars) = engine_state.env_vars.get(active_overlay) {
474 if let Some(v) = env_vars.get(name) {
475 return Some(v);
476 }
477 }
478 }
479 }
480 None
481 }
482
483 pub fn get_env_var_insensitive<'a>(
489 &'a self,
490 engine_state: &'a EngineState,
491 name: &str,
492 ) -> Option<(&'a String, &'a Value)> {
493 for scope in self.env_vars.iter().rev() {
494 for active_overlay in self.active_overlays.iter().rev() {
495 if let Some(env_vars) = scope.get(active_overlay) {
496 if let Some(v) = env_vars.iter().find(|(k, _)| k.eq_ignore_case(name)) {
497 return Some((v.0, v.1));
498 }
499 }
500 }
501 }
502
503 for active_overlay in self.active_overlays.iter().rev() {
504 let is_hidden = if let Some(env_hidden) = self.env_hidden.get(active_overlay) {
505 env_hidden.iter().any(|k| k.eq_ignore_case(name))
506 } else {
507 false
508 };
509
510 if !is_hidden {
511 if let Some(env_vars) = engine_state.env_vars.get(active_overlay) {
512 if let Some(v) = env_vars.iter().find(|(k, _)| k.eq_ignore_case(name)) {
513 return Some((v.0, v.1));
514 }
515 }
516 }
517 }
518 None
519 }
520
521 pub fn has_env_var(&self, engine_state: &EngineState, name: &str) -> bool {
522 for scope in self.env_vars.iter().rev() {
523 for active_overlay in self.active_overlays.iter().rev() {
524 if let Some(env_vars) = scope.get(active_overlay) {
525 if env_vars.contains_key(name) {
526 return true;
527 }
528 }
529 }
530 }
531
532 for active_overlay in self.active_overlays.iter().rev() {
533 let is_hidden = if let Some(env_hidden) = self.env_hidden.get(active_overlay) {
534 env_hidden.contains(name)
535 } else {
536 false
537 };
538
539 if !is_hidden {
540 if let Some(env_vars) = engine_state.env_vars.get(active_overlay) {
541 if env_vars.contains_key(name) {
542 return true;
543 }
544 }
545 }
546 }
547
548 false
549 }
550
551 pub fn remove_env_var(&mut self, engine_state: &EngineState, name: &str) -> bool {
552 for scope in self.env_vars.iter_mut().rev() {
553 let scope = Arc::make_mut(scope);
554 for active_overlay in self.active_overlays.iter().rev() {
555 if let Some(env_vars) = scope.get_mut(active_overlay) {
556 if env_vars.remove(name).is_some() {
557 return true;
558 }
559 }
560 }
561 }
562
563 for active_overlay in self.active_overlays.iter().rev() {
564 if let Some(env_vars) = engine_state.env_vars.get(active_overlay) {
565 if env_vars.get(name).is_some() {
566 let env_hidden = Arc::make_mut(&mut self.env_hidden);
567 if let Some(env_hidden_in_overlay) = env_hidden.get_mut(active_overlay) {
568 env_hidden_in_overlay.insert(name.into());
569 } else {
570 env_hidden
571 .insert(active_overlay.into(), [name.into()].into_iter().collect());
572 }
573
574 return true;
575 }
576 }
577 }
578
579 false
580 }
581
582 pub fn has_env_overlay(&self, name: &str, engine_state: &EngineState) -> bool {
583 for scope in self.env_vars.iter().rev() {
584 if scope.contains_key(name) {
585 return true;
586 }
587 }
588
589 engine_state.env_vars.contains_key(name)
590 }
591
592 pub fn is_overlay_active(&self, name: &str) -> bool {
593 self.active_overlays.iter().any(|n| n == name)
594 }
595
596 pub fn add_overlay(&mut self, name: String) {
597 self.active_overlays.retain(|o| o != &name);
598 self.active_overlays.push(name);
599 }
600
601 pub fn remove_overlay(&mut self, name: &str) {
602 self.active_overlays.retain(|o| o != name);
603 }
604
605 pub fn stdout(&self) -> &OutDest {
611 self.out_dest.stdout()
612 }
613
614 pub fn stderr(&self) -> &OutDest {
620 self.out_dest.stderr()
621 }
622
623 pub fn pipe_stdout(&self) -> Option<&OutDest> {
625 self.out_dest.pipe_stdout.as_ref()
626 }
627
628 pub fn pipe_stderr(&self) -> Option<&OutDest> {
630 self.out_dest.pipe_stderr.as_ref()
631 }
632
633 pub fn start_collect_value(&mut self) -> StackCollectValueGuard {
637 StackCollectValueGuard::new(self)
638 }
639
640 pub fn use_call_arg_out_dest(&mut self) -> StackCallArgGuard {
644 StackCallArgGuard::new(self)
645 }
646
647 pub fn push_redirection(
649 &mut self,
650 stdout: Option<Redirection>,
651 stderr: Option<Redirection>,
652 ) -> StackIoGuard {
653 StackIoGuard::new(self, stdout, stderr)
654 }
655
656 pub fn collect_value(mut self) -> Self {
663 self.out_dest.pipe_stdout = Some(OutDest::Value);
664 self.out_dest.pipe_stderr = None;
665 self
666 }
667
668 pub fn reset_out_dest(mut self) -> Self {
673 self.out_dest = StackOutDest::new();
674 self
675 }
676
677 pub fn reset_pipes(mut self) -> Self {
682 self.out_dest.pipe_stdout = None;
683 self.out_dest.pipe_stderr = None;
684 self
685 }
686
687 pub fn stdout_file(mut self, file: File) -> Self {
726 self.out_dest.stdout = OutDest::File(Arc::new(file));
727 self
728 }
729
730 pub fn stderr_file(mut self, file: File) -> Self {
734 self.out_dest.stderr = OutDest::File(Arc::new(file));
735 self
736 }
737
738 pub fn set_cwd(&mut self, path: impl AsRef<std::path::Path>) -> Result<(), ShellError> {
743 fn error(msg: &str) -> Result<(), ShellError> {
746 Err(ShellError::GenericError {
747 error: msg.into(),
748 msg: "".into(),
749 span: None,
750 help: None,
751 inner: vec![],
752 })
753 }
754
755 let path = path.as_ref();
756
757 if !path.is_absolute() {
758 error("Cannot set $env.PWD to a non-absolute path")
759 } else if !path.exists() {
760 error("Cannot set $env.PWD to a non-existent directory")
761 } else if !path.is_dir() {
762 error("Cannot set $env.PWD to a non-directory")
763 } else {
764 let path = nu_path::strip_trailing_slash(path);
766 let value = Value::string(path.to_string_lossy(), Span::unknown());
767 self.add_env_var("PWD".into(), value);
768 Ok(())
769 }
770 }
771}
772
773#[cfg(test)]
774mod test {
775 use std::sync::Arc;
776
777 use crate::{engine::EngineState, Span, Value, VarId};
778
779 use super::Stack;
780
781 #[test]
782 fn test_children_see_inner_values() {
783 let mut original = Stack::new();
784 original.add_var(VarId::new(0), Value::test_string("hello"));
785
786 let cloned = Stack::with_parent(Arc::new(original));
787 assert_eq!(
788 cloned.get_var(VarId::new(0), Span::test_data()),
789 Ok(Value::test_string("hello"))
790 );
791 }
792
793 #[test]
794 fn test_children_dont_see_deleted_values() {
795 let mut original = Stack::new();
796 original.add_var(VarId::new(0), Value::test_string("hello"));
797
798 let mut cloned = Stack::with_parent(Arc::new(original));
799 cloned.remove_var(VarId::new(0));
800
801 assert_eq!(
802 cloned.get_var(VarId::new(0), Span::test_data()),
803 Err(crate::ShellError::VariableNotFoundAtRuntime {
804 span: Span::test_data()
805 })
806 );
807 }
808
809 #[test]
810 fn test_children_changes_override_parent() {
811 let mut original = Stack::new();
812 original.add_var(VarId::new(0), Value::test_string("hello"));
813
814 let mut cloned = Stack::with_parent(Arc::new(original));
815 cloned.add_var(VarId::new(0), Value::test_string("there"));
816 assert_eq!(
817 cloned.get_var(VarId::new(0), Span::test_data()),
818 Ok(Value::test_string("there"))
819 );
820
821 cloned.remove_var(VarId::new(0));
822 assert_eq!(
824 cloned.get_var(VarId::new(0), Span::test_data()),
825 Err(crate::ShellError::VariableNotFoundAtRuntime {
826 span: Span::test_data()
827 })
828 );
829 }
830 #[test]
831 fn test_children_changes_persist_in_offspring() {
832 let mut original = Stack::new();
833 original.add_var(VarId::new(0), Value::test_string("hello"));
834
835 let mut cloned = Stack::with_parent(Arc::new(original));
836 cloned.add_var(VarId::new(1), Value::test_string("there"));
837
838 cloned.remove_var(VarId::new(0));
839 let cloned = Stack::with_parent(Arc::new(cloned));
840
841 assert_eq!(
842 cloned.get_var(VarId::new(0), Span::test_data()),
843 Err(crate::ShellError::VariableNotFoundAtRuntime {
844 span: Span::test_data()
845 })
846 );
847
848 assert_eq!(
849 cloned.get_var(VarId::new(1), Span::test_data()),
850 Ok(Value::test_string("there"))
851 );
852 }
853
854 #[test]
855 fn test_merging_children_back_to_parent() {
856 let mut original = Stack::new();
857 let engine_state = EngineState::new();
858 original.add_var(VarId::new(0), Value::test_string("hello"));
859
860 let original_arc = Arc::new(original);
861 let mut cloned = Stack::with_parent(original_arc.clone());
862 cloned.add_var(VarId::new(1), Value::test_string("there"));
863
864 cloned.remove_var(VarId::new(0));
865
866 cloned.add_env_var(
867 "ADDED_IN_CHILD".to_string(),
868 Value::test_string("New Env Var"),
869 );
870
871 let original = Stack::with_changes_from_child(original_arc, cloned);
872
873 assert_eq!(
874 original.get_var(VarId::new(0), Span::test_data()),
875 Err(crate::ShellError::VariableNotFoundAtRuntime {
876 span: Span::test_data()
877 })
878 );
879
880 assert_eq!(
881 original.get_var(VarId::new(1), Span::test_data()),
882 Ok(Value::test_string("there"))
883 );
884
885 assert_eq!(
886 original
887 .get_env_var(&engine_state, "ADDED_IN_CHILD")
888 .cloned(),
889 Some(Value::test_string("New Env Var")),
890 );
891 }
892}