nu_engine/closure_eval.rs
1use crate::{eval::CallEval, get_eval_block_with_early_return};
2use nu_protocol::{
3 IntoPipelineData, PipelineData, PipelineMetadata, ShellError, Span, Value,
4 ast::Block,
5 engine::{Closure, EngineState, EnvName, EnvVars, Stack},
6};
7use std::{
8 borrow::Cow,
9 collections::{HashMap, HashSet},
10 sync::Arc,
11};
12
13/// [`ClosureEval`] is used to repeatedly evaluate a closure with different values/inputs.
14///
15/// [`ClosureEval`] has a builder API.
16/// It is first created via [`ClosureEval::new`],
17/// then has arguments added via [`ClosureEval::add_arg`],
18/// and then can be run using [`ClosureEval::run_with_input`].
19///
20/// ```no_run
21/// # use nu_protocol::{PipelineData, Value};
22/// # use nu_engine::ClosureEval;
23/// # let engine_state = unimplemented!();
24/// # let stack = unimplemented!();
25/// # let closure = unimplemented!();
26/// let mut closure = ClosureEval::new(engine_state, stack, closure);
27/// let iter = Vec::<Value>::new()
28/// .into_iter()
29/// .map(move |value| closure.add_arg(value).unwrap().run_with_input(PipelineData::empty()));
30/// ```
31///
32/// Many closures follow a simple, common scheme where the pipeline input and the first argument are the same value.
33/// In this case, use [`ClosureEval::run_with_value`]:
34///
35/// ```no_run
36/// # use nu_protocol::{PipelineData, Value};
37/// # use nu_engine::ClosureEval;
38/// # let engine_state = unimplemented!();
39/// # let stack = unimplemented!();
40/// # let closure = unimplemented!();
41/// let mut closure = ClosureEval::new(engine_state, stack, closure);
42/// let iter = Vec::<Value>::new()
43/// .into_iter()
44/// .map(move |value| closure.run_with_value(value));
45/// ```
46///
47/// Environment isolation and other cleanup is handled by [`ClosureEval`],
48/// so nothing needs to be done following [`ClosureEval::run_with_input`] or [`ClosureEval::run_with_value`].
49///
50/// In contrast to [`ClosureEvalOnce`], [`ClosureEval`] holds an owned copy of the
51/// [`EngineState`] supplied. This makes it possible to return it from a function that
52/// only has a borrowed [`EngineState`] reference, such as the `run` function of a
53/// [`nu_engine::command_prelude::Command`] implementation.
54#[derive(Clone)]
55pub struct ClosureEval {
56 engine_state: EngineState,
57 block: Arc<Block>,
58 env_vars: Vec<Arc<EnvVars>>,
59 env_hidden: Arc<HashMap<String, HashSet<EnvName>>>,
60 call_eval: CallEval,
61}
62
63impl ClosureEval {
64 /// Create a new [`ClosureEval`].
65 pub fn new(engine_state: &EngineState, stack: &Stack, closure: Closure) -> Self {
66 let engine_state = engine_state.clone();
67 let callee_stack = stack.captures_to_stack(closure.captures);
68 let block = engine_state.get_block(closure.block_id).clone();
69 let env_vars = stack.env_vars.clone();
70 let env_hidden = stack.env_hidden.clone();
71 let call_eval = CallEval::new(
72 callee_stack,
73 Span::unknown(),
74 block.span.unwrap_or(Span::unknown()),
75 get_eval_block_with_early_return(&engine_state),
76 );
77
78 Self {
79 engine_state,
80 block,
81 env_vars,
82 env_hidden,
83 call_eval,
84 }
85 }
86
87 pub fn new_preserve_out_dest(
88 engine_state: &EngineState,
89 stack: &Stack,
90 closure: Closure,
91 ) -> Self {
92 let engine_state = engine_state.clone();
93 let callee_stack = stack.captures_to_stack_preserve_out_dest(closure.captures);
94 let block = engine_state.get_block(closure.block_id).clone();
95 let env_vars = stack.env_vars.clone();
96 let env_hidden = stack.env_hidden.clone();
97 let call_eval = CallEval::new(
98 callee_stack,
99 Span::unknown(),
100 block.span.unwrap_or(Span::unknown()),
101 get_eval_block_with_early_return(&engine_state),
102 );
103
104 Self {
105 engine_state,
106 block,
107 env_vars,
108 env_hidden,
109 call_eval,
110 }
111 }
112
113 /// Sets whether to enable debugging when evaluating the closure.
114 ///
115 /// By default, this is controlled by the [`EngineState`] used to create this [`ClosureEval`].
116 pub fn debug(&mut self, debug: bool) -> &mut Self {
117 self.call_eval.debug(debug);
118 self
119 }
120
121 /// Add an argument [`Value`] to the closure.
122 ///
123 /// Multiple [`add_arg`](Self::add_arg) calls can be chained together,
124 /// but make sure that arguments are added based on their positional order.
125 pub fn add_arg(&mut self, value: Value) -> Result<&mut Self, ShellError> {
126 self.call_eval
127 .add_positional(&self.block.signature, Cow::Owned(value))?;
128 Ok(self)
129 }
130
131 /// Run the closure, passing the given [`PipelineData`] as input.
132 ///
133 /// Any arguments should be added beforehand via [`add_arg`](Self::add_arg).
134 pub fn run_with_input(&mut self, input: PipelineData) -> Result<PipelineData, ShellError> {
135 self.call_eval
136 .with_env(&self.env_vars, &self.env_hidden)
137 .run(&self.engine_state, &self.block, input)
138 }
139
140 /// Run the closure using the given [`Value`] as both the pipeline input and the first argument.
141 ///
142 /// Using this function after or in combination with [`add_arg`](Self::add_arg) is most likely an error.
143 /// This function is equivalent to `self.add_arg(value)` followed by `self.run_with_input(value.into_pipeline_data())`.
144 pub fn run_with_value(&mut self, value: Value) -> Result<PipelineData, ShellError> {
145 self.call_eval
146 .add_positional(&self.block.signature, Cow::Borrowed(&value))?;
147 self.run_with_input(value.into_pipeline_data())
148 }
149
150 /// Run the closure using the given [`Value`] as both the pipeline input with metadata and the first argument.
151 ///
152 /// Using this function after or in combination with [`add_arg`](Self::add_arg) is most likely an error.
153 /// This function is equivalent to `self.add_arg(value)` followed by `self.run_with_input(value.into_pipeline_data_with_metadata(metadata))`.
154 pub fn run_with_value_with_metadata(
155 &mut self,
156 value: Value,
157 metadata: Option<PipelineMetadata>,
158 ) -> Result<PipelineData, ShellError> {
159 self.call_eval
160 .add_positional(&self.block.signature, Cow::Borrowed(&value))?;
161 self.run_with_input(value.into_pipeline_data_with_metadata(metadata))
162 }
163}
164
165/// [`ClosureEvalOnce`] is used to evaluate a closure a single time.
166///
167/// [`ClosureEvalOnce`] has a builder API.
168/// It is first created via [`ClosureEvalOnce::new`],
169/// then has arguments added via [`ClosureEvalOnce::add_arg`],
170/// and then can be run using [`ClosureEvalOnce::run_with_input`].
171///
172/// ```no_run
173/// # use nu_protocol::{ListStream, PipelineData, PipelineIterator};
174/// # use nu_engine::ClosureEvalOnce;
175/// # let engine_state = unimplemented!();
176/// # let stack = unimplemented!();
177/// # let closure = unimplemented!();
178/// # let value = unimplemented!();
179/// let result = ClosureEvalOnce::new(engine_state, stack, closure)
180/// .add_arg(value)
181/// .unwrap()
182/// .run_with_input(PipelineData::empty());
183/// ```
184///
185/// Many closures follow a simple, common scheme where the pipeline input and the first argument are the same value.
186/// In this case, use [`ClosureEvalOnce::run_with_value`]:
187///
188/// ```no_run
189/// # use nu_protocol::{PipelineData, PipelineIterator};
190/// # use nu_engine::ClosureEvalOnce;
191/// # let engine_state = unimplemented!();
192/// # let stack = unimplemented!();
193/// # let closure = unimplemented!();
194/// # let value = unimplemented!();
195/// let result = ClosureEvalOnce::new(engine_state, stack, closure).run_with_value(value);
196/// ```
197///
198/// In contrast to [`ClosureEval`], the lifetime of [`ClosureEvalOnce`] is bound
199/// to that of the supplied [`EngineState`] reference, which makes it more light-weight.
200pub struct ClosureEvalOnce<'a> {
201 engine_state: &'a EngineState,
202 block: &'a Block,
203 call_eval: CallEval,
204 caller_stack: Option<&'a mut Stack>,
205}
206
207impl<'a> ClosureEvalOnce<'a> {
208 /// Create a new [`ClosureEvalOnce`].
209 pub fn new(engine_state: &'a EngineState, stack: &Stack, closure: Closure) -> Self {
210 let block = engine_state.get_block(closure.block_id);
211 let callee_stack = stack.captures_to_stack(closure.captures);
212 let call_eval = CallEval::new(
213 callee_stack,
214 Span::unknown(),
215 block.span.unwrap_or(Span::unknown()),
216 get_eval_block_with_early_return(engine_state),
217 );
218 Self {
219 engine_state,
220 block,
221 call_eval,
222 caller_stack: None,
223 }
224 }
225
226 pub fn new_preserve_out_dest(
227 engine_state: &'a EngineState,
228 stack: &Stack,
229 closure: Closure,
230 ) -> Self {
231 let block = engine_state.get_block(closure.block_id);
232 let callee_stack = stack.captures_to_stack_preserve_out_dest(closure.captures);
233 let call_eval = CallEval::new(
234 callee_stack,
235 Span::unknown(),
236 block.span.unwrap_or(Span::unknown()),
237 get_eval_block_with_early_return(engine_state),
238 );
239 Self {
240 engine_state,
241 block,
242 call_eval,
243 caller_stack: None,
244 }
245 }
246
247 pub fn new_env_preserve_out_dest(
248 engine_state: &'a EngineState,
249 stack: &'a mut Stack,
250 closure: Closure,
251 ) -> Self {
252 let block = engine_state.get_block(closure.block_id);
253 let callee_stack = stack.captures_to_stack_preserve_out_dest(closure.captures);
254 let call_eval = CallEval::new(
255 callee_stack,
256 Span::unknown(),
257 block.span.unwrap_or(Span::unknown()),
258 get_eval_block_with_early_return(engine_state),
259 );
260 Self {
261 engine_state,
262 block,
263 call_eval,
264 caller_stack: Some(stack),
265 }
266 }
267
268 /// Sets whether to enable debugging when evaluating the closure.
269 ///
270 /// By default, this is controlled by the [`EngineState`] used to create this [`ClosureEvalOnce`].
271 pub fn debug(mut self, debug: bool) -> Self {
272 self.call_eval.debug(debug);
273 self
274 }
275
276 /// Add an argument [`Value`] to the closure.
277 ///
278 /// Multiple [`add_arg`](Self::add_arg) calls can be chained together,
279 /// but make sure that arguments are added based on their positional order.
280 pub fn add_arg(mut self, value: Value) -> Result<Self, ShellError> {
281 self.call_eval
282 .add_positional(&self.block.signature, Cow::Owned(value))?;
283 Ok(self)
284 }
285
286 /// Add a list of argument [`Value`]s to the closure.
287 pub fn add_args(mut self, values: Vec<Value>) -> Result<Self, ShellError> {
288 for value in values {
289 self.call_eval
290 .add_positional(&self.block.signature, Cow::Owned(value))?;
291 }
292 Ok(self)
293 }
294
295 /// Run the closure, passing the given [`PipelineData`] as input.
296 ///
297 /// Any arguments should be added beforehand via [`add_arg`](Self::add_arg).
298 pub fn run_with_input(mut self, input: PipelineData) -> Result<PipelineData, ShellError> {
299 let result = self.call_eval.run(self.engine_state, self.block, input);
300 if let Some(caller) = self.caller_stack {
301 self.call_eval.redirect_env(self.engine_state, caller);
302 }
303 result
304 }
305
306 /// Run the closure using the given [`Value`] as both the pipeline input and the first argument.
307 ///
308 /// Using this function after or in combination with [`add_arg`](Self::add_arg) is most likely an error.
309 /// This function is equivalent to `self.add_arg(value)` followed by `self.run_with_input(value.into_pipeline_data())`.
310 pub fn run_with_value(mut self, value: Value) -> Result<PipelineData, ShellError> {
311 self.call_eval
312 .add_positional(&self.block.signature, Cow::Borrowed(&value))?;
313 self.run_with_input(value.into_pipeline_data())
314 }
315
316 /// Run the closure using the given [`Value`] as both the pipeline input with metadata and the first argument.
317 ///
318 /// Using this function after or in combination with [`add_arg`](Self::add_arg) is most likely an error.
319 /// This function is equivalent to `self.add_arg(value)` followed by `self.run_with_input(value.into_pipeline_data_with_metadata(metadata))`.
320 pub fn run_with_value_with_metadata(
321 mut self,
322 value: Value,
323 metadata: Option<PipelineMetadata>,
324 ) -> Result<PipelineData, ShellError> {
325 self.call_eval
326 .add_positional(&self.block.signature, Cow::Borrowed(&value))?;
327 self.run_with_input(value.into_pipeline_data_with_metadata(metadata))
328 }
329}