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 report_shell_warning,
8};
9use nu_utils::IgnoreCaseExt;
10use std::{
11 collections::{HashMap, HashSet},
12 fs::File,
13 path::{Component, MAIN_SEPARATOR},
14 sync::Arc,
15};
16
17pub type EnvVars = HashMap<String, HashMap<String, Value>>;
19
20#[derive(Debug, Clone)]
38pub struct Stack {
39 pub vars: Vec<(VarId, Value)>,
41 pub env_vars: Vec<Arc<EnvVars>>,
43 pub env_hidden: Arc<HashMap<String, HashSet<String>>>,
45 pub active_overlays: Vec<String>,
47 pub arguments: ArgumentStack,
49 pub error_handlers: ErrorHandlerStack,
51 pub recursion_count: u64,
52 pub parent_stack: Option<Arc<Stack>>,
53 pub parent_deletions: Vec<VarId>,
55 pub config: Option<Arc<Config>>,
57 pub(crate) out_dest: StackOutDest,
58}
59
60impl Default for Stack {
61 fn default() -> Self {
62 Self::new()
63 }
64}
65
66impl Stack {
67 pub fn new() -> Self {
75 Self {
76 vars: Vec::new(),
77 env_vars: Vec::new(),
78 env_hidden: Arc::new(HashMap::new()),
79 active_overlays: vec![DEFAULT_OVERLAY_NAME.to_string()],
80 arguments: ArgumentStack::new(),
81 error_handlers: ErrorHandlerStack::new(),
82 recursion_count: 0,
83 parent_stack: None,
84 parent_deletions: vec![],
85 config: None,
86 out_dest: StackOutDest::new(),
87 }
88 }
89
90 pub fn with_parent(parent: Arc<Stack>) -> Stack {
95 Stack {
96 env_vars: parent.env_vars.clone(),
98 env_hidden: parent.env_hidden.clone(),
99 active_overlays: parent.active_overlays.clone(),
100 arguments: ArgumentStack::new(),
101 error_handlers: ErrorHandlerStack::new(),
102 recursion_count: parent.recursion_count,
103 vars: vec![],
104 parent_deletions: vec![],
105 config: parent.config.clone(),
106 out_dest: parent.out_dest.clone(),
107 parent_stack: Some(parent),
108 }
109 }
110
111 pub fn with_changes_from_child(parent: Arc<Stack>, child: Stack) -> Stack {
118 drop(child.parent_stack);
121 let mut unique_stack = Arc::unwrap_or_clone(parent);
122
123 unique_stack
124 .vars
125 .retain(|(var, _)| !child.parent_deletions.contains(var));
126 for (var, value) in child.vars {
127 unique_stack.add_var(var, value);
128 }
129 unique_stack.env_vars = child.env_vars;
130 unique_stack.env_hidden = child.env_hidden;
131 unique_stack.active_overlays = child.active_overlays;
132 unique_stack.config = child.config;
133 unique_stack
134 }
135
136 pub fn with_env(
137 &mut self,
138 env_vars: &[Arc<EnvVars>],
139 env_hidden: &Arc<HashMap<String, HashSet<String>>>,
140 ) {
141 if self.env_vars.iter().any(|scope| !scope.is_empty()) {
143 env_vars.clone_into(&mut self.env_vars);
144 }
145
146 if !self.env_hidden.is_empty() {
147 self.env_hidden.clone_from(env_hidden);
148 }
149 }
150
151 fn lookup_var(&self, var_id: VarId) -> Option<Value> {
153 for (id, val) in &self.vars {
154 if var_id == *id {
155 return Some(val.clone());
156 }
157 }
158
159 if let Some(stack) = &self.parent_stack
160 && !self.parent_deletions.contains(&var_id)
161 {
162 return stack.lookup_var(var_id);
163 }
164 None
165 }
166
167 pub fn get_var(&self, var_id: VarId, span: Span) -> Result<Value, ShellError> {
171 match self.lookup_var(var_id) {
172 Some(v) => Ok(v.with_span(span)),
173 None => Err(ShellError::VariableNotFoundAtRuntime { span }),
174 }
175 }
176
177 pub fn get_var_with_origin(&self, var_id: VarId, span: Span) -> Result<Value, ShellError> {
182 match self.lookup_var(var_id) {
183 Some(v) => Ok(v),
184 None => {
185 if var_id == NU_VARIABLE_ID || var_id == ENV_VARIABLE_ID {
186 return Err(ShellError::GenericError {
187 error: "Built-in variables `$env` and `$nu` have no metadata".into(),
188 msg: "no metadata available".into(),
189 span: Some(span),
190 help: None,
191 inner: vec![],
192 });
193 }
194 Err(ShellError::VariableNotFoundAtRuntime { span })
195 }
196 }
197 }
198
199 pub fn get_config(&self, engine_state: &EngineState) -> Arc<Config> {
203 self.config
204 .clone()
205 .unwrap_or_else(|| engine_state.config.clone())
206 }
207
208 pub fn update_config(&mut self, engine_state: &EngineState) -> Result<(), ShellError> {
213 if let Some(value) = self.get_env_var(engine_state, "config") {
214 let old = self.get_config(engine_state);
215 let mut config = (*old).clone();
216 let result = config.update_from_value(&old, value);
217 self.add_env_var("config".into(), config.clone().into_value(value.span()));
219 self.config = Some(config.into());
220 if let Some(warning) = result? {
221 report_shell_warning(engine_state, &warning);
222 }
223 } else {
224 self.config = None;
225 }
226 Ok(())
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 && let Some(env_vars) = scope.get(active_overlay)
409 {
410 result.extend(env_vars.clone());
411 }
412 }
413
414 result
415 }
416
417 pub fn get_hidden_env_vars(
419 &self,
420 excluded_overlay_name: &str,
421 engine_state: &EngineState,
422 ) -> HashMap<String, Value> {
423 let mut result = HashMap::new();
424
425 for overlay_name in self.active_overlays.iter().rev() {
426 if overlay_name == excluded_overlay_name {
427 continue;
428 }
429 if let Some(env_names) = self.env_hidden.get(overlay_name) {
430 for n in env_names {
431 if result.contains_key(n) {
432 continue;
433 }
434 if let Some(Some(v)) = engine_state
436 .env_vars
437 .get(overlay_name)
438 .map(|env_vars| env_vars.get(n))
439 {
440 result.insert(n.to_string(), v.clone());
441 }
442 }
443 }
444 }
445 result
446 }
447
448 pub fn get_env_var_names(&self, engine_state: &EngineState) -> HashSet<String> {
450 let mut result = HashSet::new();
451
452 for active_overlay in self.active_overlays.iter() {
453 if let Some(env_vars) = engine_state.env_vars.get(active_overlay) {
454 result.extend(
455 env_vars
456 .keys()
457 .filter(|k| {
458 if let Some(env_hidden) = self.env_hidden.get(active_overlay) {
459 !env_hidden.contains(*k)
460 } else {
461 true
463 }
464 })
465 .cloned()
466 .collect::<HashSet<String>>(),
467 );
468 }
469 }
470
471 for scope in &self.env_vars {
472 for active_overlay in self.active_overlays.iter() {
473 if let Some(env_vars) = scope.get(active_overlay) {
474 result.extend(env_vars.keys().cloned().collect::<HashSet<String>>());
475 }
476 }
477 }
478
479 result
480 }
481
482 pub fn get_env_var<'a>(
483 &'a self,
484 engine_state: &'a EngineState,
485 name: &str,
486 ) -> Option<&'a Value> {
487 for scope in self.env_vars.iter().rev() {
488 for active_overlay in self.active_overlays.iter().rev() {
489 if let Some(env_vars) = scope.get(active_overlay)
490 && let Some(v) = env_vars.get(name)
491 {
492 return Some(v);
493 }
494 }
495 }
496
497 for active_overlay in self.active_overlays.iter().rev() {
498 let is_hidden = if let Some(env_hidden) = self.env_hidden.get(active_overlay) {
499 env_hidden.contains(name)
500 } else {
501 false
502 };
503
504 if !is_hidden
505 && let Some(env_vars) = engine_state.env_vars.get(active_overlay)
506 && let Some(v) = env_vars.get(name)
507 {
508 return Some(v);
509 }
510 }
511 None
512 }
513
514 pub fn get_env_var_insensitive<'a>(
520 &'a self,
521 engine_state: &'a EngineState,
522 name: &str,
523 ) -> Option<(&'a String, &'a Value)> {
524 for scope in self.env_vars.iter().rev() {
525 for active_overlay in self.active_overlays.iter().rev() {
526 if let Some(env_vars) = scope.get(active_overlay)
527 && let Some(v) = env_vars.iter().find(|(k, _)| k.eq_ignore_case(name))
528 {
529 return Some((v.0, v.1));
530 }
531 }
532 }
533
534 for active_overlay in self.active_overlays.iter().rev() {
535 let is_hidden = if let Some(env_hidden) = self.env_hidden.get(active_overlay) {
536 env_hidden.iter().any(|k| k.eq_ignore_case(name))
537 } else {
538 false
539 };
540
541 if !is_hidden
542 && let Some(env_vars) = engine_state.env_vars.get(active_overlay)
543 && let Some(v) = env_vars.iter().find(|(k, _)| k.eq_ignore_case(name))
544 {
545 return Some((v.0, v.1));
546 }
547 }
548 None
549 }
550
551 pub fn has_env_var(&self, engine_state: &EngineState, name: &str) -> bool {
552 for scope in self.env_vars.iter().rev() {
553 for active_overlay in self.active_overlays.iter().rev() {
554 if let Some(env_vars) = scope.get(active_overlay)
555 && env_vars.contains_key(name)
556 {
557 return true;
558 }
559 }
560 }
561
562 for active_overlay in self.active_overlays.iter().rev() {
563 let is_hidden = if let Some(env_hidden) = self.env_hidden.get(active_overlay) {
564 env_hidden.contains(name)
565 } else {
566 false
567 };
568
569 if !is_hidden
570 && let Some(env_vars) = engine_state.env_vars.get(active_overlay)
571 && env_vars.contains_key(name)
572 {
573 return true;
574 }
575 }
576
577 false
578 }
579
580 pub fn remove_env_var(&mut self, engine_state: &EngineState, name: &str) -> bool {
581 for scope in self.env_vars.iter_mut().rev() {
582 let scope = Arc::make_mut(scope);
583 for active_overlay in self.active_overlays.iter().rev() {
584 if let Some(env_vars) = scope.get_mut(active_overlay)
585 && env_vars.remove(name).is_some()
586 {
587 return true;
588 }
589 }
590 }
591
592 for active_overlay in self.active_overlays.iter().rev() {
593 if let Some(env_vars) = engine_state.env_vars.get(active_overlay)
594 && env_vars.get(name).is_some()
595 {
596 let env_hidden = Arc::make_mut(&mut self.env_hidden);
597 if let Some(env_hidden_in_overlay) = env_hidden.get_mut(active_overlay) {
598 env_hidden_in_overlay.insert(name.into());
599 } else {
600 env_hidden.insert(active_overlay.into(), [name.into()].into_iter().collect());
601 }
602
603 return true;
604 }
605 }
606
607 false
608 }
609
610 pub fn has_env_overlay(&self, name: &str, engine_state: &EngineState) -> bool {
611 for scope in self.env_vars.iter().rev() {
612 if scope.contains_key(name) {
613 return true;
614 }
615 }
616
617 engine_state.env_vars.contains_key(name)
618 }
619
620 pub fn is_overlay_active(&self, name: &str) -> bool {
621 self.active_overlays.iter().any(|n| n == name)
622 }
623
624 pub fn add_overlay(&mut self, name: String) {
625 self.active_overlays.retain(|o| o != &name);
626 self.active_overlays.push(name);
627 }
628
629 pub fn remove_overlay(&mut self, name: &str) {
630 self.active_overlays.retain(|o| o != name);
631 }
632
633 pub fn stdout(&self) -> &OutDest {
639 self.out_dest.stdout()
640 }
641
642 pub fn stderr(&self) -> &OutDest {
648 self.out_dest.stderr()
649 }
650
651 pub fn pipe_stdout(&self) -> Option<&OutDest> {
653 self.out_dest.pipe_stdout.as_ref()
654 }
655
656 pub fn pipe_stderr(&self) -> Option<&OutDest> {
658 self.out_dest.pipe_stderr.as_ref()
659 }
660
661 pub fn start_collect_value(&mut self) -> StackCollectValueGuard<'_> {
665 StackCollectValueGuard::new(self)
666 }
667
668 pub fn use_call_arg_out_dest(&mut self) -> StackCallArgGuard<'_> {
672 StackCallArgGuard::new(self)
673 }
674
675 pub fn push_redirection(
677 &mut self,
678 stdout: Option<Redirection>,
679 stderr: Option<Redirection>,
680 ) -> StackIoGuard<'_> {
681 StackIoGuard::new(self, stdout, stderr)
682 }
683
684 pub fn collect_value(mut self) -> Self {
691 self.out_dest.pipe_stdout = Some(OutDest::Value);
692 self.out_dest.pipe_stderr = None;
693 self
694 }
695
696 pub fn reset_out_dest(mut self) -> Self {
701 self.out_dest = StackOutDest::new();
702 self
703 }
704
705 pub fn reset_pipes(mut self) -> Self {
710 self.out_dest.pipe_stdout = None;
711 self.out_dest.pipe_stderr = None;
712 self
713 }
714
715 pub fn stdout_file(mut self, file: File) -> Self {
754 self.out_dest.stdout = OutDest::File(Arc::new(file));
755 self
756 }
757
758 pub fn stderr_file(mut self, file: File) -> Self {
762 self.out_dest.stderr = OutDest::File(Arc::new(file));
763 self
764 }
765
766 pub fn set_cwd(&mut self, path: impl AsRef<std::path::Path>) -> Result<(), ShellError> {
771 fn error(msg: &str) -> Result<(), ShellError> {
774 Err(ShellError::GenericError {
775 error: msg.into(),
776 msg: "".into(),
777 span: None,
778 help: None,
779 inner: vec![],
780 })
781 }
782
783 let path = path.as_ref();
784
785 if !path.is_absolute() {
786 if let Some(Component::Prefix(_)) = path.components().next() {
787 return Err(ShellError::GenericError {
788 error: "Cannot set $env.PWD to a prefix-only path".to_string(),
789 msg: "".into(),
790 span: None,
791 help: Some(format!(
792 "Try to use {}{MAIN_SEPARATOR} instead",
793 path.display()
794 )),
795 inner: vec![],
796 });
797 }
798
799 error("Cannot set $env.PWD to a non-absolute path")
800 } else if !path.exists() {
801 error("Cannot set $env.PWD to a non-existent directory")
802 } else if !path.is_dir() {
803 error("Cannot set $env.PWD to a non-directory")
804 } else {
805 let path = nu_path::strip_trailing_slash(path);
807 let value = Value::string(path.to_string_lossy(), Span::unknown());
808 self.add_env_var("PWD".into(), value);
809 Ok(())
810 }
811 }
812}
813
814#[cfg(test)]
815mod test {
816 use std::sync::Arc;
817
818 use crate::{Span, Value, VarId, engine::EngineState};
819
820 use super::Stack;
821
822 #[test]
823 fn test_children_see_inner_values() {
824 let mut original = Stack::new();
825 original.add_var(VarId::new(0), Value::test_string("hello"));
826
827 let cloned = Stack::with_parent(Arc::new(original));
828 assert_eq!(
829 cloned.get_var(VarId::new(0), Span::test_data()),
830 Ok(Value::test_string("hello"))
831 );
832 }
833
834 #[test]
835 fn test_children_dont_see_deleted_values() {
836 let mut original = Stack::new();
837 original.add_var(VarId::new(0), Value::test_string("hello"));
838
839 let mut cloned = Stack::with_parent(Arc::new(original));
840 cloned.remove_var(VarId::new(0));
841
842 assert_eq!(
843 cloned.get_var(VarId::new(0), Span::test_data()),
844 Err(crate::ShellError::VariableNotFoundAtRuntime {
845 span: Span::test_data()
846 })
847 );
848 }
849
850 #[test]
851 fn test_children_changes_override_parent() {
852 let mut original = Stack::new();
853 original.add_var(VarId::new(0), Value::test_string("hello"));
854
855 let mut cloned = Stack::with_parent(Arc::new(original));
856 cloned.add_var(VarId::new(0), Value::test_string("there"));
857 assert_eq!(
858 cloned.get_var(VarId::new(0), Span::test_data()),
859 Ok(Value::test_string("there"))
860 );
861
862 cloned.remove_var(VarId::new(0));
863 assert_eq!(
865 cloned.get_var(VarId::new(0), Span::test_data()),
866 Err(crate::ShellError::VariableNotFoundAtRuntime {
867 span: Span::test_data()
868 })
869 );
870 }
871 #[test]
872 fn test_children_changes_persist_in_offspring() {
873 let mut original = Stack::new();
874 original.add_var(VarId::new(0), Value::test_string("hello"));
875
876 let mut cloned = Stack::with_parent(Arc::new(original));
877 cloned.add_var(VarId::new(1), Value::test_string("there"));
878
879 cloned.remove_var(VarId::new(0));
880 let cloned = Stack::with_parent(Arc::new(cloned));
881
882 assert_eq!(
883 cloned.get_var(VarId::new(0), Span::test_data()),
884 Err(crate::ShellError::VariableNotFoundAtRuntime {
885 span: Span::test_data()
886 })
887 );
888
889 assert_eq!(
890 cloned.get_var(VarId::new(1), Span::test_data()),
891 Ok(Value::test_string("there"))
892 );
893 }
894
895 #[test]
896 fn test_merging_children_back_to_parent() {
897 let mut original = Stack::new();
898 let engine_state = EngineState::new();
899 original.add_var(VarId::new(0), Value::test_string("hello"));
900
901 let original_arc = Arc::new(original);
902 let mut cloned = Stack::with_parent(original_arc.clone());
903 cloned.add_var(VarId::new(1), Value::test_string("there"));
904
905 cloned.remove_var(VarId::new(0));
906
907 cloned.add_env_var(
908 "ADDED_IN_CHILD".to_string(),
909 Value::test_string("New Env Var"),
910 );
911
912 let original = Stack::with_changes_from_child(original_arc, cloned);
913
914 assert_eq!(
915 original.get_var(VarId::new(0), Span::test_data()),
916 Err(crate::ShellError::VariableNotFoundAtRuntime {
917 span: Span::test_data()
918 })
919 );
920
921 assert_eq!(
922 original.get_var(VarId::new(1), Span::test_data()),
923 Ok(Value::test_string("there"))
924 );
925
926 assert_eq!(
927 original
928 .get_env_var(&engine_state, "ADDED_IN_CHILD")
929 .cloned(),
930 Some(Value::test_string("New Env Var")),
931 );
932 }
933}