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#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
14pub enum Argument {
15    Positional(Expression),
21    Named((Spanned<String>, Option<Spanned<String>>, Option<Expression>)),
30    Unknown(Expression),
32    Spread(Expression),
34}
35
36impl Argument {
37    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#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
72pub enum ExternalArgument {
73    Regular(Expression),
75    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#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
96pub struct Call {
97    pub decl_id: DeclId,
99    pub head: Span,
100    pub arguments: Vec<Argument>,
101    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    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, _ => 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    pub fn rest_iter(&self, start: usize) -> impl Iterator<Item = (&Expression, bool)> {
202        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    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                    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}