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 if !self.parent_deletions.contains(&var_id) {
161 return stack.lookup_var(var_id);
162 }
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 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_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 if let Some(v) = env_vars.get(name) {
491 return Some(v);
492 }
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 if let Some(env_vars) = engine_state.env_vars.get(active_overlay) {
506 if let Some(v) = env_vars.get(name) {
507 return Some(v);
508 }
509 }
510 }
511 }
512 None
513 }
514
515 pub fn get_env_var_insensitive<'a>(
521 &'a self,
522 engine_state: &'a EngineState,
523 name: &str,
524 ) -> Option<(&'a String, &'a Value)> {
525 for scope in self.env_vars.iter().rev() {
526 for active_overlay in self.active_overlays.iter().rev() {
527 if let Some(env_vars) = scope.get(active_overlay) {
528 if let Some(v) = env_vars.iter().find(|(k, _)| k.eq_ignore_case(name)) {
529 return Some((v.0, v.1));
530 }
531 }
532 }
533 }
534
535 for active_overlay in self.active_overlays.iter().rev() {
536 let is_hidden = if let Some(env_hidden) = self.env_hidden.get(active_overlay) {
537 env_hidden.iter().any(|k| k.eq_ignore_case(name))
538 } else {
539 false
540 };
541
542 if !is_hidden {
543 if let Some(env_vars) = engine_state.env_vars.get(active_overlay) {
544 if let Some(v) = env_vars.iter().find(|(k, _)| k.eq_ignore_case(name)) {
545 return Some((v.0, v.1));
546 }
547 }
548 }
549 }
550 None
551 }
552
553 pub fn has_env_var(&self, engine_state: &EngineState, name: &str) -> bool {
554 for scope in self.env_vars.iter().rev() {
555 for active_overlay in self.active_overlays.iter().rev() {
556 if let Some(env_vars) = scope.get(active_overlay) {
557 if env_vars.contains_key(name) {
558 return true;
559 }
560 }
561 }
562 }
563
564 for active_overlay in self.active_overlays.iter().rev() {
565 let is_hidden = if let Some(env_hidden) = self.env_hidden.get(active_overlay) {
566 env_hidden.contains(name)
567 } else {
568 false
569 };
570
571 if !is_hidden {
572 if let Some(env_vars) = engine_state.env_vars.get(active_overlay) {
573 if env_vars.contains_key(name) {
574 return true;
575 }
576 }
577 }
578 }
579
580 false
581 }
582
583 pub fn remove_env_var(&mut self, engine_state: &EngineState, name: &str) -> bool {
584 for scope in self.env_vars.iter_mut().rev() {
585 let scope = Arc::make_mut(scope);
586 for active_overlay in self.active_overlays.iter().rev() {
587 if let Some(env_vars) = scope.get_mut(active_overlay) {
588 if env_vars.remove(name).is_some() {
589 return true;
590 }
591 }
592 }
593 }
594
595 for active_overlay in self.active_overlays.iter().rev() {
596 if let Some(env_vars) = engine_state.env_vars.get(active_overlay) {
597 if env_vars.get(name).is_some() {
598 let env_hidden = Arc::make_mut(&mut self.env_hidden);
599 if let Some(env_hidden_in_overlay) = env_hidden.get_mut(active_overlay) {
600 env_hidden_in_overlay.insert(name.into());
601 } else {
602 env_hidden
603 .insert(active_overlay.into(), [name.into()].into_iter().collect());
604 }
605
606 return true;
607 }
608 }
609 }
610
611 false
612 }
613
614 pub fn has_env_overlay(&self, name: &str, engine_state: &EngineState) -> bool {
615 for scope in self.env_vars.iter().rev() {
616 if scope.contains_key(name) {
617 return true;
618 }
619 }
620
621 engine_state.env_vars.contains_key(name)
622 }
623
624 pub fn is_overlay_active(&self, name: &str) -> bool {
625 self.active_overlays.iter().any(|n| n == name)
626 }
627
628 pub fn add_overlay(&mut self, name: String) {
629 self.active_overlays.retain(|o| o != &name);
630 self.active_overlays.push(name);
631 }
632
633 pub fn remove_overlay(&mut self, name: &str) {
634 self.active_overlays.retain(|o| o != name);
635 }
636
637 pub fn stdout(&self) -> &OutDest {
643 self.out_dest.stdout()
644 }
645
646 pub fn stderr(&self) -> &OutDest {
652 self.out_dest.stderr()
653 }
654
655 pub fn pipe_stdout(&self) -> Option<&OutDest> {
657 self.out_dest.pipe_stdout.as_ref()
658 }
659
660 pub fn pipe_stderr(&self) -> Option<&OutDest> {
662 self.out_dest.pipe_stderr.as_ref()
663 }
664
665 pub fn start_collect_value(&mut self) -> StackCollectValueGuard {
669 StackCollectValueGuard::new(self)
670 }
671
672 pub fn use_call_arg_out_dest(&mut self) -> StackCallArgGuard {
676 StackCallArgGuard::new(self)
677 }
678
679 pub fn push_redirection(
681 &mut self,
682 stdout: Option<Redirection>,
683 stderr: Option<Redirection>,
684 ) -> StackIoGuard {
685 StackIoGuard::new(self, stdout, stderr)
686 }
687
688 pub fn collect_value(mut self) -> Self {
695 self.out_dest.pipe_stdout = Some(OutDest::Value);
696 self.out_dest.pipe_stderr = None;
697 self
698 }
699
700 pub fn reset_out_dest(mut self) -> Self {
705 self.out_dest = StackOutDest::new();
706 self
707 }
708
709 pub fn reset_pipes(mut self) -> Self {
714 self.out_dest.pipe_stdout = None;
715 self.out_dest.pipe_stderr = None;
716 self
717 }
718
719 pub fn stdout_file(mut self, file: File) -> Self {
758 self.out_dest.stdout = OutDest::File(Arc::new(file));
759 self
760 }
761
762 pub fn stderr_file(mut self, file: File) -> Self {
766 self.out_dest.stderr = OutDest::File(Arc::new(file));
767 self
768 }
769
770 pub fn set_cwd(&mut self, path: impl AsRef<std::path::Path>) -> Result<(), ShellError> {
775 fn error(msg: &str) -> Result<(), ShellError> {
778 Err(ShellError::GenericError {
779 error: msg.into(),
780 msg: "".into(),
781 span: None,
782 help: None,
783 inner: vec![],
784 })
785 }
786
787 let path = path.as_ref();
788
789 if !path.is_absolute() {
790 if matches!(path.components().next(), Some(Component::Prefix(_))) {
791 return Err(ShellError::GenericError {
792 error: "Cannot set $env.PWD to a prefix-only path".to_string(),
793 msg: "".into(),
794 span: None,
795 help: Some(format!(
796 "Try to use {}{MAIN_SEPARATOR} instead",
797 path.display()
798 )),
799 inner: vec![],
800 });
801 }
802
803 error("Cannot set $env.PWD to a non-absolute path")
804 } else if !path.exists() {
805 error("Cannot set $env.PWD to a non-existent directory")
806 } else if !path.is_dir() {
807 error("Cannot set $env.PWD to a non-directory")
808 } else {
809 let path = nu_path::strip_trailing_slash(path);
811 let value = Value::string(path.to_string_lossy(), Span::unknown());
812 self.add_env_var("PWD".into(), value);
813 Ok(())
814 }
815 }
816}
817
818#[cfg(test)]
819mod test {
820 use std::sync::Arc;
821
822 use crate::{Span, Value, VarId, engine::EngineState};
823
824 use super::Stack;
825
826 #[test]
827 fn test_children_see_inner_values() {
828 let mut original = Stack::new();
829 original.add_var(VarId::new(0), Value::test_string("hello"));
830
831 let cloned = Stack::with_parent(Arc::new(original));
832 assert_eq!(
833 cloned.get_var(VarId::new(0), Span::test_data()),
834 Ok(Value::test_string("hello"))
835 );
836 }
837
838 #[test]
839 fn test_children_dont_see_deleted_values() {
840 let mut original = Stack::new();
841 original.add_var(VarId::new(0), Value::test_string("hello"));
842
843 let mut cloned = Stack::with_parent(Arc::new(original));
844 cloned.remove_var(VarId::new(0));
845
846 assert_eq!(
847 cloned.get_var(VarId::new(0), Span::test_data()),
848 Err(crate::ShellError::VariableNotFoundAtRuntime {
849 span: Span::test_data()
850 })
851 );
852 }
853
854 #[test]
855 fn test_children_changes_override_parent() {
856 let mut original = Stack::new();
857 original.add_var(VarId::new(0), Value::test_string("hello"));
858
859 let mut cloned = Stack::with_parent(Arc::new(original));
860 cloned.add_var(VarId::new(0), Value::test_string("there"));
861 assert_eq!(
862 cloned.get_var(VarId::new(0), Span::test_data()),
863 Ok(Value::test_string("there"))
864 );
865
866 cloned.remove_var(VarId::new(0));
867 assert_eq!(
869 cloned.get_var(VarId::new(0), Span::test_data()),
870 Err(crate::ShellError::VariableNotFoundAtRuntime {
871 span: Span::test_data()
872 })
873 );
874 }
875 #[test]
876 fn test_children_changes_persist_in_offspring() {
877 let mut original = Stack::new();
878 original.add_var(VarId::new(0), Value::test_string("hello"));
879
880 let mut cloned = Stack::with_parent(Arc::new(original));
881 cloned.add_var(VarId::new(1), Value::test_string("there"));
882
883 cloned.remove_var(VarId::new(0));
884 let cloned = Stack::with_parent(Arc::new(cloned));
885
886 assert_eq!(
887 cloned.get_var(VarId::new(0), Span::test_data()),
888 Err(crate::ShellError::VariableNotFoundAtRuntime {
889 span: Span::test_data()
890 })
891 );
892
893 assert_eq!(
894 cloned.get_var(VarId::new(1), Span::test_data()),
895 Ok(Value::test_string("there"))
896 );
897 }
898
899 #[test]
900 fn test_merging_children_back_to_parent() {
901 let mut original = Stack::new();
902 let engine_state = EngineState::new();
903 original.add_var(VarId::new(0), Value::test_string("hello"));
904
905 let original_arc = Arc::new(original);
906 let mut cloned = Stack::with_parent(original_arc.clone());
907 cloned.add_var(VarId::new(1), Value::test_string("there"));
908
909 cloned.remove_var(VarId::new(0));
910
911 cloned.add_env_var(
912 "ADDED_IN_CHILD".to_string(),
913 Value::test_string("New Env Var"),
914 );
915
916 let original = Stack::with_changes_from_child(original_arc, cloned);
917
918 assert_eq!(
919 original.get_var(VarId::new(0), Span::test_data()),
920 Err(crate::ShellError::VariableNotFoundAtRuntime {
921 span: Span::test_data()
922 })
923 );
924
925 assert_eq!(
926 original.get_var(VarId::new(1), Span::test_data()),
927 Ok(Value::test_string("there"))
928 );
929
930 assert_eq!(
931 original
932 .get_env_var(&engine_state, "ADDED_IN_CHILD")
933 .cloned(),
934 Some(Value::test_string("New Env Var")),
935 );
936 }
937}