nu_engine/
call_ext.rs

1use crate::eval_expression;
2use nu_protocol::{
3    ast,
4    debugger::WithoutDebug,
5    engine::{self, EngineState, Stack, StateWorkingSet},
6    eval_const::eval_constant,
7    ir, FromValue, ShellError, Span, Value,
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            FromValue::from_value(result).map(Some)
142        } else {
143            Ok(None)
144        }
145    }
146
147    fn opt_const<T: FromValue>(
148        &self,
149        working_set: &StateWorkingSet,
150        pos: usize,
151    ) -> Result<Option<T>, ShellError> {
152        if let Some(expr) = self.positional_nth(pos) {
153            let result = eval_constant(working_set, expr)?;
154            FromValue::from_value(result).map(Some)
155        } else {
156            Ok(None)
157        }
158    }
159
160    fn req<T: FromValue>(
161        &self,
162        engine_state: &EngineState,
163        stack: &mut Stack,
164        pos: usize,
165    ) -> Result<T, ShellError> {
166        if let Some(expr) = self.positional_nth(pos) {
167            let stack = &mut stack.use_call_arg_out_dest();
168            let result = eval_expression::<WithoutDebug>(engine_state, stack, expr)?;
169            FromValue::from_value(result)
170        } else if self.positional_len() == 0 {
171            Err(ShellError::AccessEmptyContent { span: self.head })
172        } else {
173            Err(ShellError::AccessBeyondEnd {
174                max_idx: self.positional_len() - 1,
175                span: self.head,
176            })
177        }
178    }
179
180    fn req_parser_info<T: FromValue>(
181        &self,
182        engine_state: &EngineState,
183        stack: &mut Stack,
184        name: &str,
185    ) -> Result<T, ShellError> {
186        if let Some(expr) = self.get_parser_info(name) {
187            let stack = &mut stack.use_call_arg_out_dest();
188            let result = eval_expression::<WithoutDebug>(engine_state, stack, expr)?;
189            FromValue::from_value(result)
190        } else if self.parser_info.is_empty() {
191            Err(ShellError::AccessEmptyContent { span: self.head })
192        } else {
193            Err(ShellError::AccessBeyondEnd {
194                max_idx: self.parser_info.len() - 1,
195                span: self.head,
196            })
197        }
198    }
199
200    fn has_positional_args(&self, _stack: &Stack, starting_pos: usize) -> bool {
201        self.rest_iter(starting_pos).next().is_some()
202    }
203}
204
205impl CallExt for ir::Call {
206    fn has_flag(
207        &self,
208        _engine_state: &EngineState,
209        stack: &mut Stack,
210        flag_name: &str,
211    ) -> Result<bool, ShellError> {
212        Ok(self
213            .named_iter(stack)
214            .find(|(name, _)| name.item == flag_name)
215            .is_some_and(|(_, value)| {
216                // Handle --flag=false
217                !matches!(value, Some(Value::Bool { val: false, .. }))
218            }))
219    }
220
221    fn get_flag<T: FromValue>(
222        &self,
223        _engine_state: &EngineState,
224        stack: &mut Stack,
225        name: &str,
226    ) -> Result<Option<T>, ShellError> {
227        if let Some(val) = self.get_named_arg(stack, name) {
228            T::from_value(val.clone()).map(Some)
229        } else {
230            Ok(None)
231        }
232    }
233
234    fn get_flag_span(&self, stack: &Stack, name: &str) -> Option<Span> {
235        self.named_iter(stack)
236            .find_map(|(i_name, _)| (i_name.item == name).then_some(i_name.span))
237    }
238
239    fn rest<T: FromValue>(
240        &self,
241        _engine_state: &EngineState,
242        stack: &mut Stack,
243        starting_pos: usize,
244    ) -> Result<Vec<T>, ShellError> {
245        self.rest_iter_flattened(stack, starting_pos)?
246            .into_iter()
247            .map(T::from_value)
248            .collect()
249    }
250
251    fn opt<T: FromValue>(
252        &self,
253        _engine_state: &EngineState,
254        stack: &mut Stack,
255        pos: usize,
256    ) -> Result<Option<T>, ShellError> {
257        self.positional_iter(stack)
258            .nth(pos)
259            .cloned()
260            .map(T::from_value)
261            .transpose()
262    }
263
264    fn opt_const<T: FromValue>(
265        &self,
266        _working_set: &StateWorkingSet,
267        _pos: usize,
268    ) -> Result<Option<T>, ShellError> {
269        Err(ShellError::IrEvalError {
270            msg: "const evaluation is not yet implemented on ir::Call".into(),
271            span: Some(self.head),
272        })
273    }
274
275    fn req<T: FromValue>(
276        &self,
277        engine_state: &EngineState,
278        stack: &mut Stack,
279        pos: usize,
280    ) -> Result<T, ShellError> {
281        if let Some(val) = self.opt(engine_state, stack, pos)? {
282            Ok(val)
283        } else if self.positional_len(stack) == 0 {
284            Err(ShellError::AccessEmptyContent { span: self.head })
285        } else {
286            Err(ShellError::AccessBeyondEnd {
287                max_idx: self.positional_len(stack) - 1,
288                span: self.head,
289            })
290        }
291    }
292
293    fn req_parser_info<T: FromValue>(
294        &self,
295        engine_state: &EngineState,
296        stack: &mut Stack,
297        name: &str,
298    ) -> Result<T, ShellError> {
299        // FIXME: this depends on the AST evaluator. We can fix this by making the parser info an
300        // enum rather than using expressions. It's not clear that evaluation of this is ever really
301        // needed.
302        if let Some(expr) = self.get_parser_info(stack, name) {
303            let expr = expr.clone();
304            let stack = &mut stack.use_call_arg_out_dest();
305            let result = eval_expression::<WithoutDebug>(engine_state, stack, &expr)?;
306            FromValue::from_value(result)
307        } else {
308            Err(ShellError::CantFindColumn {
309                col_name: name.into(),
310                span: None,
311                src_span: self.head,
312            })
313        }
314    }
315
316    fn has_positional_args(&self, stack: &Stack, starting_pos: usize) -> bool {
317        self.rest_iter(stack, starting_pos).next().is_some()
318    }
319}
320
321macro_rules! proxy {
322    ($self:ident . $method:ident ($($param:expr),*)) => (match &$self.inner {
323        engine::CallImpl::AstRef(call) => call.$method($($param),*),
324        engine::CallImpl::AstBox(call) => call.$method($($param),*),
325        engine::CallImpl::IrRef(call) => call.$method($($param),*),
326        engine::CallImpl::IrBox(call) => call.$method($($param),*),
327    })
328}
329
330impl CallExt for engine::Call<'_> {
331    fn has_flag(
332        &self,
333        engine_state: &EngineState,
334        stack: &mut Stack,
335        flag_name: &str,
336    ) -> Result<bool, ShellError> {
337        proxy!(self.has_flag(engine_state, stack, flag_name))
338    }
339
340    fn get_flag<T: FromValue>(
341        &self,
342        engine_state: &EngineState,
343        stack: &mut Stack,
344        name: &str,
345    ) -> Result<Option<T>, ShellError> {
346        proxy!(self.get_flag(engine_state, stack, name))
347    }
348
349    fn get_flag_span(&self, stack: &Stack, name: &str) -> Option<Span> {
350        proxy!(self.get_flag_span(stack, name))
351    }
352
353    fn rest<T: FromValue>(
354        &self,
355        engine_state: &EngineState,
356        stack: &mut Stack,
357        starting_pos: usize,
358    ) -> Result<Vec<T>, ShellError> {
359        proxy!(self.rest(engine_state, stack, starting_pos))
360    }
361
362    fn opt<T: FromValue>(
363        &self,
364        engine_state: &EngineState,
365        stack: &mut Stack,
366        pos: usize,
367    ) -> Result<Option<T>, ShellError> {
368        proxy!(self.opt(engine_state, stack, pos))
369    }
370
371    fn opt_const<T: FromValue>(
372        &self,
373        working_set: &StateWorkingSet,
374        pos: usize,
375    ) -> Result<Option<T>, ShellError> {
376        proxy!(self.opt_const(working_set, pos))
377    }
378
379    fn req<T: FromValue>(
380        &self,
381        engine_state: &EngineState,
382        stack: &mut Stack,
383        pos: usize,
384    ) -> Result<T, ShellError> {
385        proxy!(self.req(engine_state, stack, pos))
386    }
387
388    fn req_parser_info<T: FromValue>(
389        &self,
390        engine_state: &EngineState,
391        stack: &mut Stack,
392        name: &str,
393    ) -> Result<T, ShellError> {
394        proxy!(self.req_parser_info(engine_state, stack, name))
395    }
396
397    fn has_positional_args(&self, stack: &Stack, starting_pos: usize) -> bool {
398        proxy!(self.has_positional_args(stack, starting_pos))
399    }
400}