nu_protocol/ast/
call.rs

1use std::collections::HashMap;
2
3use serde::{Deserialize, Serialize};
4
5use crate::{
6    DeclId, FromValue, ShellError, Span, Spanned, Value, ast::Expression, engine::StateWorkingSet,
7    eval_const::eval_constant,
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 DoubleEndedIterator<Item = &(Spanned<String>, Option<Spanned<String>>, Option<Expression>)>
132    {
133        self.arguments.iter().filter_map(|arg| match arg {
134            Argument::Named(named) => Some(named),
135            Argument::Positional(_) => None,
136            Argument::Unknown(_) => None,
137            Argument::Spread(_) => None,
138        })
139    }
140
141    pub fn named_iter_mut(
142        &mut self,
143    ) -> impl Iterator<Item = &mut (Spanned<String>, Option<Spanned<String>>, Option<Expression>)>
144    {
145        self.arguments.iter_mut().filter_map(|arg| match arg {
146            Argument::Named(named) => Some(named),
147            Argument::Positional(_) => None,
148            Argument::Unknown(_) => None,
149            Argument::Spread(_) => None,
150        })
151    }
152
153    pub fn named_len(&self) -> usize {
154        self.named_iter().count()
155    }
156
157    pub fn add_named(
158        &mut self,
159        named: (Spanned<String>, Option<Spanned<String>>, Option<Expression>),
160    ) {
161        self.arguments.push(Argument::Named(named));
162    }
163
164    pub fn add_positional(&mut self, positional: Expression) {
165        self.arguments.push(Argument::Positional(positional));
166    }
167
168    pub fn add_unknown(&mut self, unknown: Expression) {
169        self.arguments.push(Argument::Unknown(unknown));
170    }
171
172    pub fn add_spread(&mut self, args: Expression) {
173        self.arguments.push(Argument::Spread(args));
174    }
175
176    pub fn positional_iter(&self) -> impl Iterator<Item = &Expression> {
177        self.arguments
178            .iter()
179            .take_while(|arg| match arg {
180                Argument::Spread(_) => false, // Don't include positional arguments given to rest parameter
181                _ => true,
182            })
183            .filter_map(|arg| match arg {
184                Argument::Named(_) => None,
185                Argument::Positional(positional) => Some(positional),
186                Argument::Unknown(unknown) => Some(unknown),
187                Argument::Spread(_) => None,
188            })
189    }
190
191    pub fn positional_nth(&self, i: usize) -> Option<&Expression> {
192        self.positional_iter().nth(i)
193    }
194
195    pub fn positional_len(&self) -> usize {
196        self.positional_iter().count()
197    }
198
199    /// Returns every argument to the rest parameter, as well as whether each argument
200    /// is spread or a normal positional argument (true for spread, false for normal)
201    pub fn rest_iter(&self, start: usize) -> impl Iterator<Item = (&Expression, bool)> {
202        // todo maybe rewrite to be more elegant or something
203        let args = self
204            .arguments
205            .iter()
206            .filter_map(|arg| match arg {
207                Argument::Named(_) => None,
208                Argument::Positional(positional) => Some((positional, false)),
209                Argument::Unknown(unknown) => Some((unknown, false)),
210                Argument::Spread(args) => Some((args, true)),
211            })
212            .collect::<Vec<_>>();
213        let spread_start = args.iter().position(|(_, spread)| *spread).unwrap_or(start);
214        args.into_iter().skip(start.min(spread_start))
215    }
216
217    pub fn get_parser_info(&self, name: &str) -> Option<&Expression> {
218        self.parser_info.get(name)
219    }
220
221    pub fn set_parser_info(&mut self, name: String, val: Expression) -> Option<Expression> {
222        self.parser_info.insert(name, val)
223    }
224
225    pub fn get_flag_expr(&self, flag_name: &str) -> Option<&Expression> {
226        for name in self.named_iter().rev() {
227            if flag_name == name.0.item {
228                return name.2.as_ref();
229            }
230        }
231
232        None
233    }
234
235    pub fn get_named_arg(&self, flag_name: &str) -> Option<Spanned<String>> {
236        for name in self.named_iter().rev() {
237            if flag_name == name.0.item {
238                return Some(name.0.clone());
239            }
240        }
241
242        None
243    }
244
245    /// Check if a boolean flag is set (i.e. `--bool` or `--bool=true`)
246    /// evaluating the expression after = as a constant command
247    pub fn has_flag_const(
248        &self,
249        working_set: &StateWorkingSet,
250        flag_name: &str,
251    ) -> Result<bool, ShellError> {
252        for name in self.named_iter() {
253            if flag_name == name.0.item {
254                return if let Some(expr) = &name.2 {
255                    // Check --flag=false
256                    let result = eval_constant(working_set, expr)?;
257                    match result {
258                        Value::Bool { val, .. } => Ok(val),
259                        _ => Err(ShellError::CantConvert {
260                            to_type: "bool".into(),
261                            from_type: result.get_type().to_string(),
262                            span: result.span(),
263                            help: Some("".into()),
264                        }),
265                    }
266                } else {
267                    Ok(true)
268                };
269            }
270        }
271
272        Ok(false)
273    }
274
275    pub fn get_flag_const<T: FromValue>(
276        &self,
277        working_set: &StateWorkingSet,
278        name: &str,
279    ) -> Result<Option<T>, ShellError> {
280        if let Some(expr) = self.get_flag_expr(name) {
281            let result = eval_constant(working_set, expr)?;
282            FromValue::from_value(result).map(Some)
283        } else {
284            Ok(None)
285        }
286    }
287
288    pub fn rest_const<T: FromValue>(
289        &self,
290        working_set: &StateWorkingSet,
291        starting_pos: usize,
292    ) -> Result<Vec<T>, ShellError> {
293        let mut output = vec![];
294
295        for result in
296            self.rest_iter_flattened(starting_pos, |expr| eval_constant(working_set, expr))?
297        {
298            output.push(FromValue::from_value(result)?);
299        }
300
301        Ok(output)
302    }
303
304    pub fn rest_iter_flattened<F>(
305        &self,
306        start: usize,
307        mut eval: F,
308    ) -> Result<Vec<Value>, ShellError>
309    where
310        F: FnMut(&Expression) -> Result<Value, ShellError>,
311    {
312        let mut output = Vec::new();
313
314        for (expr, spread) in self.rest_iter(start) {
315            let result = eval(expr)?;
316            if spread {
317                match result {
318                    Value::List { mut vals, .. } => output.append(&mut vals),
319                    Value::Nothing { .. } => (),
320                    _ => return Err(ShellError::CannotSpreadAsList { span: expr.span }),
321                }
322            } else {
323                output.push(result);
324            }
325        }
326
327        Ok(output)
328    }
329
330    pub fn req_const<T: FromValue>(
331        &self,
332        working_set: &StateWorkingSet,
333        pos: usize,
334    ) -> Result<T, ShellError> {
335        if let Some(expr) = self.positional_nth(pos) {
336            let result = eval_constant(working_set, expr)?;
337            FromValue::from_value(result)
338        } else if self.positional_len() == 0 {
339            Err(ShellError::AccessEmptyContent { span: self.head })
340        } else {
341            Err(ShellError::AccessBeyondEnd {
342                max_idx: self.positional_len() - 1,
343                span: self.head,
344            })
345        }
346    }
347
348    pub fn span(&self) -> Span {
349        self.head.merge(self.arguments_span())
350    }
351}
352
353#[cfg(test)]
354mod test {
355    use super::*;
356    use crate::engine::EngineState;
357
358    #[test]
359    fn argument_span_named() {
360        let engine_state = EngineState::new();
361        let mut working_set = StateWorkingSet::new(&engine_state);
362
363        let named = Spanned {
364            item: "named".to_string(),
365            span: Span::new(2, 3),
366        };
367        let short = Spanned {
368            item: "short".to_string(),
369            span: Span::new(5, 7),
370        };
371        let expr = Expression::garbage(&mut working_set, Span::new(11, 13));
372
373        let arg = Argument::Named((named.clone(), None, None));
374
375        assert_eq!(Span::new(2, 3), arg.span());
376
377        let arg = Argument::Named((named.clone(), Some(short.clone()), None));
378
379        assert_eq!(Span::new(2, 7), arg.span());
380
381        let arg = Argument::Named((named.clone(), None, Some(expr.clone())));
382
383        assert_eq!(Span::new(2, 13), arg.span());
384
385        let arg = Argument::Named((named.clone(), Some(short.clone()), Some(expr.clone())));
386
387        assert_eq!(Span::new(2, 13), arg.span());
388    }
389
390    #[test]
391    fn argument_span_positional() {
392        let engine_state = EngineState::new();
393        let mut working_set = StateWorkingSet::new(&engine_state);
394
395        let span = Span::new(2, 3);
396        let expr = Expression::garbage(&mut working_set, span);
397        let arg = Argument::Positional(expr);
398
399        assert_eq!(span, arg.span());
400    }
401
402    #[test]
403    fn argument_span_unknown() {
404        let engine_state = EngineState::new();
405        let mut working_set = StateWorkingSet::new(&engine_state);
406
407        let span = Span::new(2, 3);
408        let expr = Expression::garbage(&mut working_set, span);
409        let arg = Argument::Unknown(expr);
410
411        assert_eq!(span, arg.span());
412    }
413
414    #[test]
415    fn call_arguments_span() {
416        let engine_state = EngineState::new();
417        let mut working_set = StateWorkingSet::new(&engine_state);
418
419        let mut call = Call::new(Span::new(0, 1));
420        call.add_positional(Expression::garbage(&mut working_set, Span::new(2, 3)));
421        call.add_positional(Expression::garbage(&mut working_set, Span::new(5, 7)));
422
423        assert_eq!(Span::new(2, 7), call.arguments_span());
424    }
425}