1use crate::eval_expression;
2use nu_protocol::{
3 FromValue, ShellError, Span, Value, ast,
4 debugger::WithoutDebug,
5 engine::{self, EngineState, Stack, StateWorkingSet},
6 eval_const::eval_constant,
7 ir,
8};
9
10pub trait CallExt {
11 fn has_flag(
13 &self,
14 engine_state: &EngineState,
15 stack: &mut Stack,
16 flag_name: &str,
17 ) -> Result<bool, ShellError>;
18
19 fn get_flag<T: FromValue>(
20 &self,
21 engine_state: &EngineState,
22 stack: &mut Stack,
23 name: &str,
24 ) -> Result<Option<T>, ShellError>;
25
26 fn get_flag_span(&self, stack: &Stack, name: &str) -> Option<Span>;
28
29 fn rest<T: FromValue>(
30 &self,
31 engine_state: &EngineState,
32 stack: &mut Stack,
33 starting_pos: usize,
34 ) -> Result<Vec<T>, ShellError>;
35
36 fn opt<T: FromValue>(
37 &self,
38 engine_state: &EngineState,
39 stack: &mut Stack,
40 pos: usize,
41 ) -> Result<Option<T>, ShellError>;
42
43 fn opt_const<T: FromValue>(
44 &self,
45 working_set: &StateWorkingSet,
46 pos: usize,
47 ) -> Result<Option<T>, ShellError>;
48
49 fn req<T: FromValue>(
50 &self,
51 engine_state: &EngineState,
52 stack: &mut Stack,
53 pos: usize,
54 ) -> Result<T, ShellError>;
55
56 fn req_parser_info<T: FromValue>(
57 &self,
58 engine_state: &EngineState,
59 stack: &mut Stack,
60 name: &str,
61 ) -> Result<T, ShellError>;
62
63 fn has_positional_args(&self, stack: &Stack, starting_pos: usize) -> bool;
65}
66
67impl CallExt for ast::Call {
68 fn has_flag(
69 &self,
70 engine_state: &EngineState,
71 stack: &mut Stack,
72 flag_name: &str,
73 ) -> Result<bool, ShellError> {
74 for name in self.named_iter() {
75 if flag_name == name.0.item {
76 return if let Some(expr) = &name.2 {
77 let stack = &mut stack.use_call_arg_out_dest();
79 let result = eval_expression::<WithoutDebug>(engine_state, stack, expr)?;
80 match result {
81 Value::Bool { val, .. } => Ok(val),
82 _ => Err(ShellError::CantConvert {
83 to_type: "bool".into(),
84 from_type: result.get_type().to_string(),
85 span: result.span(),
86 help: Some("".into()),
87 }),
88 }
89 } else {
90 Ok(true)
91 };
92 }
93 }
94
95 Ok(false)
96 }
97
98 fn get_flag<T: FromValue>(
99 &self,
100 engine_state: &EngineState,
101 stack: &mut Stack,
102 name: &str,
103 ) -> Result<Option<T>, ShellError> {
104 if let Some(expr) = self.get_flag_expr(name) {
105 let stack = &mut stack.use_call_arg_out_dest();
106 let result = eval_expression::<WithoutDebug>(engine_state, stack, expr)?;
107 FromValue::from_value(result).map(Some)
108 } else {
109 Ok(None)
110 }
111 }
112
113 fn get_flag_span(&self, _stack: &Stack, name: &str) -> Option<Span> {
114 self.get_named_arg(name).map(|arg| arg.span)
115 }
116
117 fn rest<T: FromValue>(
118 &self,
119 engine_state: &EngineState,
120 stack: &mut Stack,
121 starting_pos: usize,
122 ) -> Result<Vec<T>, ShellError> {
123 let stack = &mut stack.use_call_arg_out_dest();
124 self.rest_iter_flattened(starting_pos, |expr| {
125 eval_expression::<WithoutDebug>(engine_state, stack, expr)
126 })?
127 .into_iter()
128 .map(FromValue::from_value)
129 .collect()
130 }
131
132 fn opt<T: FromValue>(
133 &self,
134 engine_state: &EngineState,
135 stack: &mut Stack,
136 pos: usize,
137 ) -> Result<Option<T>, ShellError> {
138 if let Some(expr) = self.positional_nth(pos) {
139 let stack = &mut stack.use_call_arg_out_dest();
140 let result = eval_expression::<WithoutDebug>(engine_state, stack, expr)?;
141 if result.is_nothing() {
142 Ok(None)
143 } else {
144 FromValue::from_value(result).map(Some)
145 }
146 } else {
147 Ok(None)
148 }
149 }
150
151 fn opt_const<T: FromValue>(
152 &self,
153 working_set: &StateWorkingSet,
154 pos: usize,
155 ) -> Result<Option<T>, ShellError> {
156 if let Some(expr) = self.positional_nth(pos) {
157 let result = eval_constant(working_set, expr)?;
158 if result.is_nothing() {
159 Ok(None)
160 } else {
161 FromValue::from_value(result).map(Some)
162 }
163 } else {
164 Ok(None)
165 }
166 }
167
168 fn req<T: FromValue>(
169 &self,
170 engine_state: &EngineState,
171 stack: &mut Stack,
172 pos: usize,
173 ) -> Result<T, ShellError> {
174 if let Some(expr) = self.positional_nth(pos) {
175 let stack = &mut stack.use_call_arg_out_dest();
176 let result = eval_expression::<WithoutDebug>(engine_state, stack, expr)?;
177 FromValue::from_value(result)
178 } else if self.positional_len() == 0 {
179 Err(ShellError::AccessEmptyContent { span: self.head })
180 } else {
181 Err(ShellError::AccessBeyondEnd {
182 max_idx: self.positional_len() - 1,
183 span: self.head,
184 })
185 }
186 }
187
188 fn req_parser_info<T: FromValue>(
189 &self,
190 engine_state: &EngineState,
191 stack: &mut Stack,
192 name: &str,
193 ) -> Result<T, ShellError> {
194 if let Some(expr) = self.get_parser_info(name) {
195 let stack = &mut stack.use_call_arg_out_dest();
196 let result = eval_expression::<WithoutDebug>(engine_state, stack, expr)?;
197 FromValue::from_value(result)
198 } else if self.parser_info.is_empty() {
199 Err(ShellError::AccessEmptyContent { span: self.head })
200 } else {
201 Err(ShellError::AccessBeyondEnd {
202 max_idx: self.parser_info.len() - 1,
203 span: self.head,
204 })
205 }
206 }
207
208 fn has_positional_args(&self, _stack: &Stack, starting_pos: usize) -> bool {
209 self.rest_iter(starting_pos).next().is_some()
210 }
211}
212
213impl CallExt for ir::Call {
214 fn has_flag(
215 &self,
216 _engine_state: &EngineState,
217 stack: &mut Stack,
218 flag_name: &str,
219 ) -> Result<bool, ShellError> {
220 Ok(self
221 .named_iter(stack)
222 .find(|(name, _)| name.item == flag_name)
223 .is_some_and(|(_, value)| {
224 !matches!(value, Some(Value::Bool { val: false, .. }))
226 }))
227 }
228
229 fn get_flag<T: FromValue>(
230 &self,
231 _engine_state: &EngineState,
232 stack: &mut Stack,
233 name: &str,
234 ) -> Result<Option<T>, ShellError> {
235 if let Some(val) = self.get_named_arg(stack, name) {
236 T::from_value(val.clone()).map(Some)
237 } else {
238 Ok(None)
239 }
240 }
241
242 fn get_flag_span(&self, stack: &Stack, name: &str) -> Option<Span> {
243 self.named_iter(stack)
244 .find_map(|(i_name, _)| (i_name.item == name).then_some(i_name.span))
245 }
246
247 fn rest<T: FromValue>(
248 &self,
249 _engine_state: &EngineState,
250 stack: &mut Stack,
251 starting_pos: usize,
252 ) -> Result<Vec<T>, ShellError> {
253 self.rest_iter_flattened(stack, starting_pos)?
254 .into_iter()
255 .map(T::from_value)
256 .collect()
257 }
258
259 fn opt<T: FromValue>(
260 &self,
261 _engine_state: &EngineState,
262 stack: &mut Stack,
263 pos: usize,
264 ) -> Result<Option<T>, ShellError> {
265 self.positional_iter(stack)
266 .nth(pos)
267 .filter(|v| !v.is_nothing())
268 .cloned()
269 .map(T::from_value)
270 .transpose()
271 }
272
273 fn opt_const<T: FromValue>(
274 &self,
275 _working_set: &StateWorkingSet,
276 _pos: usize,
277 ) -> Result<Option<T>, ShellError> {
278 Err(ShellError::IrEvalError {
279 msg: "const evaluation is not yet implemented on ir::Call".into(),
280 span: Some(self.head),
281 })
282 }
283
284 fn req<T: FromValue>(
285 &self,
286 _engine_state: &EngineState,
287 stack: &mut Stack,
288 pos: usize,
289 ) -> Result<T, ShellError> {
290 if let Some(val) = self.positional_iter(stack).nth(pos).cloned() {
291 T::from_value(val)
292 } else if self.positional_len(stack) == 0 {
293 Err(ShellError::AccessEmptyContent { span: self.head })
294 } else {
295 Err(ShellError::AccessBeyondEnd {
296 max_idx: self.positional_len(stack) - 1,
297 span: self.head,
298 })
299 }
300 }
301
302 fn req_parser_info<T: FromValue>(
303 &self,
304 engine_state: &EngineState,
305 stack: &mut Stack,
306 name: &str,
307 ) -> Result<T, ShellError> {
308 if let Some(expr) = self.get_parser_info(stack, name) {
312 let expr = expr.clone();
313 let stack = &mut stack.use_call_arg_out_dest();
314 let result = eval_expression::<WithoutDebug>(engine_state, stack, &expr)?;
315 FromValue::from_value(result)
316 } else {
317 Err(ShellError::CantFindColumn {
318 col_name: name.into(),
319 span: None,
320 src_span: self.head,
321 })
322 }
323 }
324
325 fn has_positional_args(&self, stack: &Stack, starting_pos: usize) -> bool {
326 self.rest_iter(stack, starting_pos).next().is_some()
327 }
328}
329
330macro_rules! proxy {
331 ($self:ident . $method:ident ($($param:expr),*)) => (match &$self.inner {
332 engine::CallImpl::AstRef(call) => call.$method($($param),*),
333 engine::CallImpl::AstBox(call) => call.$method($($param),*),
334 engine::CallImpl::IrRef(call) => call.$method($($param),*),
335 engine::CallImpl::IrBox(call) => call.$method($($param),*),
336 })
337}
338
339impl CallExt for engine::Call<'_> {
340 fn has_flag(
341 &self,
342 engine_state: &EngineState,
343 stack: &mut Stack,
344 flag_name: &str,
345 ) -> Result<bool, ShellError> {
346 proxy!(self.has_flag(engine_state, stack, flag_name))
347 }
348
349 fn get_flag<T: FromValue>(
350 &self,
351 engine_state: &EngineState,
352 stack: &mut Stack,
353 name: &str,
354 ) -> Result<Option<T>, ShellError> {
355 proxy!(self.get_flag(engine_state, stack, name))
356 }
357
358 fn get_flag_span(&self, stack: &Stack, name: &str) -> Option<Span> {
359 proxy!(self.get_flag_span(stack, name))
360 }
361
362 fn rest<T: FromValue>(
363 &self,
364 engine_state: &EngineState,
365 stack: &mut Stack,
366 starting_pos: usize,
367 ) -> Result<Vec<T>, ShellError> {
368 proxy!(self.rest(engine_state, stack, starting_pos))
369 }
370
371 fn opt<T: FromValue>(
372 &self,
373 engine_state: &EngineState,
374 stack: &mut Stack,
375 pos: usize,
376 ) -> Result<Option<T>, ShellError> {
377 proxy!(self.opt(engine_state, stack, pos))
378 }
379
380 fn opt_const<T: FromValue>(
381 &self,
382 working_set: &StateWorkingSet,
383 pos: usize,
384 ) -> Result<Option<T>, ShellError> {
385 proxy!(self.opt_const(working_set, pos))
386 }
387
388 fn req<T: FromValue>(
389 &self,
390 engine_state: &EngineState,
391 stack: &mut Stack,
392 pos: usize,
393 ) -> Result<T, ShellError> {
394 proxy!(self.req(engine_state, stack, pos))
395 }
396
397 fn req_parser_info<T: FromValue>(
398 &self,
399 engine_state: &EngineState,
400 stack: &mut Stack,
401 name: &str,
402 ) -> Result<T, ShellError> {
403 proxy!(self.req_parser_info(engine_state, stack, name))
404 }
405
406 fn has_positional_args(&self, stack: &Stack, starting_pos: usize) -> bool {
407 proxy!(self.has_positional_args(stack, starting_pos))
408 }
409}