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 env_hide_history: Arc<HashMap<String, HashSet<EnvName>>>,
50 pub active_overlays: Vec<String>,
52 pub arguments: ArgumentStack,
54 pub error_handlers: ErrorHandlerStack,
56 pub finally_run_handlers: ErrorHandlerStack,
58 pub recursion_count: u64,
59 pub parent_stack: Option<Arc<Stack>>,
60 pub parent_deletions: Vec<VarId>,
62 pub deletions: Vec<VarId>,
64 pub config: Option<Arc<Config>>,
66 pub(crate) out_dest: StackOutDest,
67}
68
69impl Default for Stack {
70 fn default() -> Self {
71 Self::new()
72 }
73}
74
75impl Stack {
76 pub fn new() -> Self {
84 Self {
85 vars: Vec::new(),
86 env_vars: Vec::new(),
87 env_hidden: Arc::new(HashMap::new()),
88 env_hide_history: Arc::new(HashMap::new()),
89 active_overlays: vec![DEFAULT_OVERLAY_NAME.to_string()],
90 arguments: ArgumentStack::new(),
91 error_handlers: ErrorHandlerStack::new(),
92 finally_run_handlers: ErrorHandlerStack::new(),
93 recursion_count: 0,
94 parent_stack: None,
95 parent_deletions: vec![],
96 deletions: vec![],
97 config: None,
98 out_dest: StackOutDest::new(),
99 }
100 }
101
102 pub fn with_parent(parent: Arc<Stack>) -> Stack {
107 Stack {
108 env_vars: parent.env_vars.clone(),
110 env_hidden: parent.env_hidden.clone(),
111 env_hide_history: parent.env_hide_history.clone(),
112 active_overlays: parent.active_overlays.clone(),
113 arguments: ArgumentStack::new(),
114 error_handlers: ErrorHandlerStack::new(),
115 finally_run_handlers: ErrorHandlerStack::new(),
116 recursion_count: parent.recursion_count,
117 vars: vec![],
118 parent_deletions: vec![],
119 deletions: vec![],
120 config: parent.config.clone(),
121 out_dest: parent.out_dest.clone(),
122 parent_stack: Some(parent),
123 }
124 }
125
126 pub fn with_changes_from_child(parent: Arc<Stack>, child: Stack) -> Stack {
133 drop(child.parent_stack);
136 let mut unique_stack = Arc::unwrap_or_clone(parent);
137
138 unique_stack
139 .vars
140 .retain(|(var, _)| !child.parent_deletions.contains(var));
141 for (var, value) in child.vars {
142 unique_stack.add_var(var, value);
143 }
144 unique_stack.env_vars = child.env_vars;
145 unique_stack.env_hidden = child.env_hidden;
146 unique_stack.env_hide_history = child.env_hide_history;
147 unique_stack.active_overlays = child.active_overlays;
148 unique_stack.config = child.config;
149 unique_stack
150 }
151
152 pub fn with_env(
153 &mut self,
154 env_vars: &[Arc<EnvVars>],
155 env_hidden: &Arc<HashMap<String, HashSet<EnvName>>>,
156 ) {
157 if self.env_vars.iter().any(|scope| !scope.is_empty()) {
159 env_vars.clone_into(&mut self.env_vars);
160 }
161
162 if !self.env_hidden.is_empty() {
163 self.env_hidden.clone_from(env_hidden);
164 }
165 }
166
167 fn lookup_var(&self, var_id: VarId) -> Option<Value> {
169 for (id, val) in &self.vars {
170 if var_id == *id {
171 return Some(val.clone());
172 }
173 }
174
175 if let Some(stack) = &self.parent_stack
176 && !self.parent_deletions.contains(&var_id)
177 {
178 return stack.lookup_var(var_id);
179 }
180 None
181 }
182
183 pub fn get_var(&self, var_id: VarId, span: Span) -> Result<Value, ShellError> {
187 match self.lookup_var(var_id) {
188 Some(v) => Ok(v.with_span(span)),
189 None => Err(ShellError::VariableNotFoundAtRuntime { span }),
190 }
191 }
192
193 pub fn get_var_with_origin(&self, var_id: VarId, span: Span) -> Result<Value, ShellError> {
198 match self.lookup_var(var_id) {
199 Some(v) => Ok(v),
200 None => {
201 if var_id == NU_VARIABLE_ID || var_id == ENV_VARIABLE_ID {
202 return Err(ShellError::Generic(GenericError::new(
203 "Built-in variables `$env` and `$nu` have no metadata",
204 "no metadata available",
205 span,
206 )));
207 }
208 Err(ShellError::VariableNotFoundAtRuntime { span })
209 }
210 }
211 }
212
213 pub fn get_config(&self, engine_state: &EngineState) -> Arc<Config> {
217 self.config
218 .clone()
219 .unwrap_or_else(|| engine_state.config.clone())
220 }
221
222 pub fn update_config(&mut self, engine_state: &EngineState) -> Result<(), ShellError> {
227 if let Some(value) = self.get_env_var(engine_state, "config") {
228 let old = self.get_config(engine_state);
229 let mut config = (*old).clone();
230 let result = config.update_from_value_with_options(
231 &old,
232 value,
233 engine_state.history_locked_after_startup,
234 );
235 self.add_env_var("config".into(), config.clone().into_value(value.span()));
237 self.config = Some(config.into());
238 if let Some(warning) = result? {
239 report_shell_warning(Some(self), engine_state, &warning);
240 }
241 } else {
242 self.config = None;
243 }
244 Ok(())
245 }
246
247 pub fn add_var(&mut self, var_id: VarId, value: Value) {
248 for (id, val) in &mut self.vars {
250 if *id == var_id {
251 *val = value;
252 return;
253 }
254 }
255 self.vars.push((var_id, value));
256 }
257
258 pub fn remove_var(&mut self, var_id: VarId) {
259 for (idx, (id, _)) in self.vars.iter().enumerate() {
260 if *id == var_id {
261 self.vars.remove(idx);
262 break;
263 }
264 }
265 if self.parent_stack.is_some() {
268 self.parent_deletions.push(var_id);
269 }
270 self.deletions.push(var_id);
271 }
272
273 pub fn add_env_var(&mut self, var: String, value: Value) {
274 if let Some(last_overlay) = self.active_overlays.last().cloned() {
275 let env_name = EnvName::from(var);
276 self.clear_env_var_marks_in_active_overlay(&last_overlay, &env_name);
277
278 if let Some(scope) = self.env_vars.last_mut() {
279 let scope = Arc::make_mut(scope);
280 if let Some(env_vars) = scope.get_mut(&last_overlay) {
281 env_vars.insert(env_name, value);
282 } else {
283 scope.insert(last_overlay, [(env_name, value)].into_iter().collect());
284 }
285 } else {
286 self.env_vars.push(Arc::new(
287 [(last_overlay, [(env_name, value)].into_iter().collect())]
288 .into_iter()
289 .collect(),
290 ));
291 }
292 } else {
293 panic!("internal error: no active overlay");
295 }
296 }
297
298 fn clear_env_var_marks_in_active_overlay(&mut self, overlay: &str, env_name: &EnvName) {
299 if let Some(env_hidden) = Arc::make_mut(&mut self.env_hidden).get_mut(overlay) {
300 env_hidden.remove(env_name);
302 }
303
304 if let Some(hide_history) = Arc::make_mut(&mut self.env_hide_history).get_mut(overlay) {
305 hide_history.remove(env_name);
306 }
307 }
308
309 pub fn set_last_exit_code(&mut self, code: i32, span: Span) {
310 self.add_env_var("LAST_EXIT_CODE".into(), Value::int(code.into(), span));
311 }
312
313 pub fn set_last_error(&mut self, error: &ShellError) {
314 if let Some(code) = error.external_exit_code() {
315 self.set_last_exit_code(code.item, code.span);
316 } else if let Some(code) = error.exit_code() {
317 self.set_last_exit_code(code, Span::unknown());
318 }
319 }
320
321 pub fn last_overlay_name(&self) -> Result<String, ShellError> {
322 self.active_overlays
323 .last()
324 .cloned()
325 .ok_or_else(|| ShellError::NushellFailed {
326 msg: "No active overlay".into(),
327 })
328 }
329
330 pub fn captures_to_stack(&self, captures: Vec<(VarId, Value)>) -> Stack {
332 self.captures_to_stack_preserve_out_dest(captures)
333 .collect_value()
334 }
335
336 pub fn captures_to_stack_preserve_out_dest(&self, captures: Vec<(VarId, Value)>) -> Stack {
338 let mut env_vars = self.env_vars.clone();
339 env_vars.push(Arc::new(HashMap::new()));
340
341 Stack {
342 vars: captures,
343 env_vars,
344 env_hidden: self.env_hidden.clone(),
345 env_hide_history: self.env_hide_history.clone(),
346 active_overlays: self.active_overlays.clone(),
347 arguments: ArgumentStack::new(),
348 error_handlers: ErrorHandlerStack::new(),
349 finally_run_handlers: ErrorHandlerStack::new(),
350 recursion_count: self.recursion_count,
351 parent_stack: None,
352 parent_deletions: vec![],
353 deletions: vec![],
354 config: self.config.clone(),
355 out_dest: self.out_dest.clone(),
356 }
357 }
358
359 pub fn gather_captures(&self, engine_state: &EngineState, captures: &[(VarId, Span)]) -> Stack {
360 let mut vars = Vec::with_capacity(captures.len());
361
362 let fake_span = Span::new(0, 0);
363
364 for (capture, _) in captures {
365 if let Ok(value) = self.get_var(*capture, fake_span) {
368 vars.push((*capture, value));
369 } else if let Some(const_val) = &engine_state.get_var(*capture).const_val {
370 vars.push((*capture, const_val.clone()));
371 }
372 }
373
374 let mut env_vars = self.env_vars.clone();
375 env_vars.push(Arc::new(HashMap::new()));
376
377 Stack {
378 vars,
379 env_vars,
380 env_hidden: self.env_hidden.clone(),
381 env_hide_history: self.env_hide_history.clone(),
382 active_overlays: self.active_overlays.clone(),
383 arguments: ArgumentStack::new(),
384 error_handlers: ErrorHandlerStack::new(),
385 finally_run_handlers: ErrorHandlerStack::new(),
386 recursion_count: self.recursion_count,
387 parent_stack: None,
388 parent_deletions: vec![],
389 deletions: vec![],
390 config: self.config.clone(),
391 out_dest: self.out_dest.clone(),
392 }
393 }
394
395 pub fn get_env_vars(&self, engine_state: &EngineState) -> HashMap<String, Value> {
397 let mut result = HashMap::new();
398
399 for active_overlay in self.active_overlays.iter() {
400 if let Some(env_vars) = engine_state.env_vars.get(active_overlay) {
401 result.extend(
402 env_vars
403 .iter()
404 .filter(|(k, _)| {
405 if let Some(env_hidden) = self.env_hidden.get(active_overlay) {
406 !env_hidden.contains(*k)
407 } else {
408 true
410 }
411 })
412 .map(|(k, v)| (k.as_str().to_string(), v.clone()))
413 .collect::<HashMap<String, Value>>(),
414 );
415 }
416 }
417
418 result.extend(self.get_stack_env_vars());
419
420 result
421 }
422
423 pub fn get_stack_env_vars(&self) -> HashMap<String, Value> {
425 let mut result = HashMap::new();
426
427 for scope in &self.env_vars {
428 for active_overlay in self.active_overlays.iter() {
429 if let Some(env_vars) = scope.get(active_overlay) {
430 result.extend(
431 env_vars
432 .iter()
433 .map(|(k, v)| (k.as_str().to_string(), v.clone())),
434 );
435 }
436 }
437 }
438
439 result
440 }
441
442 pub fn get_stack_overlay_env_vars(&self, overlay_name: &str) -> HashMap<String, Value> {
444 let mut result = HashMap::new();
445
446 for scope in &self.env_vars {
447 if let Some(active_overlay) = self.active_overlays.iter().find(|n| n == &overlay_name)
448 && let Some(env_vars) = scope.get(active_overlay)
449 {
450 result.extend(
451 env_vars
452 .iter()
453 .map(|(k, v)| (k.as_str().to_string(), v.clone())),
454 );
455 }
456 }
457
458 result
459 }
460
461 pub fn get_hidden_env_vars(
463 &self,
464 excluded_overlay_name: &str,
465 engine_state: &EngineState,
466 ) -> HashMap<String, Value> {
467 let mut result = HashMap::new();
468
469 for overlay_name in self.active_overlays.iter().rev() {
470 if overlay_name == excluded_overlay_name {
471 continue;
472 }
473 if let Some(env_names) = self.env_hidden.get(overlay_name) {
474 for n in env_names {
475 if result.contains_key(n.as_str()) {
476 continue;
477 }
478 if let Some(Some(v)) = engine_state
480 .env_vars
481 .get(overlay_name)
482 .map(|env_vars| env_vars.get(n))
483 {
484 result.insert(n.as_str().to_string(), v.clone());
485 }
486 }
487 }
488 }
489 result
490 }
491
492 pub fn get_env_var_names(&self, engine_state: &EngineState) -> HashSet<String> {
494 let mut result = HashSet::new();
495
496 for active_overlay in self.active_overlays.iter() {
497 if let Some(env_vars) = engine_state.env_vars.get(active_overlay) {
498 result.extend(
499 env_vars
500 .keys()
501 .filter(|k| {
502 if let Some(env_hidden) = self.env_hidden.get(active_overlay) {
503 !env_hidden.contains(*k)
504 } else {
505 true
507 }
508 })
509 .map(|k| k.as_str().to_string())
510 .collect::<HashSet<String>>(),
511 );
512 }
513 }
514
515 for scope in &self.env_vars {
516 for active_overlay in self.active_overlays.iter() {
517 if let Some(env_vars) = scope.get(active_overlay) {
518 result.extend(
519 env_vars
520 .keys()
521 .map(|k| k.as_str().to_string())
522 .collect::<HashSet<String>>(),
523 );
524 }
525 }
526 }
527
528 result
529 }
530
531 pub fn get_env_var<'a>(
532 &'a self,
533 engine_state: &'a EngineState,
534 name: &str,
535 ) -> Option<&'a Value> {
536 let env_name = EnvName::from(name);
537
538 for scope in self.env_vars.iter().rev() {
539 for active_overlay in self.active_overlays.iter().rev() {
540 if let Some(env_vars) = scope.get(active_overlay)
541 && let Some(v) = env_vars.get(&env_name)
542 {
543 return Some(v);
544 }
545 }
546 }
547
548 for active_overlay in self.active_overlays.iter().rev() {
549 if !self.is_env_hidden_in_overlay(active_overlay, &env_name)
550 && let Some(env_vars) = engine_state.env_vars.get(active_overlay)
551 && let Some(v) = env_vars.get(&env_name)
552 {
553 return Some(v);
554 }
555 }
556 None
557 }
558
559 pub fn has_env_var(&self, engine_state: &EngineState, name: &str) -> bool {
560 let env_name = EnvName::from(name);
561
562 for scope in self.env_vars.iter().rev() {
563 for active_overlay in self.active_overlays.iter().rev() {
564 if let Some(env_vars) = scope.get(active_overlay)
565 && env_vars.contains_key(&env_name)
566 {
567 return true;
568 }
569 }
570 }
571
572 for active_overlay in self.active_overlays.iter().rev() {
573 if !self.is_env_hidden_in_overlay(active_overlay, &env_name)
574 && let Some(env_vars) = engine_state.env_vars.get(active_overlay)
575 && env_vars.contains_key(&env_name)
576 {
577 return true;
578 }
579 }
580
581 false
582 }
583
584 pub fn remove_env_var(&mut self, engine_state: &EngineState, name: &str) -> bool {
592 let env_name = EnvName::from(name);
593
594 self.remove_env_var_from_stack(&env_name)
595 || self.hide_engine_state_env_var(engine_state, &env_name)
596 }
597
598 fn remove_env_var_from_stack(&mut self, env_name: &EnvName) -> bool {
601 self.env_vars
602 .iter_mut()
603 .rev()
604 .map(Arc::make_mut)
605 .find_map(|scope| {
606 self.active_overlays
607 .iter()
608 .rev()
609 .find_map(|active_overlay| scope.get_mut(active_overlay)?.remove(env_name))
610 })
611 .is_some()
612 }
613
614 fn hide_engine_state_env_var(
619 &mut self,
620 engine_state: &EngineState,
621 env_name: &EnvName,
622 ) -> bool {
623 let overlay_containing_env_var = self.active_overlays.iter().rev().find(|active_overlay| {
624 engine_state
625 .env_vars
626 .get(active_overlay.as_str())
627 .is_some_and(|env_vars| env_vars.contains_key(env_name))
628 });
629
630 let Some(overlay_containing_env_var) = overlay_containing_env_var else {
631 return false;
632 };
633
634 let env_hidden = Arc::make_mut(&mut self.env_hidden);
635
636 if env_hidden
637 .get(overlay_containing_env_var.as_str())
638 .is_some_and(|hidden_vars| hidden_vars.contains(env_name))
639 {
640 return false;
641 }
642
643 env_hidden
644 .entry(overlay_containing_env_var.clone())
645 .or_default()
646 .insert(env_name.clone());
647 true
648 }
649
650 fn record_env_var_hide_in_active_overlay(&mut self, env_name: &EnvName) -> bool {
653 let Some(active_overlay) = self.active_overlays.last().cloned() else {
654 return false;
655 };
656
657 Arc::make_mut(&mut self.env_hide_history)
658 .entry(active_overlay)
659 .or_default()
660 .insert(env_name.clone())
661 }
662
663 fn is_env_var_hide_recorded(&self, env_name: &EnvName) -> bool {
664 self.active_overlays
665 .iter()
666 .rev()
667 .filter_map(|overlay| self.env_hide_history.get(overlay))
668 .any(|hidden_vars| hidden_vars.contains(env_name))
669 }
670
671 fn is_env_hidden_in_overlay(&self, overlay: &str, env_name: &EnvName) -> bool {
672 self.env_hidden
673 .get(overlay)
674 .is_some_and(|hidden_vars| hidden_vars.contains(env_name))
675 }
676
677 pub fn hide_env_var(&mut self, engine_state: &EngineState, name: &str) -> bool {
685 let env_name = EnvName::from(name);
686
687 if self.is_env_var_hide_recorded(&env_name) {
689 return false;
690 }
691
692 if self.remove_env_var_from_stack(&env_name) {
693 self.record_env_var_hide_in_active_overlay(&env_name);
694
695 if !self.has_env_var_in_stack(&env_name) {
696 self.hide_engine_state_env_var(engine_state, &env_name);
697 }
698 return true;
699 }
700
701 if self.hide_engine_state_env_var(engine_state, &env_name) {
702 self.record_env_var_hide_in_active_overlay(&env_name);
703 return true;
704 }
705
706 false
707 }
708
709 fn has_env_var_in_stack(&self, name: &EnvName) -> bool {
711 self.env_vars.iter().rev().any(|scope| {
712 self.active_overlays
713 .iter()
714 .rev()
715 .filter_map(|active_overlay| scope.get(active_overlay))
716 .any(|env_vars| env_vars.contains_key(name))
717 })
718 }
719
720 pub fn has_env_overlay(&self, name: &str, engine_state: &EngineState) -> bool {
721 for scope in self.env_vars.iter().rev() {
722 if scope.contains_key(name) {
723 return true;
724 }
725 }
726
727 engine_state.env_vars.contains_key(name)
728 }
729
730 pub fn is_overlay_active(&self, name: &str) -> bool {
731 self.active_overlays.iter().any(|n| n == name)
732 }
733
734 pub fn add_overlay(&mut self, name: String) {
735 self.active_overlays.retain(|o| o != &name);
736 self.active_overlays.push(name);
737 }
738
739 pub fn remove_overlay(&mut self, name: &str) {
740 self.active_overlays.retain(|o| o != name);
741 }
742
743 pub fn stdout(&self) -> &OutDest {
749 self.out_dest.stdout()
750 }
751
752 pub fn stderr(&self) -> &OutDest {
758 self.out_dest.stderr()
759 }
760
761 pub fn pipe_stdout(&self) -> Option<&OutDest> {
763 self.out_dest.pipe_stdout.as_ref()
764 }
765
766 pub fn pipe_stderr(&self) -> Option<&OutDest> {
768 self.out_dest.pipe_stderr.as_ref()
769 }
770
771 pub fn start_collect_value(&mut self) -> StackCollectValueGuard<'_> {
775 StackCollectValueGuard::new(self)
776 }
777
778 pub fn use_call_arg_out_dest(&mut self) -> StackCallArgGuard<'_> {
782 StackCallArgGuard::new(self)
783 }
784
785 pub fn push_redirection(
787 &mut self,
788 stdout: Option<Redirection>,
789 stderr: Option<Redirection>,
790 ) -> StackIoGuard<'_> {
791 StackIoGuard::new(self, stdout, stderr)
792 }
793
794 pub fn collect_value(mut self) -> Self {
801 self.out_dest.pipe_stdout = Some(OutDest::Value);
802 self.out_dest.pipe_stderr = None;
803 self
804 }
805
806 pub fn capture_all(mut self) -> Self {
815 self.out_dest.pipe_stdout = Some(OutDest::Value);
816 self.out_dest.pipe_stderr = Some(OutDest::Value);
817 self
818 }
819
820 pub fn reset_out_dest(mut self) -> Self {
825 self.out_dest = StackOutDest::new();
826 self
827 }
828
829 pub fn reset_pipes(mut self) -> Self {
834 self.out_dest.pipe_stdout = None;
835 self.out_dest.pipe_stderr = None;
836 self
837 }
838
839 pub fn stdout_file(mut self, file: File) -> Self {
878 self.out_dest.stdout = OutDest::File(Arc::new(file));
879 self
880 }
881
882 pub fn stderr_file(mut self, file: File) -> Self {
886 self.out_dest.stderr = OutDest::File(Arc::new(file));
887 self
888 }
889
890 pub fn set_cwd(&mut self, path: impl AsRef<std::path::Path>) -> Result<(), ShellError> {
895 fn error(msg: &str) -> Result<(), ShellError> {
898 Err(ShellError::Generic(GenericError::new_internal(
899 msg.to_string(),
900 "",
901 )))
902 }
903
904 let path = path.as_ref();
905
906 if !path.is_absolute() {
907 if let Some(Component::Prefix(_)) = path.components().next() {
908 return Err(ShellError::Generic(
909 GenericError::new_internal("Cannot set $env.PWD to a prefix-only path", "")
910 .with_help(format!(
911 "Try to use {}{MAIN_SEPARATOR} instead",
912 path.display()
913 )),
914 ));
915 }
916
917 error("Cannot set $env.PWD to a non-absolute path")
918 } else if !path.exists() {
919 error("Cannot set $env.PWD to a non-existent directory")
920 } else if !path.is_dir() {
921 error("Cannot set $env.PWD to a non-directory")
922 } else {
923 let path = nu_path::strip_trailing_slash(path);
925 let value = Value::string(path.to_string_lossy(), Span::unknown());
926 self.add_env_var("PWD".into(), value);
927 Ok(())
928 }
929 }
930}
931
932#[cfg(test)]
933mod test {
934 use std::sync::Arc;
935
936 use crate::{Span, Value, VarId, engine::EngineState};
937
938 use super::Stack;
939
940 #[test]
941 fn test_children_see_inner_values() {
942 let mut original = Stack::new();
943 original.add_var(VarId::new(0), Value::test_string("hello"));
944
945 let cloned = Stack::with_parent(Arc::new(original));
946 assert_eq!(
947 cloned.get_var(VarId::new(0), Span::test_data()),
948 Ok(Value::test_string("hello"))
949 );
950 }
951
952 #[test]
953 fn test_children_dont_see_deleted_values() {
954 let mut original = Stack::new();
955 original.add_var(VarId::new(0), Value::test_string("hello"));
956
957 let mut cloned = Stack::with_parent(Arc::new(original));
958 cloned.remove_var(VarId::new(0));
959
960 assert_eq!(
961 cloned.get_var(VarId::new(0), Span::test_data()),
962 Err(crate::ShellError::VariableNotFoundAtRuntime {
963 span: Span::test_data()
964 })
965 );
966 }
967
968 #[test]
969 fn test_children_changes_override_parent() {
970 let mut original = Stack::new();
971 original.add_var(VarId::new(0), Value::test_string("hello"));
972
973 let mut cloned = Stack::with_parent(Arc::new(original));
974 cloned.add_var(VarId::new(0), Value::test_string("there"));
975 assert_eq!(
976 cloned.get_var(VarId::new(0), Span::test_data()),
977 Ok(Value::test_string("there"))
978 );
979
980 cloned.remove_var(VarId::new(0));
981 assert_eq!(
983 cloned.get_var(VarId::new(0), Span::test_data()),
984 Err(crate::ShellError::VariableNotFoundAtRuntime {
985 span: Span::test_data()
986 })
987 );
988 }
989 #[test]
990 fn test_children_changes_persist_in_offspring() {
991 let mut original = Stack::new();
992 original.add_var(VarId::new(0), Value::test_string("hello"));
993
994 let mut cloned = Stack::with_parent(Arc::new(original));
995 cloned.add_var(VarId::new(1), Value::test_string("there"));
996
997 cloned.remove_var(VarId::new(0));
998 let cloned = Stack::with_parent(Arc::new(cloned));
999
1000 assert_eq!(
1001 cloned.get_var(VarId::new(0), Span::test_data()),
1002 Err(crate::ShellError::VariableNotFoundAtRuntime {
1003 span: Span::test_data()
1004 })
1005 );
1006
1007 assert_eq!(
1008 cloned.get_var(VarId::new(1), Span::test_data()),
1009 Ok(Value::test_string("there"))
1010 );
1011 }
1012
1013 #[test]
1014 fn test_merging_children_back_to_parent() {
1015 let mut original = Stack::new();
1016 let engine_state = EngineState::new();
1017 original.add_var(VarId::new(0), Value::test_string("hello"));
1018
1019 let original_arc = Arc::new(original);
1020 let mut cloned = Stack::with_parent(original_arc.clone());
1021 cloned.add_var(VarId::new(1), Value::test_string("there"));
1022
1023 cloned.remove_var(VarId::new(0));
1024
1025 cloned.add_env_var(
1026 "ADDED_IN_CHILD".to_string(),
1027 Value::test_string("New Env Var"),
1028 );
1029
1030 let original = Stack::with_changes_from_child(original_arc, cloned);
1031
1032 assert_eq!(
1033 original.get_var(VarId::new(0), Span::test_data()),
1034 Err(crate::ShellError::VariableNotFoundAtRuntime {
1035 span: Span::test_data()
1036 })
1037 );
1038
1039 assert_eq!(
1040 original.get_var(VarId::new(1), Span::test_data()),
1041 Ok(Value::test_string("there"))
1042 );
1043
1044 assert_eq!(
1045 original
1046 .get_env_var(&engine_state, "ADDED_IN_CHILD")
1047 .cloned(),
1048 Some(Value::test_string("New Env Var")),
1049 );
1050 }
1051}