1use crate::{
2 Config, ENV_VARIABLE_ID, IntoValue, NU_VARIABLE_ID, OutDest, ShellError, Span, Value, VarId,
3 engine::{
4 ArgumentStack, DEFAULT_OVERLAY_NAME, EngineState, EnvName, ErrorHandlerStack, Redirection,
5 StackCallArgGuard, StackCollectValueGuard, StackIoGuard, StackOutDest,
6 },
7 report_shell_warning,
8 shell_error::generic::GenericError,
9};
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<EnvName, 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<EnvName>>>,
45 pub active_overlays: Vec<String>,
47 pub arguments: ArgumentStack,
49 pub error_handlers: ErrorHandlerStack,
51 pub finally_run_handlers: ErrorHandlerStack,
53 pub recursion_count: u64,
54 pub parent_stack: Option<Arc<Stack>>,
55 pub parent_deletions: Vec<VarId>,
57 pub deletions: Vec<VarId>,
59 pub config: Option<Arc<Config>>,
61 pub(crate) out_dest: StackOutDest,
62}
63
64impl Default for Stack {
65 fn default() -> Self {
66 Self::new()
67 }
68}
69
70impl Stack {
71 pub fn new() -> Self {
79 Self {
80 vars: Vec::new(),
81 env_vars: Vec::new(),
82 env_hidden: Arc::new(HashMap::new()),
83 active_overlays: vec![DEFAULT_OVERLAY_NAME.to_string()],
84 arguments: ArgumentStack::new(),
85 error_handlers: ErrorHandlerStack::new(),
86 finally_run_handlers: ErrorHandlerStack::new(),
87 recursion_count: 0,
88 parent_stack: None,
89 parent_deletions: vec![],
90 deletions: vec![],
91 config: None,
92 out_dest: StackOutDest::new(),
93 }
94 }
95
96 pub fn with_parent(parent: Arc<Stack>) -> Stack {
101 Stack {
102 env_vars: parent.env_vars.clone(),
104 env_hidden: parent.env_hidden.clone(),
105 active_overlays: parent.active_overlays.clone(),
106 arguments: ArgumentStack::new(),
107 error_handlers: ErrorHandlerStack::new(),
108 finally_run_handlers: ErrorHandlerStack::new(),
109 recursion_count: parent.recursion_count,
110 vars: vec![],
111 parent_deletions: vec![],
112 deletions: vec![],
113 config: parent.config.clone(),
114 out_dest: parent.out_dest.clone(),
115 parent_stack: Some(parent),
116 }
117 }
118
119 pub fn with_changes_from_child(parent: Arc<Stack>, child: Stack) -> Stack {
126 drop(child.parent_stack);
129 let mut unique_stack = Arc::unwrap_or_clone(parent);
130
131 unique_stack
132 .vars
133 .retain(|(var, _)| !child.parent_deletions.contains(var));
134 for (var, value) in child.vars {
135 unique_stack.add_var(var, value);
136 }
137 unique_stack.env_vars = child.env_vars;
138 unique_stack.env_hidden = child.env_hidden;
139 unique_stack.active_overlays = child.active_overlays;
140 unique_stack.config = child.config;
141 unique_stack
142 }
143
144 pub fn with_env(
145 &mut self,
146 env_vars: &[Arc<EnvVars>],
147 env_hidden: &Arc<HashMap<String, HashSet<EnvName>>>,
148 ) {
149 if self.env_vars.iter().any(|scope| !scope.is_empty()) {
151 env_vars.clone_into(&mut self.env_vars);
152 }
153
154 if !self.env_hidden.is_empty() {
155 self.env_hidden.clone_from(env_hidden);
156 }
157 }
158
159 fn lookup_var(&self, var_id: VarId) -> Option<Value> {
161 for (id, val) in &self.vars {
162 if var_id == *id {
163 return Some(val.clone());
164 }
165 }
166
167 if let Some(stack) = &self.parent_stack
168 && !self.parent_deletions.contains(&var_id)
169 {
170 return stack.lookup_var(var_id);
171 }
172 None
173 }
174
175 pub fn get_var(&self, var_id: VarId, span: Span) -> Result<Value, ShellError> {
179 match self.lookup_var(var_id) {
180 Some(v) => Ok(v.with_span(span)),
181 None => Err(ShellError::VariableNotFoundAtRuntime { span }),
182 }
183 }
184
185 pub fn get_var_with_origin(&self, var_id: VarId, span: Span) -> Result<Value, ShellError> {
190 match self.lookup_var(var_id) {
191 Some(v) => Ok(v),
192 None => {
193 if var_id == NU_VARIABLE_ID || var_id == ENV_VARIABLE_ID {
194 return Err(ShellError::Generic(GenericError::new(
195 "Built-in variables `$env` and `$nu` have no metadata",
196 "no metadata available",
197 span,
198 )));
199 }
200 Err(ShellError::VariableNotFoundAtRuntime { span })
201 }
202 }
203 }
204
205 pub fn get_config(&self, engine_state: &EngineState) -> Arc<Config> {
209 self.config
210 .clone()
211 .unwrap_or_else(|| engine_state.config.clone())
212 }
213
214 pub fn update_config(&mut self, engine_state: &EngineState) -> Result<(), ShellError> {
219 if let Some(value) = self.get_env_var(engine_state, "config") {
220 let old = self.get_config(engine_state);
221 let mut config = (*old).clone();
222 let result = config.update_from_value(&old, value);
223 self.add_env_var("config".into(), config.clone().into_value(value.span()));
225 self.config = Some(config.into());
226 if let Some(warning) = result? {
227 report_shell_warning(Some(self), engine_state, &warning);
228 }
229 } else {
230 self.config = None;
231 }
232 Ok(())
233 }
234
235 pub fn add_var(&mut self, var_id: VarId, value: Value) {
236 for (id, val) in &mut self.vars {
238 if *id == var_id {
239 *val = value;
240 return;
241 }
242 }
243 self.vars.push((var_id, value));
244 }
245
246 pub fn remove_var(&mut self, var_id: VarId) {
247 for (idx, (id, _)) in self.vars.iter().enumerate() {
248 if *id == var_id {
249 self.vars.remove(idx);
250 break;
251 }
252 }
253 if self.parent_stack.is_some() {
256 self.parent_deletions.push(var_id);
257 }
258 self.deletions.push(var_id);
259 }
260
261 pub fn add_env_var(&mut self, var: String, value: Value) {
262 if let Some(last_overlay) = self.active_overlays.last() {
263 let env_name = EnvName::from(var);
264 if let Some(env_hidden) = Arc::make_mut(&mut self.env_hidden).get_mut(last_overlay) {
265 env_hidden.remove(&env_name);
267 }
268
269 if let Some(scope) = self.env_vars.last_mut() {
270 let scope = Arc::make_mut(scope);
271 if let Some(env_vars) = scope.get_mut(last_overlay) {
272 env_vars.insert(env_name, value);
273 } else {
274 scope.insert(
275 last_overlay.into(),
276 [(env_name, value)].into_iter().collect(),
277 );
278 }
279 } else {
280 self.env_vars.push(Arc::new(
281 [(
282 last_overlay.into(),
283 [(env_name, value)].into_iter().collect(),
284 )]
285 .into_iter()
286 .collect(),
287 ));
288 }
289 } else {
290 panic!("internal error: no active overlay");
292 }
293 }
294
295 pub fn set_last_exit_code(&mut self, code: i32, span: Span) {
296 self.add_env_var("LAST_EXIT_CODE".into(), Value::int(code.into(), span));
297 }
298
299 pub fn set_last_error(&mut self, error: &ShellError) {
300 if let Some(code) = error.external_exit_code() {
301 self.set_last_exit_code(code.item, code.span);
302 } else if let Some(code) = error.exit_code() {
303 self.set_last_exit_code(code, Span::unknown());
304 }
305 }
306
307 pub fn last_overlay_name(&self) -> Result<String, ShellError> {
308 self.active_overlays
309 .last()
310 .cloned()
311 .ok_or_else(|| ShellError::NushellFailed {
312 msg: "No active overlay".into(),
313 })
314 }
315
316 pub fn captures_to_stack(&self, captures: Vec<(VarId, Value)>) -> Stack {
317 self.captures_to_stack_preserve_out_dest(captures)
318 .collect_value()
319 }
320
321 pub fn captures_to_stack_preserve_out_dest(&self, captures: Vec<(VarId, Value)>) -> Stack {
322 let mut env_vars = self.env_vars.clone();
323 env_vars.push(Arc::new(HashMap::new()));
324
325 Stack {
326 vars: captures,
327 env_vars,
328 env_hidden: self.env_hidden.clone(),
329 active_overlays: self.active_overlays.clone(),
330 arguments: ArgumentStack::new(),
331 error_handlers: ErrorHandlerStack::new(),
332 finally_run_handlers: ErrorHandlerStack::new(),
333 recursion_count: self.recursion_count,
334 parent_stack: None,
335 parent_deletions: vec![],
336 deletions: vec![],
337 config: self.config.clone(),
338 out_dest: self.out_dest.clone(),
339 }
340 }
341
342 pub fn gather_captures(&self, engine_state: &EngineState, captures: &[(VarId, Span)]) -> Stack {
343 let mut vars = Vec::with_capacity(captures.len());
344
345 let fake_span = Span::new(0, 0);
346
347 for (capture, _) in captures {
348 if let Ok(value) = self.get_var(*capture, fake_span) {
351 vars.push((*capture, value));
352 } else if let Some(const_val) = &engine_state.get_var(*capture).const_val {
353 vars.push((*capture, const_val.clone()));
354 }
355 }
356
357 let mut env_vars = self.env_vars.clone();
358 env_vars.push(Arc::new(HashMap::new()));
359
360 Stack {
361 vars,
362 env_vars,
363 env_hidden: self.env_hidden.clone(),
364 active_overlays: self.active_overlays.clone(),
365 arguments: ArgumentStack::new(),
366 error_handlers: ErrorHandlerStack::new(),
367 finally_run_handlers: ErrorHandlerStack::new(),
368 recursion_count: self.recursion_count,
369 parent_stack: None,
370 parent_deletions: vec![],
371 deletions: vec![],
372 config: self.config.clone(),
373 out_dest: self.out_dest.clone(),
374 }
375 }
376
377 pub fn get_env_vars(&self, engine_state: &EngineState) -> HashMap<String, Value> {
379 let mut result = HashMap::new();
380
381 for active_overlay in self.active_overlays.iter() {
382 if let Some(env_vars) = engine_state.env_vars.get(active_overlay) {
383 result.extend(
384 env_vars
385 .iter()
386 .filter(|(k, _)| {
387 if let Some(env_hidden) = self.env_hidden.get(active_overlay) {
388 !env_hidden.contains(*k)
389 } else {
390 true
392 }
393 })
394 .map(|(k, v)| (k.as_str().to_string(), v.clone()))
395 .collect::<HashMap<String, Value>>(),
396 );
397 }
398 }
399
400 result.extend(self.get_stack_env_vars());
401
402 result
403 }
404
405 pub fn get_stack_env_vars(&self) -> HashMap<String, Value> {
407 let mut result = HashMap::new();
408
409 for scope in &self.env_vars {
410 for active_overlay in self.active_overlays.iter() {
411 if let Some(env_vars) = scope.get(active_overlay) {
412 result.extend(
413 env_vars
414 .iter()
415 .map(|(k, v)| (k.as_str().to_string(), v.clone())),
416 );
417 }
418 }
419 }
420
421 result
422 }
423
424 pub fn get_stack_overlay_env_vars(&self, overlay_name: &str) -> HashMap<String, Value> {
426 let mut result = HashMap::new();
427
428 for scope in &self.env_vars {
429 if let Some(active_overlay) = self.active_overlays.iter().find(|n| n == &overlay_name)
430 && let Some(env_vars) = scope.get(active_overlay)
431 {
432 result.extend(
433 env_vars
434 .iter()
435 .map(|(k, v)| (k.as_str().to_string(), v.clone())),
436 );
437 }
438 }
439
440 result
441 }
442
443 pub fn get_hidden_env_vars(
445 &self,
446 excluded_overlay_name: &str,
447 engine_state: &EngineState,
448 ) -> HashMap<String, Value> {
449 let mut result = HashMap::new();
450
451 for overlay_name in self.active_overlays.iter().rev() {
452 if overlay_name == excluded_overlay_name {
453 continue;
454 }
455 if let Some(env_names) = self.env_hidden.get(overlay_name) {
456 for n in env_names {
457 if result.contains_key(n.as_str()) {
458 continue;
459 }
460 if let Some(Some(v)) = engine_state
462 .env_vars
463 .get(overlay_name)
464 .map(|env_vars| env_vars.get(n))
465 {
466 result.insert(n.as_str().to_string(), v.clone());
467 }
468 }
469 }
470 }
471 result
472 }
473
474 pub fn get_env_var_names(&self, engine_state: &EngineState) -> HashSet<String> {
476 let mut result = HashSet::new();
477
478 for active_overlay in self.active_overlays.iter() {
479 if let Some(env_vars) = engine_state.env_vars.get(active_overlay) {
480 result.extend(
481 env_vars
482 .keys()
483 .filter(|k| {
484 if let Some(env_hidden) = self.env_hidden.get(active_overlay) {
485 !env_hidden.contains(*k)
486 } else {
487 true
489 }
490 })
491 .map(|k| k.as_str().to_string())
492 .collect::<HashSet<String>>(),
493 );
494 }
495 }
496
497 for scope in &self.env_vars {
498 for active_overlay in self.active_overlays.iter() {
499 if let Some(env_vars) = scope.get(active_overlay) {
500 result.extend(
501 env_vars
502 .keys()
503 .map(|k| k.as_str().to_string())
504 .collect::<HashSet<String>>(),
505 );
506 }
507 }
508 }
509
510 result
511 }
512
513 pub fn get_env_var<'a>(
514 &'a self,
515 engine_state: &'a EngineState,
516 name: &str,
517 ) -> Option<&'a Value> {
518 for scope in self.env_vars.iter().rev() {
519 for active_overlay in self.active_overlays.iter().rev() {
520 if let Some(env_vars) = scope.get(active_overlay)
521 && let Some(v) = env_vars.get(&EnvName::from(name))
522 {
523 return Some(v);
524 }
525 }
526 }
527
528 for active_overlay in self.active_overlays.iter().rev() {
529 let is_hidden = if let Some(env_hidden) = self.env_hidden.get(active_overlay) {
530 env_hidden.contains(&EnvName::from(name))
531 } else {
532 false
533 };
534
535 if !is_hidden
536 && let Some(env_vars) = engine_state.env_vars.get(active_overlay)
537 && let Some(v) = env_vars.get(&EnvName::from(name))
538 {
539 return Some(v);
540 }
541 }
542 None
543 }
544
545 pub fn has_env_var(&self, engine_state: &EngineState, name: &str) -> bool {
546 for scope in self.env_vars.iter().rev() {
547 for active_overlay in self.active_overlays.iter().rev() {
548 if let Some(env_vars) = scope.get(active_overlay)
549 && env_vars.contains_key(&EnvName::from(name))
550 {
551 return true;
552 }
553 }
554 }
555
556 for active_overlay in self.active_overlays.iter().rev() {
557 let is_hidden = if let Some(env_hidden) = self.env_hidden.get(active_overlay) {
558 env_hidden.contains(&EnvName::from(name))
559 } else {
560 false
561 };
562
563 if !is_hidden
564 && let Some(env_vars) = engine_state.env_vars.get(active_overlay)
565 && env_vars.contains_key(&EnvName::from(name))
566 {
567 return true;
568 }
569 }
570
571 false
572 }
573
574 pub fn remove_env_var(&mut self, engine_state: &EngineState, name: &str) -> bool {
575 for scope in self.env_vars.iter_mut().rev() {
576 let scope = Arc::make_mut(scope);
577 for active_overlay in self.active_overlays.iter().rev() {
578 if let Some(env_vars) = scope.get_mut(active_overlay)
579 && env_vars.remove(&EnvName::from(name)).is_some()
580 {
581 return true;
582 }
583 }
584 }
585
586 for active_overlay in self.active_overlays.iter().rev() {
587 if let Some(env_vars) = engine_state.env_vars.get(active_overlay)
588 && env_vars.get(&EnvName::from(name)).is_some()
589 {
590 let env_hidden = Arc::make_mut(&mut self.env_hidden);
591 if let Some(env_hidden_in_overlay) = env_hidden.get_mut(active_overlay) {
592 env_hidden_in_overlay.insert(EnvName::from(name));
593 } else {
594 env_hidden.insert(
595 active_overlay.into(),
596 [EnvName::from(name)].into_iter().collect(),
597 );
598 }
599
600 return true;
601 }
602 }
603
604 false
605 }
606
607 pub fn has_env_overlay(&self, name: &str, engine_state: &EngineState) -> bool {
608 for scope in self.env_vars.iter().rev() {
609 if scope.contains_key(name) {
610 return true;
611 }
612 }
613
614 engine_state.env_vars.contains_key(name)
615 }
616
617 pub fn is_overlay_active(&self, name: &str) -> bool {
618 self.active_overlays.iter().any(|n| n == name)
619 }
620
621 pub fn add_overlay(&mut self, name: String) {
622 self.active_overlays.retain(|o| o != &name);
623 self.active_overlays.push(name);
624 }
625
626 pub fn remove_overlay(&mut self, name: &str) {
627 self.active_overlays.retain(|o| o != name);
628 }
629
630 pub fn stdout(&self) -> &OutDest {
636 self.out_dest.stdout()
637 }
638
639 pub fn stderr(&self) -> &OutDest {
645 self.out_dest.stderr()
646 }
647
648 pub fn pipe_stdout(&self) -> Option<&OutDest> {
650 self.out_dest.pipe_stdout.as_ref()
651 }
652
653 pub fn pipe_stderr(&self) -> Option<&OutDest> {
655 self.out_dest.pipe_stderr.as_ref()
656 }
657
658 pub fn start_collect_value(&mut self) -> StackCollectValueGuard<'_> {
662 StackCollectValueGuard::new(self)
663 }
664
665 pub fn use_call_arg_out_dest(&mut self) -> StackCallArgGuard<'_> {
669 StackCallArgGuard::new(self)
670 }
671
672 pub fn push_redirection(
674 &mut self,
675 stdout: Option<Redirection>,
676 stderr: Option<Redirection>,
677 ) -> StackIoGuard<'_> {
678 StackIoGuard::new(self, stdout, stderr)
679 }
680
681 pub fn collect_value(mut self) -> Self {
688 self.out_dest.pipe_stdout = Some(OutDest::Value);
689 self.out_dest.pipe_stderr = None;
690 self
691 }
692
693 pub fn capture_all(mut self) -> Self {
702 self.out_dest.pipe_stdout = Some(OutDest::Value);
703 self.out_dest.pipe_stderr = Some(OutDest::Value);
704 self
705 }
706
707 pub fn reset_out_dest(mut self) -> Self {
712 self.out_dest = StackOutDest::new();
713 self
714 }
715
716 pub fn reset_pipes(mut self) -> Self {
721 self.out_dest.pipe_stdout = None;
722 self.out_dest.pipe_stderr = None;
723 self
724 }
725
726 pub fn stdout_file(mut self, file: File) -> Self {
765 self.out_dest.stdout = OutDest::File(Arc::new(file));
766 self
767 }
768
769 pub fn stderr_file(mut self, file: File) -> Self {
773 self.out_dest.stderr = OutDest::File(Arc::new(file));
774 self
775 }
776
777 pub fn set_cwd(&mut self, path: impl AsRef<std::path::Path>) -> Result<(), ShellError> {
782 fn error(msg: &str) -> Result<(), ShellError> {
785 Err(ShellError::Generic(GenericError::new_internal(
786 msg.to_string(),
787 "",
788 )))
789 }
790
791 let path = path.as_ref();
792
793 if !path.is_absolute() {
794 if let Some(Component::Prefix(_)) = path.components().next() {
795 return Err(ShellError::Generic(
796 GenericError::new_internal("Cannot set $env.PWD to a prefix-only path", "")
797 .with_help(format!(
798 "Try to use {}{MAIN_SEPARATOR} instead",
799 path.display()
800 )),
801 ));
802 }
803
804 error("Cannot set $env.PWD to a non-absolute path")
805 } else if !path.exists() {
806 error("Cannot set $env.PWD to a non-existent directory")
807 } else if !path.is_dir() {
808 error("Cannot set $env.PWD to a non-directory")
809 } else {
810 let path = nu_path::strip_trailing_slash(path);
812 let value = Value::string(path.to_string_lossy(), Span::unknown());
813 self.add_env_var("PWD".into(), value);
814 Ok(())
815 }
816 }
817}
818
819#[cfg(test)]
820mod test {
821 use std::sync::Arc;
822
823 use crate::{Span, Value, VarId, engine::EngineState};
824
825 use super::Stack;
826
827 #[test]
828 fn test_children_see_inner_values() {
829 let mut original = Stack::new();
830 original.add_var(VarId::new(0), Value::test_string("hello"));
831
832 let cloned = Stack::with_parent(Arc::new(original));
833 assert_eq!(
834 cloned.get_var(VarId::new(0), Span::test_data()),
835 Ok(Value::test_string("hello"))
836 );
837 }
838
839 #[test]
840 fn test_children_dont_see_deleted_values() {
841 let mut original = Stack::new();
842 original.add_var(VarId::new(0), Value::test_string("hello"));
843
844 let mut cloned = Stack::with_parent(Arc::new(original));
845 cloned.remove_var(VarId::new(0));
846
847 assert_eq!(
848 cloned.get_var(VarId::new(0), Span::test_data()),
849 Err(crate::ShellError::VariableNotFoundAtRuntime {
850 span: Span::test_data()
851 })
852 );
853 }
854
855 #[test]
856 fn test_children_changes_override_parent() {
857 let mut original = Stack::new();
858 original.add_var(VarId::new(0), Value::test_string("hello"));
859
860 let mut cloned = Stack::with_parent(Arc::new(original));
861 cloned.add_var(VarId::new(0), Value::test_string("there"));
862 assert_eq!(
863 cloned.get_var(VarId::new(0), Span::test_data()),
864 Ok(Value::test_string("there"))
865 );
866
867 cloned.remove_var(VarId::new(0));
868 assert_eq!(
870 cloned.get_var(VarId::new(0), Span::test_data()),
871 Err(crate::ShellError::VariableNotFoundAtRuntime {
872 span: Span::test_data()
873 })
874 );
875 }
876 #[test]
877 fn test_children_changes_persist_in_offspring() {
878 let mut original = Stack::new();
879 original.add_var(VarId::new(0), Value::test_string("hello"));
880
881 let mut cloned = Stack::with_parent(Arc::new(original));
882 cloned.add_var(VarId::new(1), Value::test_string("there"));
883
884 cloned.remove_var(VarId::new(0));
885 let cloned = Stack::with_parent(Arc::new(cloned));
886
887 assert_eq!(
888 cloned.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 cloned.get_var(VarId::new(1), Span::test_data()),
896 Ok(Value::test_string("there"))
897 );
898 }
899
900 #[test]
901 fn test_merging_children_back_to_parent() {
902 let mut original = Stack::new();
903 let engine_state = EngineState::new();
904 original.add_var(VarId::new(0), Value::test_string("hello"));
905
906 let original_arc = Arc::new(original);
907 let mut cloned = Stack::with_parent(original_arc.clone());
908 cloned.add_var(VarId::new(1), Value::test_string("there"));
909
910 cloned.remove_var(VarId::new(0));
911
912 cloned.add_env_var(
913 "ADDED_IN_CHILD".to_string(),
914 Value::test_string("New Env Var"),
915 );
916
917 let original = Stack::with_changes_from_child(original_arc, cloned);
918
919 assert_eq!(
920 original.get_var(VarId::new(0), Span::test_data()),
921 Err(crate::ShellError::VariableNotFoundAtRuntime {
922 span: Span::test_data()
923 })
924 );
925
926 assert_eq!(
927 original.get_var(VarId::new(1), Span::test_data()),
928 Ok(Value::test_string("there"))
929 );
930
931 assert_eq!(
932 original
933 .get_env_var(&engine_state, "ADDED_IN_CHILD")
934 .cloned(),
935 Some(Value::test_string("New Env Var")),
936 );
937 }
938}