1#[allow(deprecated)]
2use crate::get_full_help;
3use crate::named_flags::{
4 expand_flag_record, flag_type_accepts_nothing, list_spread_before_required_error,
5};
6use crate::{EvalBlockWithEarlyReturnFn, eval_ir::eval_ir_block};
7use nu_protocol::{
8 BlockId, CompareTypes, Config, ENV_VARIABLE_ID, IntoPipelineData, PipelineData,
9 PipelineExecutionData, ShellError, Signature, Span, Value, VarId,
10 ast::{
11 Argument as AstArgument, Assignment, Block, Call, Expr, Expression, ExternalArgument,
12 PathMember,
13 },
14 debugger::{DebugContext, WithDebug, WithoutDebug},
15 engine::{Argument as EngineArgument, Closure, EngineState, EnvName, EnvVars, Stack},
16 eval_base::Eval,
17 shell_error::generic::GenericError,
18};
19use nu_utils::IgnoreCaseExt;
20use std::borrow::Cow;
21use std::collections::{HashMap, HashSet};
22use std::sync::Arc;
23
24#[derive(Clone)]
36pub struct CallEval {
37 callee_stack: Stack,
38 head_span: Span,
39 callee_span: Span,
40 arg_index: usize,
41 named_args: Vec<String>,
42 rest_args: Vec<Value>,
43 always_spread: bool,
45 eval: EvalBlockWithEarlyReturnFn,
46}
47
48impl CallEval {
49 pub fn new(
51 callee_stack: Stack,
52 call_head: Span,
53 callee_span: Span,
54 eval: EvalBlockWithEarlyReturnFn,
55 ) -> Self {
56 Self {
57 callee_stack,
58 head_span: call_head,
59 callee_span,
60 arg_index: 0,
61 named_args: Vec::new(),
62 rest_args: Vec::new(),
63 always_spread: false,
64 eval,
65 }
66 }
67
68 pub fn add_positional(
73 &mut self,
74 signature: &Signature,
75 value: Cow<Value>,
76 ) -> Result<&mut Self, ShellError> {
77 if self.always_spread {
79 return self.push_rest(signature, value);
80 }
81
82 let maybe_param = match self
83 .arg_index
84 .checked_sub(signature.required_positional.len())
85 {
86 None => signature.required_positional.get(self.arg_index),
88 Some(opt_idx) if opt_idx < signature.optional_positional.len() => {
90 signature.optional_positional.get(opt_idx)
91 }
92 _ => None,
94 };
95
96 if let Some(param) = maybe_param {
97 let param_type = param.shape.to_type();
98 if !value.is_subtype_of(¶m_type) {
99 return Err(ShellError::CantConvert {
100 to_type: param_type.to_string(),
101 from_type: value.get_type().to_string(),
102 span: value.span(),
103 help: None,
104 });
105 }
106
107 let var_id = param
108 .var_id
109 .expect("internal error: all custom parameters must have var_ids");
110 self.callee_stack.add_var(var_id, value.into_owned());
111 self.arg_index += 1;
112 Ok(self)
113 } else {
114 self.push_rest(signature, value)
115 }
116 }
117
118 fn push_rest(
119 &mut self,
120 signature: &Signature,
121 value: Cow<Value>,
122 ) -> Result<&mut Self, ShellError> {
123 let Some(rest_positional) = &signature.rest_positional else {
124 return Ok(self);
128 };
129
130 let param_type = rest_positional.shape.to_type();
131 if !value.is_subtype_of(¶m_type) {
132 return Err(ShellError::CantConvert {
133 to_type: param_type.to_string(),
134 from_type: value.get_type().to_string(),
135 span: value.span(),
136 help: None,
137 });
138 }
139
140 self.rest_args.push(value.into_owned());
141 Ok(self)
142 }
143
144 pub fn add_list_spread(
146 &mut self,
147 signature: &Signature,
148 vals: Vec<Value>,
149 spread_span: Span,
150 ) -> Result<&mut Self, ShellError> {
151 if self.arg_index < signature.required_positional.len() && !self.always_spread {
152 return Err(list_spread_before_required_error(spread_span));
153 }
154 for v in vals {
155 self.push_rest(signature, Cow::Owned(v))?;
156 }
157 self.always_spread = true;
158 Ok(self)
159 }
160
161 pub fn add_null_spread(
163 &mut self,
164 signature: &Signature,
165 spread_span: Span,
166 ) -> Result<&mut Self, ShellError> {
167 if self.arg_index < signature.required_positional.len() && !self.always_spread {
168 return Err(list_spread_before_required_error(spread_span));
169 }
170 self.always_spread = true;
171 Ok(self)
172 }
173
174 pub fn add_named(
176 &mut self,
177 signature: &Signature,
178 long: &str,
179 short: Option<String>,
180 value: Option<Cow<Value>>,
181 ) -> Result<&mut Self, ShellError> {
182 let named = signature.named.iter().find(|named| {
183 long == named.long
184 || short
185 .as_deref()
186 .zip(named.short)
187 .is_some_and(|(arg, param)| {
188 let mut buf = [0; 4];
189 param.encode_utf8(&mut buf) == arg
190 })
191 });
192
193 if let Some(named) = named {
194 let var_id = named
195 .var_id
196 .expect("internal error: all custom parameters must have var_ids");
197
198 let value = value
199 .or_else(|| named.default_value.as_ref().map(Cow::Borrowed))
200 .unwrap_or_else(|| Cow::Owned(Value::bool(true, self.head_span)));
201
202 self.callee_stack.add_var(var_id, value.into_owned());
203 self.named_args.push(long.to_string());
204 }
205
206 Ok(self)
207 }
208
209 pub fn with_env(
211 &mut self,
212 env_vars: &[Arc<EnvVars>],
213 env_hidden: &Arc<HashMap<String, HashSet<EnvName>>>,
214 ) -> &mut Self {
215 self.callee_stack.with_env(env_vars, env_hidden);
216 self
217 }
218
219 pub fn debug(&mut self, debug: bool) -> &mut Self {
221 if debug {
222 self.eval = eval_block_with_early_return::<WithDebug>
223 } else {
224 self.eval = eval_block_with_early_return::<WithoutDebug>
225 };
226 self
227 }
228
229 pub fn run(
231 &mut self,
232 engine_state: &EngineState,
233 block: &Block,
234 input: PipelineData,
235 ) -> Result<PipelineData, ShellError> {
236 self.finalize_arguments(&block.signature)?;
237 self.arg_index = 0;
238 self.rest_args.clear();
239 (self.eval)(engine_state, &mut self.callee_stack, block, input).map(|p| p.body)
240 }
241
242 pub fn finalize_for_signature(
247 &mut self,
248 signature: &Signature,
249 ) -> Result<&mut Self, ShellError> {
250 self.finalize_arguments(signature)?;
251 Ok(self)
252 }
253
254 pub fn run_prebound(
256 &mut self,
257 engine_state: &EngineState,
258 block: &Block,
259 input: PipelineData,
260 ) -> Result<PipelineData, ShellError> {
261 self.arg_index = 0;
262 self.rest_args.clear();
263 (self.eval)(engine_state, &mut self.callee_stack, block, input).map(|p| p.body)
264 }
265
266 pub fn redirect_env(&self, engine_state: &EngineState, stack: &mut Stack) {
268 redirect_env(engine_state, stack, &self.callee_stack);
269 }
270
271 fn finalize_arguments(&mut self, signature: &Signature) -> Result<(), ShellError> {
274 let remaining_positionals = signature
275 .required_positional
276 .iter()
277 .map(|p| (p, true))
278 .chain(signature.optional_positional.iter().map(|p| (p, false)))
279 .skip(self.arg_index);
281
282 for (param, required) in remaining_positionals {
283 let var_id = param
284 .var_id
285 .expect("internal error: all custom parameters must have var_ids");
286
287 let maybe_value = param
288 .default_value
289 .clone()
290 .or((!required).then_some(Value::nothing(self.callee_span)));
291
292 if let Some(value) = maybe_value {
293 self.callee_stack.add_var(var_id, value);
294 } else {
295 return Err(ShellError::MissingParameter {
296 param_name: param.name.to_string(),
297 span: self.callee_span,
298 });
299 }
300 }
301
302 if let Some(rest_positional) = &signature.rest_positional {
303 let span = self
304 .rest_args
305 .first()
306 .map(|x| x.span())
307 .unwrap_or(self.callee_span);
308
309 self.callee_stack.add_var(
310 rest_positional
311 .var_id
312 .expect("Internal error: rest positional parameter lackes var_id"),
313 Value::list(self.rest_args.to_owned(), span),
314 );
315 }
316
317 let remaining_flags = signature
318 .named
319 .iter()
320 .filter(|flag| !self.named_args.contains(&flag.long))
322 .filter_map(|flag| Some((flag.var_id?, flag)));
325
326 for (var_id, flag) in remaining_flags {
327 if flag.arg.is_none() {
328 self.callee_stack
329 .add_var(var_id, Value::bool(false, self.head_span));
330 } else {
331 let value = flag
332 .default_value
333 .clone()
334 .unwrap_or(Value::nothing(self.head_span));
335 self.callee_stack.add_var(var_id, value);
336 }
337 }
338
339 Ok(())
340 }
341}
342
343pub fn eval_call<D: DebugContext>(
345 engine_state: &EngineState,
346 caller_stack: &mut Stack,
347 call: &Call,
348 input: PipelineData,
349) -> Result<PipelineData, ShellError> {
350 engine_state.signals().check(&call.head)?;
351 let decl = engine_state.get_decl(call.decl_id);
352
353 if !decl.is_known_external() && call.named_iter().any(|(flag, _, _)| flag.item == "help") {
354 let help = get_full_help(decl, engine_state, caller_stack, call.head);
355 Ok(Value::string(help, call.head).into_pipeline_data())
356 } else if let Some(block_id) = decl.block_id() {
357 let block = engine_state.get_block(block_id);
359 let mut callee_stack = caller_stack.gather_captures(engine_state, &block.captures);
360
361 let maximum_call_stack_depth: u64 = engine_state.config.recursion_limit as u64;
367 callee_stack.recursion_count += 1;
368 if callee_stack.recursion_count > maximum_call_stack_depth {
369 callee_stack.recursion_count = 0;
370 return Err(ShellError::RecursionLimitReached {
371 recursion_limit: maximum_call_stack_depth,
372 span: block.span,
373 });
374 }
375
376 let mut call_eval = CallEval::new(
377 callee_stack,
378 call.head,
379 block.span.unwrap_or(Span::unknown()),
380 eval_block_with_early_return::<D>,
381 );
382 let signature = decl.signature();
383 for arg in &call.arguments {
386 match arg {
387 AstArgument::Positional(expr) | AstArgument::Unknown(expr) => {
388 let result = eval_expression::<D>(engine_state, caller_stack, expr)?;
389 call_eval.add_positional(&signature, Cow::Owned(result))?;
390 }
391 AstArgument::Named((long, short, maybe_expr)) => {
392 let result: Option<Cow<Value>> = if let Some(expr) = maybe_expr {
393 let value = eval_expression::<D>(engine_state, caller_stack, expr)?;
394 if value.is_nothing() {
395 let accepts_nothing = signature
396 .get_long_flag(&long.item)
397 .or_else(|| {
398 short
399 .as_ref()
400 .and_then(|s| s.item.chars().next())
401 .and_then(|c| signature.get_short_flag(c))
402 })
403 .is_some_and(|flag| flag_type_accepts_nothing(&flag));
404 if !accepts_nothing {
405 continue;
406 }
407 }
408 Some(Cow::Owned(value))
409 } else {
410 None
411 };
412 call_eval.add_named(
413 &signature,
414 &long.item,
415 short.as_ref().map(|s| s.item.clone()),
416 result,
417 )?;
418 }
419 AstArgument::Spread(expr) => {
423 let val = eval_expression::<D>(engine_state, caller_stack, expr)?;
424 match val {
425 Value::Record { val, .. } => {
426 for engine_arg in
427 expand_flag_record(&signature, val.into_owned(), expr.span)?
428 {
429 match engine_arg {
430 EngineArgument::Flag { data, name, .. } => {
431 let long =
432 std::str::from_utf8(&data[name]).unwrap_or_default();
433 call_eval.add_named(&signature, long, None, None)?;
434 }
435 EngineArgument::Named {
436 data, name, val, ..
437 } => {
438 let long =
439 std::str::from_utf8(&data[name]).unwrap_or_default();
440 call_eval.add_named(
441 &signature,
442 long,
443 None,
444 Some(Cow::Owned(val)),
445 )?;
446 }
447 _ => {}
448 }
449 }
450 }
451 Value::List { vals, .. } => {
452 if signature.rest_positional.is_none() && !signature.allows_unknown_args
453 {
454 return Err(ShellError::Generic(GenericError::new(
455 "Cannot spread a list into this command",
456 "This command has no ...rest parameter to receive a list spread. Use a record to spread named flags, e.g. ...{flag: value}",
457 expr.span,
458 )));
459 }
460 call_eval.add_list_spread(&signature, vals.into_owned(), expr.span)?;
461 }
462 Value::Nothing { .. } => {
463 call_eval.add_null_spread(&signature, expr.span)?;
464 }
465 Value::Error { error, .. } => return Err(*error),
466 other => {
467 return Err(ShellError::CannotSpreadAsList { span: other.span() });
468 }
469 }
470 }
471 }
472 }
473
474 let result = call_eval.run(engine_state, block, input);
475
476 if block.redirect_env {
477 call_eval.redirect_env(engine_state, caller_stack);
478 }
479
480 result
481 } else {
482 decl.run(engine_state, caller_stack, &call.into(), input)
486 }
487}
488
489pub fn redirect_env(engine_state: &EngineState, caller_stack: &mut Stack, callee_stack: &Stack) {
491 let caller_env_vars = caller_stack.get_env_var_names(engine_state);
493
494 for var in caller_env_vars.iter() {
497 if !callee_stack.has_env_var(engine_state, var) {
498 caller_stack.hide_env_var(engine_state, var);
499 }
500 }
501
502 for (var, value) in callee_stack.get_stack_env_vars() {
504 caller_stack.add_env_var(var, value);
505 }
506
507 caller_stack.config.clone_from(&callee_stack.config);
509}
510
511fn eval_external(
512 engine_state: &EngineState,
513 stack: &mut Stack,
514 head: &Expression,
515 args: &[ExternalArgument],
516 input: PipelineData,
517) -> Result<PipelineData, ShellError> {
518 let decl_id = engine_state
519 .find_decl("run-external".as_bytes(), &[])
520 .ok_or(ShellError::ExternalNotSupported {
521 span: head.span(&engine_state),
522 })?;
523
524 let command = engine_state.get_decl(decl_id);
525
526 let mut call = Call::new(head.span(&engine_state));
527
528 call.add_positional(head.clone());
529
530 for arg in args {
531 match arg {
532 ExternalArgument::Regular(expr) => call.add_positional(expr.clone()),
533 ExternalArgument::Spread(expr) => call.add_spread(expr.clone()),
534 }
535 }
536
537 command.run(engine_state, stack, &(&call).into(), input)
538}
539
540pub fn eval_expression<D: DebugContext>(
541 engine_state: &EngineState,
542 stack: &mut Stack,
543 expr: &Expression,
544) -> Result<Value, ShellError> {
545 let stack = &mut stack.start_collect_value();
546 <EvalRuntime as Eval>::eval::<D>(engine_state, stack, expr)
547}
548
549pub fn eval_expression_with_input<D: DebugContext>(
556 engine_state: &EngineState,
557 stack: &mut Stack,
558 expr: &Expression,
559 mut input: PipelineData,
560) -> Result<PipelineData, ShellError> {
561 match &expr.expr {
562 Expr::Call(call) => {
563 input = eval_call::<D>(engine_state, stack, call, input)?;
564 }
565 Expr::ExternalCall(head, args) => {
566 input = eval_external(engine_state, stack, head, args, input)?;
567 }
568
569 Expr::Collect(var_id, expr) => {
570 input = eval_collect::<D>(engine_state, stack, *var_id, expr, input)?;
571 }
572
573 Expr::Subexpression(block_id) => {
574 let block = engine_state.get_block(*block_id);
575 input = eval_subexpression::<D>(engine_state, stack, block, input)?;
577 }
578
579 Expr::FullCellPath(full_cell_path) => match &full_cell_path.head {
580 Expression {
581 expr: Expr::Subexpression(block_id),
582 span,
583 ..
584 } => {
585 let block = engine_state.get_block(*block_id);
586
587 if !full_cell_path.tail.is_empty() {
588 let stack = &mut stack.start_collect_value();
589 input = eval_subexpression::<D>(engine_state, stack, block, input)?
591 .into_value(*span)?
592 .follow_cell_path(&full_cell_path.tail)?
593 .into_owned()
594 .into_pipeline_data()
595 } else {
596 input = eval_subexpression::<D>(engine_state, stack, block, input)?;
597 }
598 }
599 _ => {
600 let input_value = input.into_value(expr.span)?;
601 stack.add_var(nu_protocol::IN_VARIABLE_ID, input_value);
602 input = eval_expression::<D>(engine_state, stack, expr)?.into_pipeline_data();
603 }
604 },
605
606 Expr::StringInterpolation(_) | Expr::GlobInterpolation(_, _) => {
607 let input_value = input.into_value(expr.span)?;
608 stack.add_var(nu_protocol::IN_VARIABLE_ID, input_value);
609 let value = eval_expression::<D>(engine_state, stack, expr)?;
610 input = PipelineData::Value(value, None);
611 }
612
613 _ => {
614 let input_value = input.into_value(expr.span)?;
615 stack.add_var(nu_protocol::IN_VARIABLE_ID, input_value);
616 let value = eval_expression::<D>(engine_state, stack, expr)?;
617 input = PipelineData::Value(value, None);
618 }
619 };
620
621 Ok(input)
622}
623
624pub fn eval_block<D: DebugContext>(
625 engine_state: &EngineState,
626 stack: &mut Stack,
627 block: &Block,
628 input: PipelineData,
629) -> Result<PipelineExecutionData, ShellError> {
630 let result = eval_ir_block::<D>(engine_state, stack, block, input);
631 if let Err(err) = &result {
632 stack.set_last_error(err);
633 }
634 result
635}
636
637pub fn eval_block_with_early_return<D: DebugContext>(
655 engine_state: &EngineState,
656 stack: &mut Stack,
657 block: &Block,
658 input: PipelineData,
659) -> Result<PipelineExecutionData, ShellError> {
660 let mut result = eval_block::<D>(engine_state, stack, block, input)?;
661 result.early_return = false;
662 Ok(result)
663}
664
665pub fn eval_collect<D: DebugContext>(
666 engine_state: &EngineState,
667 stack: &mut Stack,
668 var_id: VarId,
669 expr: &Expression,
670 mut input: PipelineData,
671) -> Result<PipelineData, ShellError> {
672 let span = input.span().unwrap_or(expr.span);
674
675 let metadata = input.take_metadata().and_then(|m| m.for_collect());
676
677 let input = input.into_value(span)?;
678
679 stack.add_var(var_id, input.clone());
680
681 let result = eval_expression_with_input::<D>(
682 engine_state,
683 stack,
684 expr,
685 input.into_pipeline_data_with_metadata(metadata),
687 );
688
689 stack.remove_var(var_id);
690
691 result
692}
693
694pub fn eval_subexpression<D: DebugContext>(
695 engine_state: &EngineState,
696 stack: &mut Stack,
697 block: &Block,
698 input: PipelineData,
699) -> Result<PipelineData, ShellError> {
700 eval_block::<D>(engine_state, stack, block, input).map(|p| p.body)
701}
702
703pub fn eval_variable(
704 engine_state: &EngineState,
705 stack: &Stack,
706 var_id: VarId,
707 span: Span,
708) -> Result<Value, ShellError> {
709 match var_id {
710 nu_protocol::NU_VARIABLE_ID => {
712 if let Some(val) = engine_state.get_constant(var_id) {
713 Ok(val.clone())
714 } else {
715 Err(ShellError::VariableNotFoundAtRuntime { span })
716 }
717 }
718 ENV_VARIABLE_ID => {
720 let env_vars = stack.get_env_vars(engine_state);
721 let env_columns = env_vars.keys();
722 let env_values = env_vars.values();
723
724 let mut pairs = env_columns
725 .map(|x| x.to_string())
726 .zip(env_values.cloned())
727 .collect::<Vec<(String, Value)>>();
728
729 pairs.sort_by(|a, b| a.0.cmp(&b.0));
730
731 Ok(Value::record(pairs.into_iter().collect(), span))
732 }
733 id if id == nu_protocol::LAST_VARIABLE_ID => {
736 stack.defer_last_result_truncation_warning();
737 stack.get_var(var_id, span)
738 }
739 var_id => stack.get_var(var_id, span),
740 }
741}
742
743struct EvalRuntime;
744
745impl Eval for EvalRuntime {
746 type State<'a> = &'a EngineState;
747
748 type MutState = Stack;
749
750 fn get_config(engine_state: Self::State<'_>, stack: &mut Stack) -> Arc<Config> {
751 stack.get_config(engine_state)
752 }
753
754 fn eval_var(
755 engine_state: &EngineState,
756 stack: &mut Stack,
757 var_id: VarId,
758 span: Span,
759 ) -> Result<Value, ShellError> {
760 eval_variable(engine_state, stack, var_id, span)
761 }
762
763 fn eval_call<D: DebugContext>(
764 engine_state: &EngineState,
765 stack: &mut Stack,
766 call: &Call,
767 _: Span,
768 ) -> Result<Value, ShellError> {
769 eval_call::<D>(engine_state, stack, call, PipelineData::empty())?.into_value(call.head)
771 }
772
773 fn eval_external_call(
774 engine_state: &EngineState,
775 stack: &mut Stack,
776 head: &Expression,
777 args: &[ExternalArgument],
778 _: Span,
779 ) -> Result<Value, ShellError> {
780 let span = head.span(&engine_state);
781 eval_external(engine_state, stack, head, args, PipelineData::empty())?.into_value(span)
783 }
784
785 fn eval_collect<D: DebugContext>(
786 engine_state: &EngineState,
787 stack: &mut Stack,
788 var_id: VarId,
789 expr: &Expression,
790 ) -> Result<Value, ShellError> {
791 eval_collect::<D>(engine_state, stack, var_id, expr, PipelineData::empty())?
794 .into_value(expr.span)
795 }
796
797 fn eval_subexpression<D: DebugContext>(
798 engine_state: &EngineState,
799 stack: &mut Stack,
800 block_id: BlockId,
801 span: Span,
802 ) -> Result<Value, ShellError> {
803 let block = engine_state.get_block(block_id);
804 eval_subexpression::<D>(engine_state, stack, block, PipelineData::empty())?.into_value(span)
806 }
807
808 fn regex_match(
809 engine_state: &EngineState,
810 op_span: Span,
811 lhs: &Value,
812 rhs: &Value,
813 invert: bool,
814 expr_span: Span,
815 ) -> Result<Value, ShellError> {
816 lhs.regex_match(engine_state, op_span, rhs, invert, expr_span)
817 }
818
819 fn eval_assignment<D: DebugContext>(
820 engine_state: &EngineState,
821 stack: &mut Stack,
822 lhs: &Expression,
823 rhs: &Expression,
824 assignment: Assignment,
825 op_span: Span,
826 _expr_span: Span,
827 ) -> Result<Value, ShellError> {
828 let rhs = eval_expression::<D>(engine_state, stack, rhs)?;
829
830 let rhs = match assignment {
831 Assignment::Assign => rhs,
832 Assignment::AddAssign => {
833 let lhs = eval_expression::<D>(engine_state, stack, lhs)?;
834 lhs.add(op_span, &rhs, op_span)?
835 }
836 Assignment::SubtractAssign => {
837 let lhs = eval_expression::<D>(engine_state, stack, lhs)?;
838 lhs.sub(op_span, &rhs, op_span)?
839 }
840 Assignment::MultiplyAssign => {
841 let lhs = eval_expression::<D>(engine_state, stack, lhs)?;
842 lhs.mul(op_span, &rhs, op_span)?
843 }
844 Assignment::DivideAssign => {
845 let lhs = eval_expression::<D>(engine_state, stack, lhs)?;
846 lhs.div(op_span, &rhs, op_span)?
847 }
848 Assignment::ConcatenateAssign => {
849 let lhs = eval_expression::<D>(engine_state, stack, lhs)?;
850 lhs.concat(op_span, &rhs, op_span)?
851 }
852 };
853
854 match &lhs.expr {
855 Expr::Var(var_id) | Expr::VarDecl(var_id) => {
856 let var_info = engine_state.get_var(*var_id);
857 if var_info.mutable {
858 stack.add_var(*var_id, rhs);
859 Ok(Value::nothing(lhs.span(&engine_state)))
860 } else {
861 Err(ShellError::AssignmentRequiresMutableVar {
862 lhs_span: lhs.span(&engine_state),
863 })
864 }
865 }
866 Expr::FullCellPath(cell_path) => {
867 match &cell_path.head.expr {
868 Expr::Var(var_id) | Expr::VarDecl(var_id) => {
869 let is_env = var_id == &ENV_VARIABLE_ID;
872 if is_env || engine_state.get_var(*var_id).mutable {
873 if is_env {
874 let mut lhs =
875 eval_expression::<D>(engine_state, stack, &cell_path.head)?;
876
877 if cell_path.tail.is_empty() {
879 return Err(ShellError::CannotReplaceEnv {
880 span: cell_path.head.span(&engine_state),
881 });
882 }
883
884 let (key, span) = match &cell_path.tail[0] {
887 PathMember::String { val, span, .. } => (val.to_string(), span),
888 PathMember::Int { val, span, .. } => (val.to_string(), span),
889 };
890 let original_key = if let Value::Record { val: record, .. } = &lhs {
891 record
892 .iter()
893 .rev()
894 .map(|(k, _)| k)
895 .find(|x| x.eq_ignore_case(&key))
896 .cloned()
897 .unwrap_or(key)
898 } else {
899 key
900 };
901
902 lhs.upsert_data_at_cell_path(&cell_path.tail, rhs)?;
904 let value = lhs.follow_cell_path(&[{
905 let mut pm = cell_path.tail[0].clone();
906 pm.make_insensitive();
907 pm
908 }])?;
909
910 if is_automatic_env_var(&original_key) {
912 return Err(ShellError::AutomaticEnvVarSetManually {
913 envvar_name: original_key,
914 span: *span,
915 });
916 }
917
918 let is_config = original_key == "config";
919
920 stack.add_env_var(original_key, value.into_owned());
921
922 if is_config {
924 stack.update_config(engine_state)?;
925 }
926 } else {
927 stack.upsert_var_cell_path(
930 *var_id,
931 &cell_path.tail,
932 rhs,
933 cell_path.head.span(&engine_state),
934 )?;
935 }
936 Ok(Value::nothing(cell_path.head.span(&engine_state)))
937 } else {
938 Err(ShellError::AssignmentRequiresMutableVar {
939 lhs_span: lhs.span(&engine_state),
940 })
941 }
942 }
943 _ => Err(ShellError::AssignmentRequiresVar {
944 lhs_span: lhs.span(&engine_state),
945 }),
946 }
947 }
948 _ => Err(ShellError::AssignmentRequiresVar {
949 lhs_span: lhs.span(&engine_state),
950 }),
951 }
952 }
953
954 fn eval_row_condition_or_closure(
955 engine_state: &EngineState,
956 stack: &mut Stack,
957 block_id: BlockId,
958 span: Span,
959 ) -> Result<Value, ShellError> {
960 let captures = engine_state
961 .get_block(block_id)
962 .captures
963 .iter()
964 .map(|(id, span)| {
965 stack
966 .get_var(*id, *span)
967 .or_else(|_| {
968 engine_state
969 .get_var(*id)
970 .const_val
971 .clone()
972 .ok_or(ShellError::VariableNotFoundAtRuntime { span: *span })
973 })
974 .map(|var| (*id, var))
975 })
976 .collect::<Result<_, _>>()?;
977
978 Ok(Value::closure(Closure { block_id, captures }, span))
979 }
980
981 fn eval_overlay(engine_state: &EngineState, span: Span) -> Result<Value, ShellError> {
982 let name = String::from_utf8_lossy(engine_state.get_span_contents(span)).to_string();
983
984 Ok(Value::string(name, span))
985 }
986
987 fn unreachable(engine_state: &EngineState, expr: &Expression) -> Result<Value, ShellError> {
988 Ok(Value::nothing(expr.span(&engine_state)))
989 }
990}
991
992pub(crate) fn is_automatic_env_var(var: &str) -> bool {
998 let names = ["PWD", "FILE_PWD", "CURRENT_FILE"];
999 names.iter().any(|&name| {
1000 if cfg!(windows) {
1001 name.eq_ignore_case(var)
1002 } else {
1003 name.eq(var)
1004 }
1005 })
1006}