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
16pub type EnvVars = HashMap<String, HashMap<String, Value>>;
18
19#[derive(Debug, Clone)]
37pub struct Stack {
38 pub vars: Vec<(VarId, Value)>,
40 pub env_vars: Vec<Arc<EnvVars>>,
42 pub env_hidden: Arc<HashMap<String, HashSet<String>>>,
44 pub active_overlays: Vec<String>,
46 pub arguments: ArgumentStack,
48 pub error_handlers: ErrorHandlerStack,
50 pub recursion_count: u64,
51 pub parent_stack: Option<Arc<Stack>>,
52 pub parent_deletions: Vec<VarId>,
54 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 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 pub fn with_parent(parent: Arc<Stack>) -> Stack {
94 Stack {
95 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 pub fn with_changes_from_child(parent: Arc<Stack>, child: Stack) -> Stack {
117 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 pub fn stdout(&self) -> &OutDest {
612 self.out_dest.stdout()
613 }
614
615 pub fn stderr(&self) -> &OutDest {
621 self.out_dest.stderr()
622 }
623
624 pub fn pipe_stdout(&self) -> Option<&OutDest> {
626 self.out_dest.pipe_stdout.as_ref()
627 }
628
629 pub fn pipe_stderr(&self) -> Option<&OutDest> {
631 self.out_dest.pipe_stderr.as_ref()
632 }
633
634 pub fn start_collect_value(&mut self) -> StackCollectValueGuard {
638 StackCollectValueGuard::new(self)
639 }
640
641 pub fn use_call_arg_out_dest(&mut self) -> StackCallArgGuard {
645 StackCallArgGuard::new(self)
646 }
647
648 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 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 pub fn reset_out_dest(mut self) -> Self {
674 self.out_dest = StackOutDest::new();
675 self
676 }
677
678 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 pub fn stdout_file(mut self, file: File) -> Self {
727 self.out_dest.stdout = OutDest::File(Arc::new(file));
728 self
729 }
730
731 pub fn stderr_file(mut self, file: File) -> Self {
735 self.out_dest.stderr = OutDest::File(Arc::new(file));
736 self
737 }
738
739 pub fn set_cwd(&mut self, path: impl AsRef<std::path::Path>) -> Result<(), ShellError> {
744 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 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 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}