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}