1use crate::eval_expression;
2use nu_protocol::{
3 ast,
4 debugger::WithoutDebug,
5 engine::{self, EngineState, Stack, StateWorkingSet},
6 eval_const::eval_constant,
7 ir, FromValue, ShellError, Span, Value,
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 FromValue::from_value(result).map(Some)
142 } else {
143 Ok(None)
144 }
145 }
146
147 fn opt_const<T: FromValue>(
148 &self,
149 working_set: &StateWorkingSet,
150 pos: usize,
151 ) -> Result<Option<T>, ShellError> {
152 if let Some(expr) = self.positional_nth(pos) {
153 let result = eval_constant(working_set, expr)?;
154 FromValue::from_value(result).map(Some)
155 } else {
156 Ok(None)
157 }
158 }
159
160 fn req<T: FromValue>(
161 &self,
162 engine_state: &EngineState,
163 stack: &mut Stack,
164 pos: usize,
165 ) -> Result<T, ShellError> {
166 if let Some(expr) = self.positional_nth(pos) {
167 let stack = &mut stack.use_call_arg_out_dest();
168 let result = eval_expression::<WithoutDebug>(engine_state, stack, expr)?;
169 FromValue::from_value(result)
170 } else if self.positional_len() == 0 {
171 Err(ShellError::AccessEmptyContent { span: self.head })
172 } else {
173 Err(ShellError::AccessBeyondEnd {
174 max_idx: self.positional_len() - 1,
175 span: self.head,
176 })
177 }
178 }
179
180 fn req_parser_info<T: FromValue>(
181 &self,
182 engine_state: &EngineState,
183 stack: &mut Stack,
184 name: &str,
185 ) -> Result<T, ShellError> {
186 if let Some(expr) = self.get_parser_info(name) {
187 let stack = &mut stack.use_call_arg_out_dest();
188 let result = eval_expression::<WithoutDebug>(engine_state, stack, expr)?;
189 FromValue::from_value(result)
190 } else if self.parser_info.is_empty() {
191 Err(ShellError::AccessEmptyContent { span: self.head })
192 } else {
193 Err(ShellError::AccessBeyondEnd {
194 max_idx: self.parser_info.len() - 1,
195 span: self.head,
196 })
197 }
198 }
199
200 fn has_positional_args(&self, _stack: &Stack, starting_pos: usize) -> bool {
201 self.rest_iter(starting_pos).next().is_some()
202 }
203}
204
205impl CallExt for ir::Call {
206 fn has_flag(
207 &self,
208 _engine_state: &EngineState,
209 stack: &mut Stack,
210 flag_name: &str,
211 ) -> Result<bool, ShellError> {
212 Ok(self
213 .named_iter(stack)
214 .find(|(name, _)| name.item == flag_name)
215 .is_some_and(|(_, value)| {
216 !matches!(value, Some(Value::Bool { val: false, .. }))
218 }))
219 }
220
221 fn get_flag<T: FromValue>(
222 &self,
223 _engine_state: &EngineState,
224 stack: &mut Stack,
225 name: &str,
226 ) -> Result<Option<T>, ShellError> {
227 if let Some(val) = self.get_named_arg(stack, name) {
228 T::from_value(val.clone()).map(Some)
229 } else {
230 Ok(None)
231 }
232 }
233
234 fn get_flag_span(&self, stack: &Stack, name: &str) -> Option<Span> {
235 self.named_iter(stack)
236 .find_map(|(i_name, _)| (i_name.item == name).then_some(i_name.span))
237 }
238
239 fn rest<T: FromValue>(
240 &self,
241 _engine_state: &EngineState,
242 stack: &mut Stack,
243 starting_pos: usize,
244 ) -> Result<Vec<T>, ShellError> {
245 self.rest_iter_flattened(stack, starting_pos)?
246 .into_iter()
247 .map(T::from_value)
248 .collect()
249 }
250
251 fn opt<T: FromValue>(
252 &self,
253 _engine_state: &EngineState,
254 stack: &mut Stack,
255 pos: usize,
256 ) -> Result<Option<T>, ShellError> {
257 self.positional_iter(stack)
258 .nth(pos)
259 .cloned()
260 .map(T::from_value)
261 .transpose()
262 }
263
264 fn opt_const<T: FromValue>(
265 &self,
266 _working_set: &StateWorkingSet,
267 _pos: usize,
268 ) -> Result<Option<T>, ShellError> {
269 Err(ShellError::IrEvalError {
270 msg: "const evaluation is not yet implemented on ir::Call".into(),
271 span: Some(self.head),
272 })
273 }
274
275 fn req<T: FromValue>(
276 &self,
277 engine_state: &EngineState,
278 stack: &mut Stack,
279 pos: usize,
280 ) -> Result<T, ShellError> {
281 if let Some(val) = self.opt(engine_state, stack, pos)? {
282 Ok(val)
283 } else if self.positional_len(stack) == 0 {
284 Err(ShellError::AccessEmptyContent { span: self.head })
285 } else {
286 Err(ShellError::AccessBeyondEnd {
287 max_idx: self.positional_len(stack) - 1,
288 span: self.head,
289 })
290 }
291 }
292
293 fn req_parser_info<T: FromValue>(
294 &self,
295 engine_state: &EngineState,
296 stack: &mut Stack,
297 name: &str,
298 ) -> Result<T, ShellError> {
299 if let Some(expr) = self.get_parser_info(stack, name) {
303 let expr = expr.clone();
304 let stack = &mut stack.use_call_arg_out_dest();
305 let result = eval_expression::<WithoutDebug>(engine_state, stack, &expr)?;
306 FromValue::from_value(result)
307 } else {
308 Err(ShellError::CantFindColumn {
309 col_name: name.into(),
310 span: None,
311 src_span: self.head,
312 })
313 }
314 }
315
316 fn has_positional_args(&self, stack: &Stack, starting_pos: usize) -> bool {
317 self.rest_iter(stack, starting_pos).next().is_some()
318 }
319}
320
321macro_rules! proxy {
322 ($self:ident . $method:ident ($($param:expr),*)) => (match &$self.inner {
323 engine::CallImpl::AstRef(call) => call.$method($($param),*),
324 engine::CallImpl::AstBox(call) => call.$method($($param),*),
325 engine::CallImpl::IrRef(call) => call.$method($($param),*),
326 engine::CallImpl::IrBox(call) => call.$method($($param),*),
327 })
328}
329
330impl CallExt for engine::Call<'_> {
331 fn has_flag(
332 &self,
333 engine_state: &EngineState,
334 stack: &mut Stack,
335 flag_name: &str,
336 ) -> Result<bool, ShellError> {
337 proxy!(self.has_flag(engine_state, stack, flag_name))
338 }
339
340 fn get_flag<T: FromValue>(
341 &self,
342 engine_state: &EngineState,
343 stack: &mut Stack,
344 name: &str,
345 ) -> Result<Option<T>, ShellError> {
346 proxy!(self.get_flag(engine_state, stack, name))
347 }
348
349 fn get_flag_span(&self, stack: &Stack, name: &str) -> Option<Span> {
350 proxy!(self.get_flag_span(stack, name))
351 }
352
353 fn rest<T: FromValue>(
354 &self,
355 engine_state: &EngineState,
356 stack: &mut Stack,
357 starting_pos: usize,
358 ) -> Result<Vec<T>, ShellError> {
359 proxy!(self.rest(engine_state, stack, starting_pos))
360 }
361
362 fn opt<T: FromValue>(
363 &self,
364 engine_state: &EngineState,
365 stack: &mut Stack,
366 pos: usize,
367 ) -> Result<Option<T>, ShellError> {
368 proxy!(self.opt(engine_state, stack, pos))
369 }
370
371 fn opt_const<T: FromValue>(
372 &self,
373 working_set: &StateWorkingSet,
374 pos: usize,
375 ) -> Result<Option<T>, ShellError> {
376 proxy!(self.opt_const(working_set, pos))
377 }
378
379 fn req<T: FromValue>(
380 &self,
381 engine_state: &EngineState,
382 stack: &mut Stack,
383 pos: usize,
384 ) -> Result<T, ShellError> {
385 proxy!(self.req(engine_state, stack, pos))
386 }
387
388 fn req_parser_info<T: FromValue>(
389 &self,
390 engine_state: &EngineState,
391 stack: &mut Stack,
392 name: &str,
393 ) -> Result<T, ShellError> {
394 proxy!(self.req_parser_info(engine_state, stack, name))
395 }
396
397 fn has_positional_args(&self, stack: &Stack, starting_pos: usize) -> bool {
398 proxy!(self.has_positional_args(stack, starting_pos))
399 }
400}