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