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