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 set_kth_argument(&mut self, k: usize, arg: Argument) -> bool {
226        self.arguments.get_mut(k).map(|a| *a = arg).is_some()
227    }
228
229    pub fn get_flag_expr(&self, flag_name: &str) -> Option<&Expression> {
230        for name in self.named_iter().rev() {
231            if flag_name == name.0.item {
232                return name.2.as_ref();
233            }
234        }
235
236        None
237    }
238
239    pub fn get_named_arg(&self, flag_name: &str) -> Option<Spanned<String>> {
240        for name in self.named_iter().rev() {
241            if flag_name == name.0.item {
242                return Some(name.0.clone());
243            }
244        }
245
246        None
247    }
248
249    /// Check if a boolean flag is set (i.e. `--bool` or `--bool=true`)
250    /// evaluating the expression after = as a constant command
251    pub fn has_flag_const(
252        &self,
253        working_set: &StateWorkingSet,
254        flag_name: &str,
255    ) -> Result<bool, ShellError> {
256        for name in self.named_iter() {
257            if flag_name == name.0.item {
258                return if let Some(expr) = &name.2 {
259                    // Check --flag=false
260                    let result = eval_constant(working_set, expr)?;
261                    match result {
262                        Value::Bool { val, .. } => Ok(val),
263                        _ => Err(ShellError::CantConvert {
264                            to_type: "bool".into(),
265                            from_type: result.get_type().to_string(),
266                            span: result.span(),
267                            help: Some("".into()),
268                        }),
269                    }
270                } else {
271                    Ok(true)
272                };
273            }
274        }
275
276        Ok(false)
277    }
278
279    pub fn get_flag_const<T: FromValue>(
280        &self,
281        working_set: &StateWorkingSet,
282        name: &str,
283    ) -> Result<Option<T>, ShellError> {
284        if let Some(expr) = self.get_flag_expr(name) {
285            let result = eval_constant(working_set, expr)?;
286            FromValue::from_value(result).map(Some)
287        } else {
288            Ok(None)
289        }
290    }
291
292    pub fn rest_const<T: FromValue>(
293        &self,
294        working_set: &StateWorkingSet,
295        starting_pos: usize,
296    ) -> Result<Vec<T>, ShellError> {
297        let mut output = vec![];
298
299        for result in
300            self.rest_iter_flattened(starting_pos, |expr| eval_constant(working_set, expr))?
301        {
302            output.push(FromValue::from_value(result)?);
303        }
304
305        Ok(output)
306    }
307
308    pub fn rest_iter_flattened<F>(
309        &self,
310        start: usize,
311        mut eval: F,
312    ) -> Result<Vec<Value>, ShellError>
313    where
314        F: FnMut(&Expression) -> Result<Value, ShellError>,
315    {
316        let mut output = Vec::new();
317
318        for (expr, spread) in self.rest_iter(start) {
319            let result = eval(expr)?;
320            if spread {
321                match result {
322                    Value::List { mut vals, .. } => output.append(&mut vals),
323                    Value::Nothing { .. } => (),
324                    _ => return Err(ShellError::CannotSpreadAsList { span: expr.span }),
325                }
326            } else {
327                output.push(result);
328            }
329        }
330
331        Ok(output)
332    }
333
334    pub fn req_const<T: FromValue>(
335        &self,
336        working_set: &StateWorkingSet,
337        pos: usize,
338    ) -> Result<T, ShellError> {
339        if let Some(expr) = self.positional_nth(pos) {
340            let result = eval_constant(working_set, expr)?;
341            FromValue::from_value(result)
342        } else if self.positional_len() == 0 {
343            Err(ShellError::AccessEmptyContent { span: self.head })
344        } else {
345            Err(ShellError::AccessBeyondEnd {
346                max_idx: self.positional_len() - 1,
347                span: self.head,
348            })
349        }
350    }
351
352    pub fn span(&self) -> Span {
353        self.head.merge(self.arguments_span())
354    }
355}
356
357#[cfg(test)]
358mod test {
359    use super::*;
360    use crate::engine::EngineState;
361
362    #[test]
363    fn argument_span_named() {
364        let engine_state = EngineState::new();
365        let mut working_set = StateWorkingSet::new(&engine_state);
366
367        let named = Spanned {
368            item: "named".to_string(),
369            span: Span::new(2, 3),
370        };
371        let short = Spanned {
372            item: "short".to_string(),
373            span: Span::new(5, 7),
374        };
375        let expr = Expression::garbage(&mut working_set, Span::new(11, 13));
376
377        let arg = Argument::Named((named.clone(), None, None));
378
379        assert_eq!(Span::new(2, 3), arg.span());
380
381        let arg = Argument::Named((named.clone(), Some(short.clone()), None));
382
383        assert_eq!(Span::new(2, 7), arg.span());
384
385        let arg = Argument::Named((named.clone(), None, Some(expr.clone())));
386
387        assert_eq!(Span::new(2, 13), arg.span());
388
389        let arg = Argument::Named((named.clone(), Some(short.clone()), Some(expr.clone())));
390
391        assert_eq!(Span::new(2, 13), arg.span());
392    }
393
394    #[test]
395    fn argument_span_positional() {
396        let engine_state = EngineState::new();
397        let mut working_set = StateWorkingSet::new(&engine_state);
398
399        let span = Span::new(2, 3);
400        let expr = Expression::garbage(&mut working_set, span);
401        let arg = Argument::Positional(expr);
402
403        assert_eq!(span, arg.span());
404    }
405
406    #[test]
407    fn argument_span_unknown() {
408        let engine_state = EngineState::new();
409        let mut working_set = StateWorkingSet::new(&engine_state);
410
411        let span = Span::new(2, 3);
412        let expr = Expression::garbage(&mut working_set, span);
413        let arg = Argument::Unknown(expr);
414
415        assert_eq!(span, arg.span());
416    }
417
418    #[test]
419    fn call_arguments_span() {
420        let engine_state = EngineState::new();
421        let mut working_set = StateWorkingSet::new(&engine_state);
422
423        let mut call = Call::new(Span::new(0, 1));
424        call.add_positional(Expression::garbage(&mut working_set, Span::new(2, 3)));
425        call.add_positional(Expression::garbage(&mut working_set, Span::new(5, 7)));
426
427        assert_eq!(Span::new(2, 7), call.arguments_span());
428    }
429}