nu_protocol/ir/
call.rs

1use std::sync::Arc;
2
3use crate::{
4    ast::Expression,
5    engine::{self, Argument, Stack},
6    DeclId, ShellError, Span, Spanned, Value,
7};
8
9use super::DataSlice;
10
11/// Contains the information for a call being made to a declared command.
12#[derive(Debug, Clone)]
13pub struct Call {
14    /// The declaration ID of the command to be invoked.
15    pub decl_id: DeclId,
16    /// The span encompassing the command name, before the arguments.
17    pub head: Span,
18    /// The span encompassing the command name and all arguments.
19    pub span: Span,
20    /// The base index of the arguments for this call within the
21    /// [argument stack](crate::engine::ArgumentStack).
22    pub args_base: usize,
23    /// The number of [`Argument`]s for the call. Note that this just counts the number of
24    /// `Argument` entries on the stack, and has nothing to do with the actual number of positional
25    /// or spread arguments.
26    pub args_len: usize,
27}
28
29impl Call {
30    /// Build a new call with arguments.
31    pub fn build(decl_id: DeclId, head: Span) -> CallBuilder {
32        CallBuilder {
33            inner: Call {
34                decl_id,
35                head,
36                span: head,
37                args_base: 0,
38                args_len: 0,
39            },
40        }
41    }
42
43    /// Get the arguments for this call from the arguments stack.
44    pub fn arguments<'a>(&self, stack: &'a Stack) -> &'a [Argument] {
45        stack.arguments.get_args(self.args_base, self.args_len)
46    }
47
48    /// The span encompassing the arguments
49    ///
50    /// If there are no arguments the span covers where the first argument would exist
51    ///
52    /// If there are one or more arguments the span encompasses the start of the first argument to
53    /// end of the last argument
54    pub fn arguments_span(&self) -> Span {
55        let past = self.head.past();
56        Span::new(past.start, self.span.end)
57    }
58
59    /// The number of named arguments, with or without values.
60    pub fn named_len(&self, stack: &Stack) -> usize {
61        self.arguments(stack)
62            .iter()
63            .filter(|arg| matches!(arg, Argument::Named { .. } | Argument::Flag { .. }))
64            .count()
65    }
66
67    /// Iterate through named arguments, with or without values.
68    pub fn named_iter<'a>(
69        &'a self,
70        stack: &'a Stack,
71    ) -> impl Iterator<Item = (Spanned<&'a str>, Option<&'a Value>)> + 'a {
72        self.arguments(stack).iter().filter_map(
73            |arg: &Argument| -> Option<(Spanned<&str>, Option<&Value>)> {
74                match arg {
75                    Argument::Flag {
76                        data, name, span, ..
77                    } => Some((
78                        Spanned {
79                            item: std::str::from_utf8(&data[*name]).expect("invalid arg name"),
80                            span: *span,
81                        },
82                        None,
83                    )),
84                    Argument::Named {
85                        data,
86                        name,
87                        span,
88                        val,
89                        ..
90                    } => Some((
91                        Spanned {
92                            item: std::str::from_utf8(&data[*name]).expect("invalid arg name"),
93                            span: *span,
94                        },
95                        Some(val),
96                    )),
97                    _ => None,
98                }
99            },
100        )
101    }
102
103    /// Get a named argument's value by name. Returns [`None`] for named arguments with no value as
104    /// well.
105    pub fn get_named_arg<'a>(&self, stack: &'a Stack, flag_name: &str) -> Option<&'a Value> {
106        // Optimized to avoid str::from_utf8()
107        self.arguments(stack)
108            .iter()
109            .find_map(|arg: &Argument| -> Option<Option<&Value>> {
110                match arg {
111                    Argument::Flag { data, name, .. } if &data[*name] == flag_name.as_bytes() => {
112                        Some(None)
113                    }
114                    Argument::Named {
115                        data, name, val, ..
116                    } if &data[*name] == flag_name.as_bytes() => Some(Some(val)),
117                    _ => None,
118                }
119            })
120            .flatten()
121    }
122
123    /// The number of positional arguments, excluding spread arguments.
124    pub fn positional_len(&self, stack: &Stack) -> usize {
125        self.arguments(stack)
126            .iter()
127            .filter(|arg| matches!(arg, Argument::Positional { .. }))
128            .count()
129    }
130
131    /// Iterate through positional arguments. Does not include spread arguments.
132    pub fn positional_iter<'a>(&self, stack: &'a Stack) -> impl Iterator<Item = &'a Value> {
133        self.arguments(stack).iter().filter_map(|arg| match arg {
134            Argument::Positional { val, .. } => Some(val),
135            _ => None,
136        })
137    }
138
139    /// Get a positional argument by index. Does not include spread arguments.
140    pub fn positional_nth<'a>(&self, stack: &'a Stack, index: usize) -> Option<&'a Value> {
141        self.positional_iter(stack).nth(index)
142    }
143
144    /// Get the AST node for a positional argument by index. Not usually available unless the decl
145    /// required it.
146    pub fn positional_ast<'a>(
147        &self,
148        stack: &'a Stack,
149        index: usize,
150    ) -> Option<&'a Arc<Expression>> {
151        self.arguments(stack)
152            .iter()
153            .filter_map(|arg| match arg {
154                Argument::Positional { ast, .. } => Some(ast),
155                _ => None,
156            })
157            .nth(index)
158            .and_then(|option| option.as_ref())
159    }
160
161    /// Returns every argument to the rest parameter, as well as whether each argument
162    /// is spread or a normal positional argument (true for spread, false for normal)
163    pub fn rest_iter<'a>(
164        &self,
165        stack: &'a Stack,
166        start: usize,
167    ) -> impl Iterator<Item = (&'a Value, bool)> + 'a {
168        self.arguments(stack)
169            .iter()
170            .filter_map(|arg| match arg {
171                Argument::Positional { val, .. } => Some((val, false)),
172                Argument::Spread { vals, .. } => Some((vals, true)),
173                _ => None,
174            })
175            .skip(start)
176    }
177
178    /// Returns all of the positional arguments including and after `start`, with spread arguments
179    /// flattened into a single `Vec`.
180    pub fn rest_iter_flattened(
181        &self,
182        stack: &Stack,
183        start: usize,
184    ) -> Result<Vec<Value>, ShellError> {
185        let mut acc = vec![];
186        for (rest_val, spread) in self.rest_iter(stack, start) {
187            if spread {
188                match rest_val {
189                    Value::List { vals, .. } => acc.extend(vals.iter().cloned()),
190                    Value::Error { error, .. } => return Err(ShellError::clone(error)),
191                    _ => {
192                        return Err(ShellError::CannotSpreadAsList {
193                            span: rest_val.span(),
194                        })
195                    }
196                }
197            } else {
198                acc.push(rest_val.clone());
199            }
200        }
201        Ok(acc)
202    }
203
204    /// Get a parser info argument by name.
205    pub fn get_parser_info<'a>(&self, stack: &'a Stack, name: &str) -> Option<&'a Expression> {
206        self.arguments(stack)
207            .iter()
208            .find_map(|argument| match argument {
209                Argument::ParserInfo {
210                    data,
211                    name: name_slice,
212                    info: expr,
213                } if &data[*name_slice] == name.as_bytes() => Some(expr.as_ref()),
214                _ => None,
215            })
216    }
217
218    /// Returns a span encompassing the entire call.
219    pub fn span(&self) -> Span {
220        self.span
221    }
222
223    /// Resets the [`Stack`] to its state before the call was made.
224    pub fn leave(&self, stack: &mut Stack) {
225        stack.arguments.leave_frame(self.args_base);
226    }
227}
228
229/// Utility struct for building a [`Call`] with arguments on the [`Stack`].
230pub struct CallBuilder {
231    inner: Call,
232}
233
234impl CallBuilder {
235    /// Add an argument to the [`Stack`] and reference it from the [`Call`].
236    pub fn add_argument(&mut self, stack: &mut Stack, argument: Argument) -> &mut Self {
237        if self.inner.args_len == 0 {
238            self.inner.args_base = stack.arguments.get_base();
239        }
240        self.inner.args_len += 1;
241        if let Some(span) = argument.span() {
242            self.inner.span = self.inner.span.merge(span);
243        }
244        stack.arguments.push(argument);
245        self
246    }
247
248    /// Add a positional argument to the [`Stack`] and reference it from the [`Call`].
249    pub fn add_positional(&mut self, stack: &mut Stack, span: Span, val: Value) -> &mut Self {
250        self.add_argument(
251            stack,
252            Argument::Positional {
253                span,
254                val,
255                ast: None,
256            },
257        )
258    }
259
260    /// Add a spread argument to the [`Stack`] and reference it from the [`Call`].
261    pub fn add_spread(&mut self, stack: &mut Stack, span: Span, vals: Value) -> &mut Self {
262        self.add_argument(
263            stack,
264            Argument::Spread {
265                span,
266                vals,
267                ast: None,
268            },
269        )
270    }
271
272    /// Add a flag (no-value named) argument to the [`Stack`] and reference it from the [`Call`].
273    pub fn add_flag(
274        &mut self,
275        stack: &mut Stack,
276        name: impl AsRef<str>,
277        short: impl AsRef<str>,
278        span: Span,
279    ) -> &mut Self {
280        let (data, name, short) = data_from_name_and_short(name.as_ref(), short.as_ref());
281        self.add_argument(
282            stack,
283            Argument::Flag {
284                data,
285                name,
286                short,
287                span,
288            },
289        )
290    }
291
292    /// Add a named argument to the [`Stack`] and reference it from the [`Call`].
293    pub fn add_named(
294        &mut self,
295        stack: &mut Stack,
296        name: impl AsRef<str>,
297        short: impl AsRef<str>,
298        span: Span,
299        val: Value,
300    ) -> &mut Self {
301        let (data, name, short) = data_from_name_and_short(name.as_ref(), short.as_ref());
302        self.add_argument(
303            stack,
304            Argument::Named {
305                data,
306                name,
307                short,
308                span,
309                val,
310                ast: None,
311            },
312        )
313    }
314
315    /// Produce the finished [`Call`] from the builder.
316    ///
317    /// The call should be entered / run before any other calls are constructed, because the
318    /// argument stack will be reset when they exit.
319    pub fn finish(&self) -> Call {
320        self.inner.clone()
321    }
322
323    /// Run a closure with the [`Call`] as an [`engine::Call`] reference, and then clean up the
324    /// arguments that were added to the [`Stack`] after.
325    ///
326    /// For convenience. Calls [`Call::leave`] after the closure ends.
327    pub fn with<T>(
328        self,
329        stack: &mut Stack,
330        f: impl FnOnce(&mut Stack, &engine::Call<'_>) -> T,
331    ) -> T {
332        let call = engine::Call::from(&self.inner);
333        let result = f(stack, &call);
334        self.inner.leave(stack);
335        result
336    }
337}
338
339fn data_from_name_and_short(name: &str, short: &str) -> (Arc<[u8]>, DataSlice, DataSlice) {
340    let data: Vec<u8> = name.bytes().chain(short.bytes()).collect();
341    let data: Arc<[u8]> = data.into();
342    let name = DataSlice {
343        start: 0,
344        len: name.len().try_into().expect("flag name too big"),
345    };
346    let short = DataSlice {
347        start: name.start.checked_add(name.len).expect("flag name too big"),
348        len: short.len().try_into().expect("flag short name too big"),
349    };
350    (data, name, short)
351}