1use crate::{
2 Config, ENV_VARIABLE_ID, IntoValue, LAST_VARIABLE_ID, NU_VARIABLE_ID, OutDest, PipelineData,
3 PipelineMetadata, ShellError, Span, Value, VarId,
4 ast::PathMember,
5 engine::{
6 ArgumentStack, DEFAULT_OVERLAY_NAME, EngineState, EnvName, ErrorHandlerStack, Redirection,
7 ScopeBindings, StackCallArgGuard, StackCollectValueGuard, StackIoGuard, StackOutDest,
8 StackWithInvocation,
9 },
10 ir::ScopeRegion,
11 record, report_shell_warning,
12 shell_error::generic::GenericError,
13 truncate_value_to_budget,
14};
15use std::{
16 collections::{HashMap, HashSet},
17 fs::File,
18 path::{Component, MAIN_SEPARATOR},
19 sync::{Arc, Mutex},
20};
21
22#[derive(Debug, Default)]
32struct LastResultSlot {
33 present: bool,
35 last: Option<Value>,
37 metadata: Option<PipelineMetadata>,
39 truncated: bool,
40 exit_code: i64,
42 duration_ns: i64,
44 command: String,
46 warn_pending: bool,
48 warn_deferred: bool,
50}
51
52pub type EnvVars = HashMap<String, HashMap<EnvName, Value>>;
54
55#[derive(Debug, Clone)]
73pub struct Stack {
74 pub vars: Vec<(VarId, Value)>,
76 pub env_vars: Vec<Arc<EnvVars>>,
78 pub env_hidden: Arc<HashMap<String, HashSet<EnvName>>>,
80 pub env_hide_history: Arc<HashMap<String, HashSet<EnvName>>>,
85 pub active_overlays: Vec<String>,
87 pub arguments: ArgumentStack,
89 pub error_handlers: ErrorHandlerStack,
91 pub finally_run_handlers: ErrorHandlerStack,
93 pub recursion_count: u64,
94 pub parent_stack: Option<Arc<Stack>>,
95 pub parent_deletions: Vec<VarId>,
97 pub deletions: Vec<VarId>,
99 pub config: Option<Arc<Config>>,
101 pub(crate) out_dest: StackOutDest,
102 pub suppress_stdin: bool,
105 pub active_scope_bindings: Vec<Arc<ScopeBindings>>,
110 pub ir_scope_regions: Vec<ScopeRegion>,
112 pub ir_instruction_index: Option<usize>,
114 last_result: Arc<Mutex<LastResultSlot>>,
119}
120
121impl Default for Stack {
122 fn default() -> Self {
123 Self::new()
124 }
125}
126
127impl Stack {
128 pub fn new() -> Self {
136 Self {
137 vars: Vec::new(),
138 env_vars: Vec::new(),
139 env_hidden: Arc::new(HashMap::new()),
140 env_hide_history: Arc::new(HashMap::new()),
141 active_overlays: vec![DEFAULT_OVERLAY_NAME.to_string()],
142 arguments: ArgumentStack::new(),
143 error_handlers: ErrorHandlerStack::new(),
144 finally_run_handlers: ErrorHandlerStack::new(),
145 recursion_count: 0,
146 parent_stack: None,
147 parent_deletions: vec![],
148 deletions: vec![],
149 config: None,
150 out_dest: StackOutDest::new(),
151 suppress_stdin: false,
152 active_scope_bindings: vec![],
153 ir_scope_regions: vec![],
154 ir_instruction_index: None,
155 last_result: Arc::new(Mutex::new(LastResultSlot::default())),
156 }
157 }
158
159 pub fn with_parent(parent: Arc<Stack>) -> Stack {
164 Stack {
165 env_vars: parent.env_vars.clone(),
167 env_hidden: parent.env_hidden.clone(),
168 env_hide_history: parent.env_hide_history.clone(),
169 active_overlays: parent.active_overlays.clone(),
170 arguments: ArgumentStack::new(),
171 error_handlers: ErrorHandlerStack::new(),
172 finally_run_handlers: ErrorHandlerStack::new(),
173 recursion_count: parent.recursion_count,
174 vars: vec![],
175 parent_deletions: vec![],
176 deletions: vec![],
177 config: parent.config.clone(),
178 out_dest: parent.out_dest.clone(),
179 suppress_stdin: parent.suppress_stdin,
180 active_scope_bindings: parent.active_scope_bindings.clone(),
182 ir_scope_regions: vec![],
184 ir_instruction_index: None,
185 last_result: parent.last_result.clone(),
187 parent_stack: Some(parent),
188 }
189 }
190
191 pub fn push_scope_bindings(&mut self, bindings: Arc<ScopeBindings>) {
193 self.active_scope_bindings.push(bindings);
194 }
195
196 pub fn pop_scope_bindings(&mut self) {
198 let popped = self.active_scope_bindings.pop();
199 debug_assert!(
200 popped.is_some(),
201 "pop_scope_bindings with empty active_scope_bindings (unbalanced push/pop)"
202 );
203 }
204
205 pub fn with_changes_from_child(parent: Arc<Stack>, child: Stack) -> Stack {
212 drop(child.parent_stack);
215 let mut unique_stack = Arc::unwrap_or_clone(parent);
216
217 unique_stack
218 .vars
219 .retain(|(var, _)| !child.parent_deletions.contains(var));
220 for (var, value) in child.vars {
221 unique_stack.add_var(var, value);
222 }
223 unique_stack.env_vars = child.env_vars;
224 unique_stack.env_hidden = child.env_hidden;
225 unique_stack.env_hide_history = child.env_hide_history;
226 unique_stack.active_overlays = child.active_overlays;
227 unique_stack.config = child.config;
228 unique_stack
230 }
231
232 pub fn with_env(
233 &mut self,
234 env_vars: &[Arc<EnvVars>],
235 env_hidden: &Arc<HashMap<String, HashSet<EnvName>>>,
236 ) {
237 if self.env_vars.iter().any(|scope| !scope.is_empty()) {
239 env_vars.clone_into(&mut self.env_vars);
240 }
241
242 if !self.env_hidden.is_empty() {
243 self.env_hidden.clone_from(env_hidden);
244 }
245 }
246
247 fn lookup_var(&self, var_id: VarId) -> Option<Value> {
249 if var_id == LAST_VARIABLE_ID {
250 return self.assemble_ans_record(Span::unknown());
251 }
252
253 for (id, val) in &self.vars {
254 if var_id == *id {
255 return Some(val.clone());
256 }
257 }
258
259 if let Some(stack) = &self.parent_stack
260 && !self.parent_deletions.contains(&var_id)
261 {
262 return stack.lookup_var(var_id);
263 }
264 None
265 }
266
267 fn with_last_result_slot<R>(&self, f: impl FnOnce(&LastResultSlot) -> R) -> R {
268 match self.last_result.lock() {
269 Ok(slot) => f(&slot),
270 Err(poisoned) => f(&poisoned.into_inner()),
272 }
273 }
274
275 fn with_last_result_slot_mut<R>(&self, f: impl FnOnce(&mut LastResultSlot) -> R) -> R {
276 match self.last_result.lock() {
277 Ok(mut slot) => f(&mut slot),
278 Err(poisoned) => {
279 let mut slot = poisoned.into_inner();
280 *slot = LastResultSlot::default();
282 f(&mut slot)
283 }
284 }
285 }
286
287 fn assemble_ans_record(&self, span: Span) -> Option<Value> {
293 self.with_last_result_slot(|slot| {
294 if !slot.present {
295 return None;
296 }
297 Some(match &slot.last {
298 Some(last) => Value::record(
299 record! {
300 "last" => last.clone().with_span(span),
301 "exit_code" => Value::int(slot.exit_code, span),
302 "duration" => Value::duration(slot.duration_ns, span),
303 "command" => Value::string(slot.command.clone(), span),
304 },
305 span,
306 ),
307 None => Value::record(
308 record! {
309 "exit_code" => Value::int(slot.exit_code, span),
310 "duration" => Value::duration(slot.duration_ns, span),
311 "command" => Value::string(slot.command.clone(), span),
312 },
313 span,
314 ),
315 })
316 })
317 }
318
319 pub fn clear_last_result(&mut self) {
321 self.with_last_result_slot_mut(|slot| {
322 if let Some(old) = slot.last.take() {
323 drop(old);
324 }
325 *slot = LastResultSlot::default();
326 });
327 }
328
329 pub fn clear_last_result_payload(&mut self) {
334 self.with_last_result_slot_mut(|slot| {
335 if let Some(old) = slot.last.take() {
336 drop(old);
337 }
338 slot.metadata = None;
339 slot.truncated = false;
340 slot.warn_pending = false;
341 slot.warn_deferred = false;
342 });
343 }
344
345 pub fn set_last_result(
351 &mut self,
352 value: Value,
353 metadata: Option<PipelineMetadata>,
354 budget: usize,
355 ) {
356 if budget == 0 {
357 self.clear_last_result_payload();
358 return;
359 }
360
361 let (stored, truncated) = truncate_value_to_budget(value, budget);
362 self.store_last_result_raw(stored, metadata, truncated);
363 }
364
365 pub fn store_last_result_raw(
370 &mut self,
371 value: Value,
372 metadata: Option<PipelineMetadata>,
373 truncated: bool,
374 ) {
375 self.with_last_result_slot_mut(|slot| {
376 if let Some(old) = slot.last.replace(value) {
377 drop(old);
378 }
379 slot.metadata = metadata;
380 slot.truncated = truncated;
381 slot.warn_pending = truncated;
382 slot.warn_deferred = false;
384 slot.present = true;
385 });
386 }
387
388 pub fn snapshot_ans_repl_metadata(
398 &mut self,
399 engine_state: &EngineState,
400 duration: std::time::Duration,
401 command: impl Into<String>,
402 ) {
403 let budget = self.get_config(engine_state).max_last_result_size_bytes();
404 let exit_code = self
405 .get_env_var(engine_state, "LAST_EXIT_CODE")
406 .and_then(|v| v.as_int().ok())
407 .unwrap_or(0);
408 let duration_ns = i64::try_from(duration.as_nanos()).unwrap_or(i64::MAX);
409 let command = command.into();
410
411 if budget == 0 {
412 self.clear_last_result_payload();
414 }
415
416 self.with_last_result_slot_mut(|slot| {
417 slot.exit_code = exit_code;
418 slot.duration_ns = duration_ns;
419 slot.command = command;
420 slot.present = true;
421 });
422 }
423
424 pub fn last_result_metadata(&self) -> Option<PipelineMetadata> {
426 self.with_last_result_slot(|slot| slot.metadata.clone())
427 }
428
429 pub fn last_result_memory_size(&self) -> usize {
431 self.with_last_result_slot(|slot| slot.last.as_ref().map(|v| v.memory_size()).unwrap_or(0))
432 }
433
434 pub const ANS_LAST_RESULT_METADATA_KEY: &str = "ans_last_result";
439
440 pub fn last_result_pipeline_data(&self, span: Span) -> PipelineData {
443 let mut metadata = self.last_result_metadata().unwrap_or_default();
444 metadata
446 .custom
447 .insert(Self::ANS_LAST_RESULT_METADATA_KEY, Value::bool(true, span));
448 let value = self
449 .assemble_ans_record(span)
450 .unwrap_or_else(|| Value::nothing(span));
451 PipelineData::value(value, Some(metadata))
452 }
453
454 pub fn last_result_was_truncated(&self) -> bool {
456 self.with_last_result_slot(|slot| slot.truncated)
457 }
458
459 pub fn defer_last_result_truncation_warning(&self) {
464 self.with_last_result_slot_mut(|slot| {
465 if slot.warn_pending {
466 slot.warn_pending = false;
467 slot.warn_deferred = true;
468 }
469 });
470 }
471
472 pub fn last_result_warn_pending(&self) -> bool {
474 self.with_last_result_slot(|slot| slot.warn_pending)
475 }
476
477 pub fn take_last_result_warn_deferred(&self) -> bool {
482 self.with_last_result_slot_mut(|slot| std::mem::take(&mut slot.warn_deferred))
483 }
484
485 pub fn flush_last_result_truncation_warning(&self, engine_state: &EngineState, span: Span) {
489 if !self.take_last_result_warn_deferred() {
490 return;
491 }
492 let limit_bytes = self.get_config(engine_state).max_last_result_size_bytes();
493 report_shell_warning(
494 Some(self),
495 engine_state,
496 &crate::ShellWarning::LastResultTruncated {
497 span,
498 limit_bytes,
499 help: Some(format!(
500 "Increase $env.config.max_last_result_size or use a smaller command result. Variable name is `${}`.",
501 crate::LAST_RESULT_VAR_NAME
502 )),
503 report_mode: crate::ReportMode::EveryUse,
505 },
506 );
507 }
508
509 pub fn get_var(&self, var_id: VarId, span: Span) -> Result<Value, ShellError> {
513 match self.lookup_var(var_id) {
514 Some(v) => Ok(v.with_span(span)),
515 None if var_id == LAST_VARIABLE_ID => Ok(Value::nothing(span)),
517 None => Err(ShellError::VariableNotFoundAtRuntime { span }),
518 }
519 }
520
521 pub fn get_var_with_origin(&self, var_id: VarId, span: Span) -> Result<Value, ShellError> {
526 match self.lookup_var(var_id) {
527 Some(v) => Ok(v),
528 None => {
529 if var_id == NU_VARIABLE_ID || var_id == ENV_VARIABLE_ID {
530 return Err(ShellError::Generic(GenericError::new(
531 "Built-in variables `$env` and `$nu` have no metadata",
532 "no metadata available",
533 span,
534 )));
535 }
536 Err(ShellError::VariableNotFoundAtRuntime { span })
537 }
538 }
539 }
540
541 pub fn get_config(&self, engine_state: &EngineState) -> Arc<Config> {
545 self.config
546 .clone()
547 .unwrap_or_else(|| engine_state.config.clone())
548 }
549
550 pub fn update_config(&mut self, engine_state: &EngineState) -> Result<(), ShellError> {
555 if let Some(value) = self.get_env_var(engine_state, "config") {
556 let old = self.get_config(engine_state);
557 let mut config = (*old).clone();
558 let result = config.update_from_value_with_options(
559 &old,
560 value,
561 engine_state.history_locked_after_startup,
562 );
563 self.add_env_var("config".into(), config.clone().into_value(value.span()));
565 self.config = Some(config.into());
566 if let Some(warning) = result? {
567 report_shell_warning(Some(self), engine_state, &warning);
568 }
569 } else {
570 self.config = None;
571 }
572 Ok(())
573 }
574
575 pub fn add_var(&mut self, var_id: VarId, value: Value) {
576 for (id, val) in &mut self.vars {
578 if *id == var_id {
579 *val = value;
580 return;
581 }
582 }
583 self.vars.push((var_id, value));
584 }
585
586 pub fn get_var_mut(&mut self, var_id: VarId) -> Option<&mut Value> {
593 if let Some(pos) = self.vars.iter().position(|(id, _)| var_id == *id) {
595 return Some(&mut self.vars[pos].1);
596 }
597 if let Some(parent) = &self.parent_stack
599 && !self.parent_deletions.contains(&var_id)
600 {
601 let value = parent.lookup_var(var_id)?;
602 self.vars.push((var_id, value));
603 return self.vars.last_mut().map(|(_, val)| val);
604 }
605 None
606 }
607
608 pub fn upsert_var_cell_path(
613 &mut self,
614 var_id: VarId,
615 members: &[PathMember],
616 new_value: Value,
617 span: Span,
618 ) -> Result<(), ShellError> {
619 let value = self
620 .get_var_mut(var_id)
621 .ok_or(ShellError::VariableNotFoundAtRuntime { span })?;
622 value.upsert_data_at_cell_path(members, new_value)
623 }
624
625 pub fn remove_var(&mut self, var_id: VarId) {
626 for (idx, (id, _)) in self.vars.iter().enumerate() {
627 if *id == var_id {
628 self.vars.remove(idx);
629 break;
630 }
631 }
632 if self.parent_stack.is_some() {
635 self.parent_deletions.push(var_id);
636 }
637 self.deletions.push(var_id);
638 }
639
640 pub fn add_env_var(&mut self, var: String, value: Value) {
641 if let Some(last_overlay) = self.active_overlays.last().cloned() {
642 let env_name = EnvName::from(var);
643 self.clear_env_var_marks_in_active_overlay(&last_overlay, &env_name);
644
645 if let Some(scope) = self.env_vars.last_mut() {
646 let scope = Arc::make_mut(scope);
647 if let Some(env_vars) = scope.get_mut(&last_overlay) {
648 env_vars.insert(env_name, value);
649 } else {
650 scope.insert(last_overlay, [(env_name, value)].into_iter().collect());
651 }
652 } else {
653 self.env_vars.push(Arc::new(
654 [(last_overlay, [(env_name, value)].into_iter().collect())]
655 .into_iter()
656 .collect(),
657 ));
658 }
659 } else {
660 panic!("internal error: no active overlay");
662 }
663 }
664
665 fn clear_env_var_marks_in_active_overlay(&mut self, overlay: &str, env_name: &EnvName) {
666 if let Some(env_hidden) = Arc::make_mut(&mut self.env_hidden).get_mut(overlay) {
667 env_hidden.remove(env_name);
669 }
670
671 if let Some(hide_history) = Arc::make_mut(&mut self.env_hide_history).get_mut(overlay) {
672 hide_history.remove(env_name);
673 }
674 }
675
676 pub fn set_last_exit_code(&mut self, code: i32, span: Span) {
677 self.add_env_var("LAST_EXIT_CODE".into(), Value::int(code.into(), span));
678 }
679
680 pub fn set_last_error(&mut self, error: &ShellError) {
681 if let Some(code) = error.external_exit_code() {
682 self.set_last_exit_code(code.item, code.span);
683 } else if let Some(code) = error.exit_code() {
684 self.set_last_exit_code(code, Span::unknown());
685 }
686 }
687
688 pub fn last_overlay_name(&self) -> Result<String, ShellError> {
689 self.active_overlays
690 .last()
691 .cloned()
692 .ok_or_else(|| ShellError::NushellFailed {
693 msg: "No active overlay".into(),
694 })
695 }
696
697 pub fn captures_to_stack(&self, captures: Vec<(VarId, Value)>) -> Stack {
699 self.captures_to_stack_preserve_out_dest(captures)
700 .collect_value()
701 }
702
703 pub fn captures_to_stack_preserve_out_dest(&self, captures: Vec<(VarId, Value)>) -> Stack {
709 let mut env_vars = self.env_vars.clone();
710 env_vars.push(Arc::new(HashMap::new()));
711
712 Stack {
713 vars: captures,
714 env_vars,
715 env_hidden: self.env_hidden.clone(),
716 env_hide_history: self.env_hide_history.clone(),
717 active_overlays: self.active_overlays.clone(),
718 arguments: ArgumentStack::new(),
719 error_handlers: ErrorHandlerStack::new(),
720 finally_run_handlers: ErrorHandlerStack::new(),
721 recursion_count: self.recursion_count,
722 parent_stack: Some(Arc::new(self.clone())),
725 parent_deletions: vec![],
726 deletions: vec![],
727 config: self.config.clone(),
728 out_dest: self.out_dest.clone(),
729 suppress_stdin: self.suppress_stdin,
730 active_scope_bindings: self.active_scope_bindings.clone(),
732 ir_scope_regions: vec![],
733 ir_instruction_index: None,
734 last_result: self.last_result.clone(),
736 }
737 }
738
739 pub fn gather_captures(&self, engine_state: &EngineState, captures: &[(VarId, Span)]) -> Stack {
740 let mut vars = Vec::with_capacity(captures.len());
741
742 let fake_span = Span::new(0, 0);
743
744 for (capture, _) in captures {
745 if let Ok(value) = self.get_var(*capture, fake_span) {
748 vars.push((*capture, value));
749 } else if let Some(const_val) = &engine_state.get_var(*capture).const_val {
750 vars.push((*capture, const_val.clone()));
751 }
752 }
753
754 let mut env_vars = self.env_vars.clone();
755 env_vars.push(Arc::new(HashMap::new()));
756
757 Stack {
758 vars,
759 env_vars,
760 env_hidden: self.env_hidden.clone(),
761 env_hide_history: self.env_hide_history.clone(),
762 active_overlays: self.active_overlays.clone(),
763 arguments: ArgumentStack::new(),
764 error_handlers: ErrorHandlerStack::new(),
765 finally_run_handlers: ErrorHandlerStack::new(),
766 recursion_count: self.recursion_count,
767 parent_stack: Some(Arc::new(self.clone())),
768 parent_deletions: vec![],
769 deletions: vec![],
770 config: self.config.clone(),
771 out_dest: self.out_dest.clone(),
772 suppress_stdin: self.suppress_stdin,
773 active_scope_bindings: self.active_scope_bindings.clone(),
775 ir_scope_regions: vec![],
776 ir_instruction_index: None,
777 last_result: self.last_result.clone(),
779 }
780 }
781
782 pub fn get_env_vars(&self, engine_state: &EngineState) -> HashMap<String, Value> {
784 let mut result = HashMap::new();
785
786 for active_overlay in self.active_overlays.iter() {
787 if let Some(env_vars) = engine_state.env_vars.get(active_overlay) {
788 result.extend(
789 env_vars
790 .iter()
791 .filter(|(k, _)| {
792 if let Some(env_hidden) = self.env_hidden.get(active_overlay) {
793 !env_hidden.contains(*k)
794 } else {
795 true
797 }
798 })
799 .map(|(k, v)| (k.as_str().to_string(), v.clone()))
800 .collect::<HashMap<String, Value>>(),
801 );
802 }
803 }
804
805 result.extend(self.get_stack_env_vars());
806
807 result
808 }
809
810 pub fn get_stack_env_vars(&self) -> HashMap<String, Value> {
812 let mut result = HashMap::new();
813
814 for scope in &self.env_vars {
815 for active_overlay in self.active_overlays.iter() {
816 if let Some(env_vars) = scope.get(active_overlay) {
817 result.extend(
818 env_vars
819 .iter()
820 .map(|(k, v)| (k.as_str().to_string(), v.clone())),
821 );
822 }
823 }
824 }
825
826 result
827 }
828
829 pub fn get_stack_overlay_env_vars(&self, overlay_name: &str) -> HashMap<String, Value> {
831 let mut result = HashMap::new();
832
833 for scope in &self.env_vars {
834 if let Some(active_overlay) = self.active_overlays.iter().find(|n| n == &overlay_name)
835 && let Some(env_vars) = scope.get(active_overlay)
836 {
837 result.extend(
838 env_vars
839 .iter()
840 .map(|(k, v)| (k.as_str().to_string(), v.clone())),
841 );
842 }
843 }
844
845 result
846 }
847
848 pub fn get_hidden_env_vars(
850 &self,
851 excluded_overlay_name: &str,
852 engine_state: &EngineState,
853 ) -> HashMap<String, Value> {
854 let mut result = HashMap::new();
855
856 for overlay_name in self.active_overlays.iter().rev() {
857 if overlay_name == excluded_overlay_name {
858 continue;
859 }
860 if let Some(env_names) = self.env_hidden.get(overlay_name) {
861 for n in env_names {
862 if result.contains_key(n.as_str()) {
863 continue;
864 }
865 if let Some(Some(v)) = engine_state
867 .env_vars
868 .get(overlay_name)
869 .map(|env_vars| env_vars.get(n))
870 {
871 result.insert(n.as_str().to_string(), v.clone());
872 }
873 }
874 }
875 }
876 result
877 }
878
879 pub fn get_env_var_names(&self, engine_state: &EngineState) -> HashSet<String> {
881 let mut result = HashSet::new();
882
883 for active_overlay in self.active_overlays.iter() {
884 if let Some(env_vars) = engine_state.env_vars.get(active_overlay) {
885 result.extend(
886 env_vars
887 .keys()
888 .filter(|k| {
889 if let Some(env_hidden) = self.env_hidden.get(active_overlay) {
890 !env_hidden.contains(*k)
891 } else {
892 true
894 }
895 })
896 .map(|k| k.as_str().to_string())
897 .collect::<HashSet<String>>(),
898 );
899 }
900 }
901
902 for scope in &self.env_vars {
903 for active_overlay in self.active_overlays.iter() {
904 if let Some(env_vars) = scope.get(active_overlay) {
905 result.extend(
906 env_vars
907 .keys()
908 .map(|k| k.as_str().to_string())
909 .collect::<HashSet<String>>(),
910 );
911 }
912 }
913 }
914
915 result
916 }
917
918 pub fn get_env_var<'a>(
919 &'a self,
920 engine_state: &'a EngineState,
921 name: &str,
922 ) -> Option<&'a Value> {
923 let env_name = EnvName::from(name);
924
925 for scope in self.env_vars.iter().rev() {
926 for active_overlay in self.active_overlays.iter().rev() {
927 if let Some(env_vars) = scope.get(active_overlay)
928 && let Some(v) = env_vars.get(&env_name)
929 {
930 return Some(v);
931 }
932 }
933 }
934
935 for active_overlay in self.active_overlays.iter().rev() {
936 if !self.is_env_hidden_in_overlay(active_overlay, &env_name)
937 && let Some(env_vars) = engine_state.env_vars.get(active_overlay)
938 && let Some(v) = env_vars.get(&env_name)
939 {
940 return Some(v);
941 }
942 }
943 None
944 }
945
946 pub fn has_env_var(&self, engine_state: &EngineState, name: &str) -> bool {
947 let env_name = EnvName::from(name);
948
949 for scope in self.env_vars.iter().rev() {
950 for active_overlay in self.active_overlays.iter().rev() {
951 if let Some(env_vars) = scope.get(active_overlay)
952 && env_vars.contains_key(&env_name)
953 {
954 return true;
955 }
956 }
957 }
958
959 for active_overlay in self.active_overlays.iter().rev() {
960 if !self.is_env_hidden_in_overlay(active_overlay, &env_name)
961 && let Some(env_vars) = engine_state.env_vars.get(active_overlay)
962 && env_vars.contains_key(&env_name)
963 {
964 return true;
965 }
966 }
967
968 false
969 }
970
971 pub fn remove_env_var(&mut self, engine_state: &EngineState, name: &str) -> bool {
979 let env_name = EnvName::from(name);
980
981 self.remove_env_var_from_stack(&env_name)
982 || self.hide_engine_state_env_var(engine_state, &env_name)
983 }
984
985 fn remove_env_var_from_stack(&mut self, env_name: &EnvName) -> bool {
988 self.env_vars
989 .iter_mut()
990 .rev()
991 .map(Arc::make_mut)
992 .find_map(|scope| {
993 self.active_overlays
994 .iter()
995 .rev()
996 .find_map(|active_overlay| scope.get_mut(active_overlay)?.remove(env_name))
997 })
998 .is_some()
999 }
1000
1001 fn hide_engine_state_env_var(
1006 &mut self,
1007 engine_state: &EngineState,
1008 env_name: &EnvName,
1009 ) -> bool {
1010 let overlay_containing_env_var = self.active_overlays.iter().rev().find(|active_overlay| {
1011 engine_state
1012 .env_vars
1013 .get(active_overlay.as_str())
1014 .is_some_and(|env_vars| env_vars.contains_key(env_name))
1015 });
1016
1017 let Some(overlay_containing_env_var) = overlay_containing_env_var else {
1018 return false;
1019 };
1020
1021 let env_hidden = Arc::make_mut(&mut self.env_hidden);
1022
1023 if env_hidden
1024 .get(overlay_containing_env_var.as_str())
1025 .is_some_and(|hidden_vars| hidden_vars.contains(env_name))
1026 {
1027 return false;
1028 }
1029
1030 env_hidden
1031 .entry(overlay_containing_env_var.clone())
1032 .or_default()
1033 .insert(env_name.clone());
1034 true
1035 }
1036
1037 fn record_env_var_hide_in_active_overlay(&mut self, env_name: &EnvName) -> bool {
1040 let Some(active_overlay) = self.active_overlays.last().cloned() else {
1041 return false;
1042 };
1043
1044 Arc::make_mut(&mut self.env_hide_history)
1045 .entry(active_overlay)
1046 .or_default()
1047 .insert(env_name.clone())
1048 }
1049
1050 fn is_env_var_hide_recorded(&self, env_name: &EnvName) -> bool {
1051 self.active_overlays
1052 .iter()
1053 .rev()
1054 .filter_map(|overlay| self.env_hide_history.get(overlay))
1055 .any(|hidden_vars| hidden_vars.contains(env_name))
1056 }
1057
1058 fn is_env_hidden_in_overlay(&self, overlay: &str, env_name: &EnvName) -> bool {
1059 self.env_hidden
1060 .get(overlay)
1061 .is_some_and(|hidden_vars| hidden_vars.contains(env_name))
1062 }
1063
1064 pub fn is_env_var_hidden(&self, name: &str) -> bool {
1070 let env_name = EnvName::from(name);
1071
1072 self.active_overlays
1073 .iter()
1074 .rev()
1075 .any(|overlay| self.is_env_hidden_in_overlay(overlay, &env_name))
1076 || self.is_env_var_hide_recorded(&env_name)
1077 }
1078
1079 pub fn hide_env_var(&mut self, engine_state: &EngineState, name: &str) -> bool {
1087 let env_name = EnvName::from(name);
1088
1089 if self.is_env_var_hide_recorded(&env_name) {
1091 return false;
1092 }
1093
1094 if self.remove_env_var_from_stack(&env_name) {
1095 self.record_env_var_hide_in_active_overlay(&env_name);
1096
1097 if !self.has_env_var_in_stack(&env_name) {
1098 self.hide_engine_state_env_var(engine_state, &env_name);
1099 }
1100 return true;
1101 }
1102
1103 if self.hide_engine_state_env_var(engine_state, &env_name) {
1104 self.record_env_var_hide_in_active_overlay(&env_name);
1105 return true;
1106 }
1107
1108 false
1109 }
1110
1111 fn has_env_var_in_stack(&self, name: &EnvName) -> bool {
1113 self.env_vars.iter().rev().any(|scope| {
1114 self.active_overlays
1115 .iter()
1116 .rev()
1117 .filter_map(|active_overlay| scope.get(active_overlay))
1118 .any(|env_vars| env_vars.contains_key(name))
1119 })
1120 }
1121
1122 pub fn has_env_overlay(&self, name: &str, engine_state: &EngineState) -> bool {
1123 for scope in self.env_vars.iter().rev() {
1124 if scope.contains_key(name) {
1125 return true;
1126 }
1127 }
1128
1129 engine_state.env_vars.contains_key(name)
1130 }
1131
1132 pub fn is_overlay_active(&self, name: &str) -> bool {
1133 self.active_overlays.iter().any(|n| n == name)
1134 }
1135
1136 pub fn add_overlay(&mut self, name: String) {
1137 self.active_overlays.retain(|o| o != &name);
1138 self.active_overlays.push(name);
1139 }
1140
1141 pub fn remove_overlay(&mut self, name: &str) {
1142 self.active_overlays.retain(|o| o != name);
1143 }
1144
1145 pub fn stdout(&self) -> &OutDest {
1151 self.out_dest.stdout()
1152 }
1153
1154 pub fn stderr(&self) -> &OutDest {
1160 self.out_dest.stderr()
1161 }
1162
1163 pub fn pipe_stdout(&self) -> Option<&OutDest> {
1165 self.out_dest.pipe_stdout.as_ref()
1166 }
1167
1168 pub fn pipe_stderr(&self) -> Option<&OutDest> {
1170 self.out_dest.pipe_stderr.as_ref()
1171 }
1172
1173 pub fn invocation_stdout(&self) -> Option<&OutDest> {
1181 self.out_dest.invocation_stdout.last()
1182 }
1183
1184 #[must_use]
1193 pub fn is_stdout_redirected(&self) -> bool {
1194 self.invocation_stdout()
1195 .unwrap_or_else(|| self.stdout())
1196 .is_redirected()
1197 }
1198
1199 pub fn with_invocation_stdout(self, dest: OutDest) -> StackWithInvocation {
1211 StackWithInvocation::new(self, dest)
1212 }
1213
1214 pub fn start_collect_value(&mut self) -> StackCollectValueGuard<'_> {
1218 StackCollectValueGuard::new(self)
1219 }
1220
1221 pub fn use_call_arg_out_dest(&mut self) -> StackCallArgGuard<'_> {
1225 StackCallArgGuard::new(self)
1226 }
1227
1228 pub fn push_redirection(
1230 &mut self,
1231 stdout: Option<Redirection>,
1232 stderr: Option<Redirection>,
1233 ) -> StackIoGuard<'_> {
1234 StackIoGuard::new(self, stdout, stderr)
1235 }
1236
1237 pub fn collect_value(mut self) -> Self {
1244 self.out_dest.pipe_stdout = Some(OutDest::Value);
1245 self.out_dest.pipe_stderr = None;
1246 self
1247 }
1248
1249 pub fn capture_all(mut self) -> Self {
1258 self.out_dest.pipe_stdout = Some(OutDest::Value);
1259 self.out_dest.pipe_stderr = Some(OutDest::Value);
1260 self
1261 }
1262
1263 pub fn reset_out_dest(mut self) -> Self {
1268 self.out_dest = StackOutDest::new();
1269 self
1270 }
1271
1272 pub fn suppress_output(mut self) -> Self {
1277 self.out_dest.stdout = OutDest::Null;
1278 self.out_dest.stderr = OutDest::Null;
1279 self
1280 }
1281
1282 pub fn suppress_stdin(mut self) -> Self {
1291 self.suppress_stdin = true;
1292 self
1293 }
1294
1295 pub fn reset_pipes(mut self) -> Self {
1300 self.out_dest.pipe_stdout = None;
1301 self.out_dest.pipe_stderr = None;
1302 self
1303 }
1304
1305 pub fn stdout_file(mut self, file: File) -> Self {
1344 self.out_dest.stdout = OutDest::File(Arc::new(file));
1345 self
1346 }
1347
1348 pub fn stderr_file(mut self, file: File) -> Self {
1352 self.out_dest.stderr = OutDest::File(Arc::new(file));
1353 self
1354 }
1355
1356 pub fn set_cwd(&mut self, path: impl AsRef<std::path::Path>) -> Result<(), ShellError> {
1361 fn error(msg: &str) -> Result<(), ShellError> {
1364 Err(ShellError::Generic(GenericError::new_internal(
1365 msg.to_string(),
1366 "",
1367 )))
1368 }
1369
1370 let path = path.as_ref();
1371
1372 if !path.is_absolute() {
1373 if let Some(Component::Prefix(_)) = path.components().next() {
1374 return Err(ShellError::Generic(
1375 GenericError::new_internal("Cannot set $env.PWD to a prefix-only path", "")
1376 .with_help(format!(
1377 "Try to use {}{MAIN_SEPARATOR} instead",
1378 path.display()
1379 )),
1380 ));
1381 }
1382
1383 error("Cannot set $env.PWD to a non-absolute path")
1384 } else if !path.exists() {
1385 error("Cannot set $env.PWD to a non-existent directory")
1386 } else if !path.is_dir() {
1387 error("Cannot set $env.PWD to a non-directory")
1388 } else {
1389 let path = nu_path::strip_trailing_slash(path);
1391 let value = Value::string(path.to_string_lossy(), Span::unknown());
1392 self.add_env_var("PWD".into(), value);
1393 Ok(())
1394 }
1395 }
1396}
1397
1398#[cfg(test)]
1399mod test {
1400 use std::sync::Arc;
1401
1402 use crate::{Span, Value, VarId, engine::EngineState};
1403
1404 use super::Stack;
1405
1406 #[test]
1407 fn test_children_see_inner_values() {
1408 let mut original = Stack::new();
1409 original.add_var(VarId::new(0), Value::test_string("hello"));
1410
1411 let cloned = Stack::with_parent(Arc::new(original));
1412 assert_eq!(
1413 cloned.get_var(VarId::new(0), Span::test_data()),
1414 Ok(Value::test_string("hello"))
1415 );
1416 }
1417
1418 #[test]
1419 fn test_children_dont_see_deleted_values() {
1420 let mut original = Stack::new();
1421 original.add_var(VarId::new(0), Value::test_string("hello"));
1422
1423 let mut cloned = Stack::with_parent(Arc::new(original));
1424 cloned.remove_var(VarId::new(0));
1425
1426 assert_eq!(
1427 cloned.get_var(VarId::new(0), Span::test_data()),
1428 Err(crate::ShellError::VariableNotFoundAtRuntime {
1429 span: Span::test_data()
1430 })
1431 );
1432 }
1433
1434 #[test]
1435 fn test_children_changes_override_parent() {
1436 let mut original = Stack::new();
1437 original.add_var(VarId::new(0), Value::test_string("hello"));
1438
1439 let mut cloned = Stack::with_parent(Arc::new(original));
1440 cloned.add_var(VarId::new(0), Value::test_string("there"));
1441 assert_eq!(
1442 cloned.get_var(VarId::new(0), Span::test_data()),
1443 Ok(Value::test_string("there"))
1444 );
1445
1446 cloned.remove_var(VarId::new(0));
1447 assert_eq!(
1449 cloned.get_var(VarId::new(0), Span::test_data()),
1450 Err(crate::ShellError::VariableNotFoundAtRuntime {
1451 span: Span::test_data()
1452 })
1453 );
1454 }
1455 #[test]
1456 fn test_children_changes_persist_in_offspring() {
1457 let mut original = Stack::new();
1458 original.add_var(VarId::new(0), Value::test_string("hello"));
1459
1460 let mut cloned = Stack::with_parent(Arc::new(original));
1461 cloned.add_var(VarId::new(1), Value::test_string("there"));
1462
1463 cloned.remove_var(VarId::new(0));
1464 let cloned = Stack::with_parent(Arc::new(cloned));
1465
1466 assert_eq!(
1467 cloned.get_var(VarId::new(0), Span::test_data()),
1468 Err(crate::ShellError::VariableNotFoundAtRuntime {
1469 span: Span::test_data()
1470 })
1471 );
1472
1473 assert_eq!(
1474 cloned.get_var(VarId::new(1), Span::test_data()),
1475 Ok(Value::test_string("there"))
1476 );
1477 }
1478
1479 #[test]
1480 fn test_merging_children_back_to_parent() {
1481 let mut original = Stack::new();
1482 let engine_state = EngineState::new();
1483 original.add_var(VarId::new(0), Value::test_string("hello"));
1484
1485 let original_arc = Arc::new(original);
1486 let mut cloned = Stack::with_parent(original_arc.clone());
1487 cloned.add_var(VarId::new(1), Value::test_string("there"));
1488
1489 cloned.remove_var(VarId::new(0));
1490
1491 cloned.add_env_var(
1492 "ADDED_IN_CHILD".to_string(),
1493 Value::test_string("New Env Var"),
1494 );
1495
1496 let original = Stack::with_changes_from_child(original_arc, cloned);
1497
1498 assert_eq!(
1499 original.get_var(VarId::new(0), Span::test_data()),
1500 Err(crate::ShellError::VariableNotFoundAtRuntime {
1501 span: Span::test_data()
1502 })
1503 );
1504
1505 assert_eq!(
1506 original.get_var(VarId::new(1), Span::test_data()),
1507 Ok(Value::test_string("there"))
1508 );
1509
1510 assert_eq!(
1511 original
1512 .get_env_var(&engine_state, "ADDED_IN_CHILD")
1513 .cloned(),
1514 Some(Value::test_string("New Env Var")),
1515 );
1516 }
1517
1518 #[test]
1519 fn test_get_var_mut_local_in_place() {
1520 use crate::ast::PathMember;
1521 use crate::casing::Casing;
1522 use crate::record;
1523
1524 let mut stack = Stack::new();
1525 let var_id = VarId::new(0);
1526 stack.add_var(
1527 var_id,
1528 Value::test_record(record! { "a" => Value::test_int(1) }),
1529 );
1530
1531 let path = vec![PathMember::test_string("a", false, Casing::Sensitive)];
1532 stack
1533 .upsert_var_cell_path(var_id, &path, Value::test_int(2), Span::test_data())
1534 .expect("upsert should succeed");
1535
1536 assert_eq!(
1537 stack.get_var(var_id, Span::test_data()),
1538 Ok(Value::test_record(record! { "a" => Value::test_int(2) }))
1539 );
1540 assert_eq!(stack.vars.len(), 1);
1542 }
1543
1544 #[test]
1545 fn test_get_var_mut_pulls_from_parent() {
1546 use crate::ast::PathMember;
1547 use crate::casing::Casing;
1548 use crate::record;
1549
1550 let mut parent = Stack::new();
1551 let var_id = VarId::new(0);
1552 parent.add_var(
1553 var_id,
1554 Value::test_record(record! { "a" => Value::test_int(1) }),
1555 );
1556
1557 let mut child = Stack::with_parent(Arc::new(parent));
1558 assert!(child.vars.is_empty());
1559
1560 let path = vec![PathMember::test_string("a", false, Casing::Sensitive)];
1561 child
1562 .upsert_var_cell_path(var_id, &path, Value::test_int(9), Span::test_data())
1563 .expect("upsert should succeed");
1564
1565 assert_eq!(child.vars.len(), 1);
1567 assert_eq!(
1568 child.get_var(var_id, Span::test_data()),
1569 Ok(Value::test_record(record! { "a" => Value::test_int(9) }))
1570 );
1571
1572 child
1574 .upsert_var_cell_path(var_id, &path, Value::test_int(10), Span::test_data())
1575 .expect("second upsert should succeed");
1576 assert_eq!(child.vars.len(), 1);
1577 assert_eq!(
1578 child.get_var(var_id, Span::test_data()),
1579 Ok(Value::test_record(record! { "a" => Value::test_int(10) }))
1580 );
1581 }
1582
1583 #[test]
1584 fn test_upsert_var_cell_path_missing_and_deleted() {
1585 use crate::ast::PathMember;
1586 use crate::casing::Casing;
1587
1588 let mut stack = Stack::new();
1589 let var_id = VarId::new(0);
1590 let path = vec![PathMember::test_string("a", false, Casing::Sensitive)];
1591
1592 assert!(matches!(
1593 stack.upsert_var_cell_path(var_id, &path, Value::test_int(1), Span::test_data()),
1594 Err(crate::ShellError::VariableNotFoundAtRuntime { .. })
1595 ));
1596
1597 let mut parent = Stack::new();
1598 parent.add_var(var_id, Value::test_int(1));
1599 let mut child = Stack::with_parent(Arc::new(parent));
1600 child.remove_var(var_id);
1601
1602 assert!(matches!(
1603 child.upsert_var_cell_path(var_id, &path, Value::test_int(2), Span::test_data()),
1604 Err(crate::ShellError::VariableNotFoundAtRuntime { .. })
1605 ));
1606 assert!(child.get_var_mut(var_id).is_none());
1607 }
1608}