Skip to main content

nu_engine/
call_ext.rs

1use crate::eval_expression;
2use nu_protocol::{
3    FromValue, ShellError, Span, Value, ast,
4    debugger::WithoutDebug,
5    engine::{self, EngineState, Stack, StateWorkingSet},
6    eval_const::eval_constant,
7    ir,
8};
9
10pub trait CallExt {
11    /// Check if a boolean flag is set (i.e. `--bool` or `--bool=true`)
12    fn has_flag(
13        &self,
14        engine_state: &EngineState,
15        stack: &mut Stack,
16        flag_name: &str,
17    ) -> Result<bool, ShellError>;
18
19    fn get_flag<T: FromValue>(
20        &self,
21        engine_state: &EngineState,
22        stack: &mut Stack,
23        name: &str,
24    ) -> Result<Option<T>, ShellError>;
25
26    /// Efficiently get the span of a flag argument
27    fn get_flag_span(&self, stack: &Stack, name: &str) -> Option<Span>;
28
29    fn rest<T: FromValue>(
30        &self,
31        engine_state: &EngineState,
32        stack: &mut Stack,
33        starting_pos: usize,
34    ) -> Result<Vec<T>, ShellError>;
35
36    /// Returns the rest arguments starting at `starting_pos`, preserving whether each item was
37    /// passed as a spread argument.
38    ///
39    /// The tuple contains `(value, is_spread)`. This differs from [`CallExt::rest()`], which
40    /// flattens spread arguments into a single list of values.
41    fn rest_preserving_spreads<T: FromValue>(
42        &self,
43        engine_state: &EngineState,
44        stack: &mut Stack,
45        starting_pos: usize,
46    ) -> Result<Vec<(T, bool)>, ShellError>;
47
48    fn opt<T: FromValue>(
49        &self,
50        engine_state: &EngineState,
51        stack: &mut Stack,
52        pos: usize,
53    ) -> Result<Option<T>, ShellError>;
54
55    fn opt_const<T: FromValue>(
56        &self,
57        working_set: &StateWorkingSet,
58        pos: usize,
59    ) -> Result<Option<T>, ShellError>;
60
61    fn req<T: FromValue>(
62        &self,
63        engine_state: &EngineState,
64        stack: &mut Stack,
65        pos: usize,
66    ) -> Result<T, ShellError>;
67
68    fn req_parser_info<T: FromValue>(
69        &self,
70        engine_state: &EngineState,
71        stack: &mut Stack,
72        name: &str,
73    ) -> Result<T, ShellError>;
74
75    /// True if the command has any positional or rest arguments, excluding before the given index.
76    fn has_positional_args(&self, stack: &Stack, starting_pos: usize) -> bool;
77}
78
79impl CallExt for ast::Call {
80    fn has_flag(
81        &self,
82        engine_state: &EngineState,
83        stack: &mut Stack,
84        flag_name: &str,
85    ) -> Result<bool, ShellError> {
86        for name in self.named_iter() {
87            if flag_name == name.0.item {
88                return if let Some(expr) = &name.2 {
89                    // Check --flag=false
90                    let stack = &mut stack.use_call_arg_out_dest();
91                    let result = eval_expression::<WithoutDebug>(engine_state, stack, expr)?;
92                    match result {
93                        Value::Bool { val, .. } => Ok(val),
94                        _ => Err(ShellError::CantConvert {
95                            to_type: "bool".into(),
96                            from_type: result.get_type().to_string(),
97                            span: result.span(),
98                            help: Some("".into()),
99                        }),
100                    }
101                } else {
102                    Ok(true)
103                };
104            }
105        }
106
107        Ok(false)
108    }
109
110    fn get_flag<T: FromValue>(
111        &self,
112        engine_state: &EngineState,
113        stack: &mut Stack,
114        name: &str,
115    ) -> Result<Option<T>, ShellError> {
116        if let Some(expr) = self.get_flag_expr(name) {
117            let stack = &mut stack.use_call_arg_out_dest();
118            let result = eval_expression::<WithoutDebug>(engine_state, stack, expr)?;
119            FromValue::from_value(result).map(Some)
120        } else {
121            Ok(None)
122        }
123    }
124
125    fn get_flag_span(&self, _stack: &Stack, name: &str) -> Option<Span> {
126        self.get_named_arg(name).map(|arg| arg.span)
127    }
128
129    fn rest<T: FromValue>(
130        &self,
131        engine_state: &EngineState,
132        stack: &mut Stack,
133        starting_pos: usize,
134    ) -> Result<Vec<T>, ShellError> {
135        let stack = &mut stack.use_call_arg_out_dest();
136        self.rest_iter_flattened(starting_pos, |expr| {
137            eval_expression::<WithoutDebug>(engine_state, stack, expr)
138        })?
139        .into_iter()
140        .map(FromValue::from_value)
141        .collect()
142    }
143
144    fn rest_preserving_spreads<T: FromValue>(
145        &self,
146        engine_state: &EngineState,
147        stack: &mut Stack,
148        starting_pos: usize,
149    ) -> Result<Vec<(T, bool)>, ShellError> {
150        let stack = &mut stack.use_call_arg_out_dest();
151        self.rest_iter(starting_pos)
152            .map(|(expr, is_spread)| {
153                let result = eval_expression::<WithoutDebug>(engine_state, stack, expr)?;
154                Ok((T::from_value(result)?, is_spread))
155            })
156            .collect()
157    }
158
159    fn opt<T: FromValue>(
160        &self,
161        engine_state: &EngineState,
162        stack: &mut Stack,
163        pos: usize,
164    ) -> Result<Option<T>, ShellError> {
165        if let Some(expr) = self.positional_iter().nth(pos) {
166            let stack = &mut stack.use_call_arg_out_dest();
167            let result = eval_expression::<WithoutDebug>(engine_state, stack, expr)?;
168            if result.is_nothing() {
169                Ok(None)
170            } else {
171                FromValue::from_value(result).map(Some)
172            }
173        } else {
174            Ok(None)
175        }
176    }
177
178    fn opt_const<T: FromValue>(
179        &self,
180        working_set: &StateWorkingSet,
181        pos: usize,
182    ) -> Result<Option<T>, ShellError> {
183        if let Some(expr) = self.positional_iter().nth(pos) {
184            let result = eval_constant(working_set, expr)?;
185            if result.is_nothing() {
186                Ok(None)
187            } else {
188                FromValue::from_value(result).map(Some)
189            }
190        } else {
191            Ok(None)
192        }
193    }
194
195    fn req<T: FromValue>(
196        &self,
197        engine_state: &EngineState,
198        stack: &mut Stack,
199        pos: usize,
200    ) -> Result<T, ShellError> {
201        let maybe_expr = self.positional_iter().nth(pos);
202        let expr = maybe_expr.ok_or_else(|| {
203            let max_idx = self.positional_iter().count().checked_sub(1);
204            match max_idx {
205                None => ShellError::AccessEmptyContent { span: self.head },
206                Some(max_idx) => ShellError::AccessBeyondEnd {
207                    max_idx,
208                    span: self.head,
209                },
210            }
211        })?;
212
213        let stack = &mut stack.use_call_arg_out_dest();
214        let result = eval_expression::<WithoutDebug>(engine_state, stack, expr)?;
215        FromValue::from_value(result)
216    }
217
218    fn req_parser_info<T: FromValue>(
219        &self,
220        engine_state: &EngineState,
221        stack: &mut Stack,
222        name: &str,
223    ) -> Result<T, ShellError> {
224        if let Some(expr) = self.get_parser_info(name) {
225            let stack = &mut stack.use_call_arg_out_dest();
226            let result = eval_expression::<WithoutDebug>(engine_state, stack, expr)?;
227            FromValue::from_value(result)
228        } else if self.parser_info.is_empty() {
229            Err(ShellError::AccessEmptyContent { span: self.head })
230        } else {
231            Err(ShellError::AccessBeyondEnd {
232                max_idx: self.parser_info.len() - 1,
233                span: self.head,
234            })
235        }
236    }
237
238    fn has_positional_args(&self, _stack: &Stack, starting_pos: usize) -> bool {
239        self.rest_iter(starting_pos).next().is_some()
240    }
241}
242
243impl CallExt for ir::Call {
244    fn has_flag(
245        &self,
246        _engine_state: &EngineState,
247        stack: &mut Stack,
248        flag_name: &str,
249    ) -> Result<bool, ShellError> {
250        Ok(self
251            .named_iter(stack)
252            .find(|(name, _)| name.item == flag_name)
253            .is_some_and(|(_, value)| {
254                // Handle --flag=false
255                !matches!(value, Some(Value::Bool { val: false, .. }))
256            }))
257    }
258
259    fn get_flag<T: FromValue>(
260        &self,
261        _engine_state: &EngineState,
262        stack: &mut Stack,
263        name: &str,
264    ) -> Result<Option<T>, ShellError> {
265        if let Some(val) = self.get_named_arg(stack, name) {
266            T::from_value(val.clone()).map(Some)
267        } else {
268            Ok(None)
269        }
270    }
271
272    fn get_flag_span(&self, stack: &Stack, name: &str) -> Option<Span> {
273        self.named_iter(stack)
274            .find_map(|(i_name, _)| (i_name.item == name).then_some(i_name.span))
275    }
276
277    fn rest<T: FromValue>(
278        &self,
279        _engine_state: &EngineState,
280        stack: &mut Stack,
281        starting_pos: usize,
282    ) -> Result<Vec<T>, ShellError> {
283        self.rest_iter_flattened(stack, starting_pos)?
284            .into_iter()
285            .map(T::from_value)
286            .collect()
287    }
288
289    fn rest_preserving_spreads<T: FromValue>(
290        &self,
291        _engine_state: &EngineState,
292        stack: &mut Stack,
293        starting_pos: usize,
294    ) -> Result<Vec<(T, bool)>, ShellError> {
295        self.rest_iter(stack, starting_pos)
296            .map(|(val, is_spread)| Ok((T::from_value(val.clone())?, is_spread)))
297            .collect()
298    }
299
300    fn opt<T: FromValue>(
301        &self,
302        _engine_state: &EngineState,
303        stack: &mut Stack,
304        pos: usize,
305    ) -> Result<Option<T>, ShellError> {
306        self.positional_iter(stack)
307            .nth(pos)
308            .filter(|v| !v.is_nothing())
309            .cloned()
310            .map(T::from_value)
311            .transpose()
312    }
313
314    fn opt_const<T: FromValue>(
315        &self,
316        _working_set: &StateWorkingSet,
317        _pos: usize,
318    ) -> Result<Option<T>, ShellError> {
319        Err(ShellError::IrEvalError {
320            msg: "const evaluation is not yet implemented on ir::Call".into(),
321            span: Some(self.head),
322        })
323    }
324
325    fn req<T: FromValue>(
326        &self,
327        _engine_state: &EngineState,
328        stack: &mut Stack,
329        pos: usize,
330    ) -> Result<T, ShellError> {
331        let maybe_val = self.positional_iter(stack).nth(pos).cloned();
332        let val = maybe_val.ok_or_else(|| {
333            let max_idx = self.positional_iter(stack).count().checked_sub(1);
334            match max_idx {
335                None => ShellError::AccessEmptyContent { span: self.head },
336                Some(max_idx) => ShellError::AccessBeyondEnd {
337                    max_idx,
338                    span: self.head,
339                },
340            }
341        })?;
342
343        T::from_value(val)
344    }
345
346    fn req_parser_info<T: FromValue>(
347        &self,
348        engine_state: &EngineState,
349        stack: &mut Stack,
350        name: &str,
351    ) -> Result<T, ShellError> {
352        // FIXME: this depends on the AST evaluator. We can fix this by making the parser info an
353        // enum rather than using expressions. It's not clear that evaluation of this is ever really
354        // needed.
355        if let Some(expr) = self.get_parser_info(stack, name) {
356            let expr = expr.clone();
357            let stack = &mut stack.use_call_arg_out_dest();
358            let result = eval_expression::<WithoutDebug>(engine_state, stack, &expr)?;
359            FromValue::from_value(result)
360        } else {
361            Err(ShellError::CantFindColumn {
362                col_name: name.into(),
363                span: None,
364                src_span: self.head,
365            })
366        }
367    }
368
369    fn has_positional_args(&self, stack: &Stack, starting_pos: usize) -> bool {
370        self.rest_iter(stack, starting_pos).next().is_some()
371    }
372}
373
374macro_rules! proxy {
375    ($self:ident . $method:ident ($($param:expr),*)) => (match &$self.inner {
376        engine::CallImpl::AstRef(call) => call.$method($($param),*),
377        engine::CallImpl::AstBox(call) => call.$method($($param),*),
378        engine::CallImpl::IrRef(call) => call.$method($($param),*),
379        engine::CallImpl::IrBox(call) => call.$method($($param),*),
380    })
381}
382
383impl CallExt for engine::Call<'_> {
384    fn has_flag(
385        &self,
386        engine_state: &EngineState,
387        stack: &mut Stack,
388        flag_name: &str,
389    ) -> Result<bool, ShellError> {
390        proxy!(self.has_flag(engine_state, stack, flag_name))
391    }
392
393    fn get_flag<T: FromValue>(
394        &self,
395        engine_state: &EngineState,
396        stack: &mut Stack,
397        name: &str,
398    ) -> Result<Option<T>, ShellError> {
399        proxy!(self.get_flag(engine_state, stack, name))
400    }
401
402    fn get_flag_span(&self, stack: &Stack, name: &str) -> Option<Span> {
403        proxy!(self.get_flag_span(stack, name))
404    }
405
406    fn rest<T: FromValue>(
407        &self,
408        engine_state: &EngineState,
409        stack: &mut Stack,
410        starting_pos: usize,
411    ) -> Result<Vec<T>, ShellError> {
412        proxy!(self.rest(engine_state, stack, starting_pos))
413    }
414
415    fn rest_preserving_spreads<T: FromValue>(
416        &self,
417        engine_state: &EngineState,
418        stack: &mut Stack,
419        starting_pos: usize,
420    ) -> Result<Vec<(T, bool)>, ShellError> {
421        proxy!(self.rest_preserving_spreads(engine_state, stack, starting_pos))
422    }
423
424    fn opt<T: FromValue>(
425        &self,
426        engine_state: &EngineState,
427        stack: &mut Stack,
428        pos: usize,
429    ) -> Result<Option<T>, ShellError> {
430        proxy!(self.opt(engine_state, stack, pos))
431    }
432
433    fn opt_const<T: FromValue>(
434        &self,
435        working_set: &StateWorkingSet,
436        pos: usize,
437    ) -> Result<Option<T>, ShellError> {
438        proxy!(self.opt_const(working_set, pos))
439    }
440
441    fn req<T: FromValue>(
442        &self,
443        engine_state: &EngineState,
444        stack: &mut Stack,
445        pos: usize,
446    ) -> Result<T, ShellError> {
447        proxy!(self.req(engine_state, stack, pos))
448    }
449
450    fn req_parser_info<T: FromValue>(
451        &self,
452        engine_state: &EngineState,
453        stack: &mut Stack,
454        name: &str,
455    ) -> Result<T, ShellError> {
456        proxy!(self.req_parser_info(engine_state, stack, name))
457    }
458
459    fn has_positional_args(&self, stack: &Stack, starting_pos: usize) -> bool {
460        proxy!(self.has_positional_args(stack, starting_pos))
461    }
462}