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