nu_protocol/ast/
call.rs

1use std::collections::HashMap;
2
3use serde::{Deserialize, Serialize};
4
5use crate::{
6    ast::Expression, engine::StateWorkingSet, eval_const::eval_constant, DeclId, FromValue,
7    ShellError, Span, Spanned, Value,
8};
9
10/// Parsed command arguments
11///
12/// Primarily for internal commands
13#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
14pub enum Argument {
15    /// A positional argument (that is not [`Argument::Spread`])
16    ///
17    /// ```nushell
18    /// my_cmd positional
19    /// ```
20    Positional(Expression),
21    /// A named/flag argument that can optionally receive a [`Value`] as an [`Expression`]
22    ///
23    /// The optional second `Spanned<String>` refers to the short-flag version if used
24    /// ```nushell
25    /// my_cmd --flag
26    /// my_cmd -f
27    /// my_cmd --flag-with-value <expr>
28    /// ```
29    Named((Spanned<String>, Option<Spanned<String>>, Option<Expression>)),
30    /// unknown argument used in "fall-through" signatures
31    Unknown(Expression),
32    /// a list spread to fill in rest arguments
33    Spread(Expression),
34}
35
36impl Argument {
37    /// The span for an argument
38    pub fn span(&self) -> Span {
39        match self {
40            Argument::Positional(e) => e.span,
41            Argument::Named((named, short, expr)) => {
42                let start = named.span.start;
43                let end = if let Some(expr) = expr {
44                    expr.span.end
45                } else if let Some(short) = short {
46                    short.span.end
47                } else {
48                    named.span.end
49                };
50
51                Span::new(start, end)
52            }
53            Argument::Unknown(e) => e.span,
54            Argument::Spread(e) => e.span,
55        }
56    }
57
58    pub fn expr(&self) -> Option<&Expression> {
59        match self {
60            Argument::Named((_, _, expr)) => expr.as_ref(),
61            Argument::Positional(expr) | Argument::Unknown(expr) | Argument::Spread(expr) => {
62                Some(expr)
63            }
64        }
65    }
66}
67
68/// Argument passed to an external command
69///
70/// Here the parsing rules slightly differ to directly pass strings to the external process
71#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
72pub enum ExternalArgument {
73    /// Expression that needs to be evaluated to turn into an external process argument
74    Regular(Expression),
75    /// Occurrence of a `...` spread operator that needs to be expanded
76    Spread(Expression),
77}
78
79impl ExternalArgument {
80    pub fn expr(&self) -> &Expression {
81        match self {
82            ExternalArgument::Regular(expr) => expr,
83            ExternalArgument::Spread(expr) => expr,
84        }
85    }
86}
87
88/// Parsed call of a `Command`
89///
90/// As we also implement some internal keywords in terms of the `Command` trait, this type stores the passed arguments as [`Expression`].
91/// Some of its methods lazily evaluate those to [`Value`] while others return the underlying
92/// [`Expression`].
93///
94/// For further utilities check the `nu_engine::CallExt` trait that extends [`Call`]
95#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
96pub struct Call {
97    /// identifier of the declaration to call
98    pub decl_id: DeclId,
99    pub head: Span,
100    pub arguments: Vec<Argument>,
101    /// this field is used by the parser to pass additional command-specific information
102    pub parser_info: HashMap<String, Expression>,
103}
104
105impl Call {
106    pub fn new(head: Span) -> Call {
107        Self {
108            decl_id: DeclId::new(0),
109            head,
110            arguments: vec![],
111            parser_info: HashMap::new(),
112        }
113    }
114
115    /// The span encompassing the arguments
116    ///
117    /// If there are no arguments the span covers where the first argument would exist
118    ///
119    /// If there are one or more arguments the span encompasses the start of the first argument to
120    /// end of the last argument
121    pub fn arguments_span(&self) -> Span {
122        if self.arguments.is_empty() {
123            self.head.past()
124        } else {
125            Span::merge_many(self.arguments.iter().map(|a| a.span()))
126        }
127    }
128
129    pub fn named_iter(
130        &self,
131    ) -> impl Iterator<Item = &(Spanned<String>, Option<Spanned<String>>, Option<Expression>)> {
132        self.arguments.iter().filter_map(|arg| match arg {
133            Argument::Named(named) => Some(named),
134            Argument::Positional(_) => None,
135            Argument::Unknown(_) => None,
136            Argument::Spread(_) => None,
137        })
138    }
139
140    pub fn named_iter_mut(
141        &mut self,
142    ) -> impl Iterator<Item = &mut (Spanned<String>, Option<Spanned<String>>, Option<Expression>)>
143    {
144        self.arguments.iter_mut().filter_map(|arg| match arg {
145            Argument::Named(named) => Some(named),
146            Argument::Positional(_) => None,
147            Argument::Unknown(_) => None,
148            Argument::Spread(_) => None,
149        })
150    }
151
152    pub fn named_len(&self) -> usize {
153        self.named_iter().count()
154    }
155
156    pub fn add_named(
157        &mut self,
158        named: (Spanned<String>, Option<Spanned<String>>, Option<Expression>),
159    ) {
160        self.arguments.push(Argument::Named(named));
161    }
162
163    pub fn add_positional(&mut self, positional: Expression) {
164        self.arguments.push(Argument::Positional(positional));
165    }
166
167    pub fn add_unknown(&mut self, unknown: Expression) {
168        self.arguments.push(Argument::Unknown(unknown));
169    }
170
171    pub fn add_spread(&mut self, args: Expression) {
172        self.arguments.push(Argument::Spread(args));
173    }
174
175    pub fn positional_iter(&self) -> impl Iterator<Item = &Expression> {
176        self.arguments
177            .iter()
178            .take_while(|arg| match arg {
179                Argument::Spread(_) => false, // Don't include positional arguments given to rest parameter
180                _ => true,
181            })
182            .filter_map(|arg| match arg {
183                Argument::Named(_) => None,
184                Argument::Positional(positional) => Some(positional),
185                Argument::Unknown(unknown) => Some(unknown),
186                Argument::Spread(_) => None,
187            })
188    }
189
190    pub fn positional_nth(&self, i: usize) -> Option<&Expression> {
191        self.positional_iter().nth(i)
192    }
193
194    pub fn positional_len(&self) -> usize {
195        self.positional_iter().count()
196    }
197
198    /// Returns every argument to the rest parameter, as well as whether each argument
199    /// is spread or a normal positional argument (true for spread, false for normal)
200    pub fn rest_iter(&self, start: usize) -> impl Iterator<Item = (&Expression, bool)> {
201        // todo maybe rewrite to be more elegant or something
202        let args = self
203            .arguments
204            .iter()
205            .filter_map(|arg| match arg {
206                Argument::Named(_) => None,
207                Argument::Positional(positional) => Some((positional, false)),
208                Argument::Unknown(unknown) => Some((unknown, false)),
209                Argument::Spread(args) => Some((args, true)),
210            })
211            .collect::<Vec<_>>();
212        let spread_start = args.iter().position(|(_, spread)| *spread).unwrap_or(start);
213        args.into_iter().skip(start.min(spread_start))
214    }
215
216    pub fn get_parser_info(&self, name: &str) -> Option<&Expression> {
217        self.parser_info.get(name)
218    }
219
220    pub fn set_parser_info(&mut self, name: String, val: Expression) -> Option<Expression> {
221        self.parser_info.insert(name, val)
222    }
223
224    pub fn get_flag_expr(&self, flag_name: &str) -> Option<&Expression> {
225        for name in self.named_iter() {
226            if flag_name == name.0.item {
227                return name.2.as_ref();
228            }
229        }
230
231        None
232    }
233
234    pub fn get_named_arg(&self, flag_name: &str) -> Option<Spanned<String>> {
235        for name in self.named_iter() {
236            if flag_name == name.0.item {
237                return Some(name.0.clone());
238            }
239        }
240
241        None
242    }
243
244    /// Check if a boolean flag is set (i.e. `--bool` or `--bool=true`)
245    /// evaluating the expression after = as a constant command
246    pub fn has_flag_const(
247        &self,
248        working_set: &StateWorkingSet,
249        flag_name: &str,
250    ) -> Result<bool, ShellError> {
251        for name in self.named_iter() {
252            if flag_name == name.0.item {
253                return if let Some(expr) = &name.2 {
254                    // Check --flag=false
255                    let result = eval_constant(working_set, expr)?;
256                    match result {
257                        Value::Bool { val, .. } => Ok(val),
258                        _ => Err(ShellError::CantConvert {
259                            to_type: "bool".into(),
260                            from_type: result.get_type().to_string(),
261                            span: result.span(),
262                            help: Some("".into()),
263                        }),
264                    }
265                } else {
266                    Ok(true)
267                };
268            }
269        }
270
271        Ok(false)
272    }
273
274    pub fn get_flag_const<T: FromValue>(
275        &self,
276        working_set: &StateWorkingSet,
277        name: &str,
278    ) -> Result<Option<T>, ShellError> {
279        if let Some(expr) = self.get_flag_expr(name) {
280            let result = eval_constant(working_set, expr)?;
281            FromValue::from_value(result).map(Some)
282        } else {
283            Ok(None)
284        }
285    }
286
287    pub fn rest_const<T: FromValue>(
288        &self,
289        working_set: &StateWorkingSet,
290        starting_pos: usize,
291    ) -> Result<Vec<T>, ShellError> {
292        let mut output = vec![];
293
294        for result in
295            self.rest_iter_flattened(starting_pos, |expr| eval_constant(working_set, expr))?
296        {
297            output.push(FromValue::from_value(result)?);
298        }
299
300        Ok(output)
301    }
302
303    pub fn rest_iter_flattened<F>(
304        &self,
305        start: usize,
306        mut eval: F,
307    ) -> Result<Vec<Value>, ShellError>
308    where
309        F: FnMut(&Expression) -> Result<Value, ShellError>,
310    {
311        let mut output = Vec::new();
312
313        for (expr, spread) in self.rest_iter(start) {
314            let result = eval(expr)?;
315            if spread {
316                match result {
317                    Value::List { mut vals, .. } => output.append(&mut vals),
318                    _ => return Err(ShellError::CannotSpreadAsList { span: expr.span }),
319                }
320            } else {
321                output.push(result);
322            }
323        }
324
325        Ok(output)
326    }
327
328    pub fn req_const<T: FromValue>(
329        &self,
330        working_set: &StateWorkingSet,
331        pos: usize,
332    ) -> Result<T, ShellError> {
333        if let Some(expr) = self.positional_nth(pos) {
334            let result = eval_constant(working_set, expr)?;
335            FromValue::from_value(result)
336        } else if self.positional_len() == 0 {
337            Err(ShellError::AccessEmptyContent { span: self.head })
338        } else {
339            Err(ShellError::AccessBeyondEnd {
340                max_idx: self.positional_len() - 1,
341                span: self.head,
342            })
343        }
344    }
345
346    pub fn span(&self) -> Span {
347        self.head.merge(self.arguments_span())
348    }
349}
350
351#[cfg(test)]
352mod test {
353    use super::*;
354    use crate::engine::EngineState;
355
356    #[test]
357    fn argument_span_named() {
358        let engine_state = EngineState::new();
359        let mut working_set = StateWorkingSet::new(&engine_state);
360
361        let named = Spanned {
362            item: "named".to_string(),
363            span: Span::new(2, 3),
364        };
365        let short = Spanned {
366            item: "short".to_string(),
367            span: Span::new(5, 7),
368        };
369        let expr = Expression::garbage(&mut working_set, Span::new(11, 13));
370
371        let arg = Argument::Named((named.clone(), None, None));
372
373        assert_eq!(Span::new(2, 3), arg.span());
374
375        let arg = Argument::Named((named.clone(), Some(short.clone()), None));
376
377        assert_eq!(Span::new(2, 7), arg.span());
378
379        let arg = Argument::Named((named.clone(), None, Some(expr.clone())));
380
381        assert_eq!(Span::new(2, 13), arg.span());
382
383        let arg = Argument::Named((named.clone(), Some(short.clone()), Some(expr.clone())));
384
385        assert_eq!(Span::new(2, 13), arg.span());
386    }
387
388    #[test]
389    fn argument_span_positional() {
390        let engine_state = EngineState::new();
391        let mut working_set = StateWorkingSet::new(&engine_state);
392
393        let span = Span::new(2, 3);
394        let expr = Expression::garbage(&mut working_set, span);
395        let arg = Argument::Positional(expr);
396
397        assert_eq!(span, arg.span());
398    }
399
400    #[test]
401    fn argument_span_unknown() {
402        let engine_state = EngineState::new();
403        let mut working_set = StateWorkingSet::new(&engine_state);
404
405        let span = Span::new(2, 3);
406        let expr = Expression::garbage(&mut working_set, span);
407        let arg = Argument::Unknown(expr);
408
409        assert_eq!(span, arg.span());
410    }
411
412    #[test]
413    fn call_arguments_span() {
414        let engine_state = EngineState::new();
415        let mut working_set = StateWorkingSet::new(&engine_state);
416
417        let mut call = Call::new(Span::new(0, 1));
418        call.add_positional(Expression::garbage(&mut working_set, Span::new(2, 3)));
419        call.add_positional(Expression::garbage(&mut working_set, Span::new(5, 7)));
420
421        assert_eq!(Span::new(2, 7), call.arguments_span());
422    }
423}