Skip to main content

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