use crate::{eval::CallEval, get_eval_block_with_early_return};
use nu_protocol::{
IntoPipelineData, PipelineData, PipelineMetadata, ShellError, Span, Value,
ast::Block,
engine::{Closure, EngineState, EnvName, EnvVars, Stack},
};
use std::{
borrow::Cow,
collections::{HashMap, HashSet},
sync::Arc,
};
#[derive(Clone)]
pub struct ClosureEval {
engine_state: EngineState,
block: Arc<Block>,
env_vars: Vec<Arc<EnvVars>>,
env_hidden: Arc<HashMap<String, HashSet<EnvName>>>,
call_eval: CallEval,
}
impl ClosureEval {
pub fn new(engine_state: &EngineState, stack: &Stack, closure: Closure) -> Self {
let engine_state = engine_state.clone();
let callee_stack = stack.captures_to_stack(closure.captures);
let block = engine_state.get_block(closure.block_id).clone();
let env_vars = stack.env_vars.clone();
let env_hidden = stack.env_hidden.clone();
let call_eval = CallEval::new(
callee_stack,
Span::unknown(),
block.span.unwrap_or(Span::unknown()),
get_eval_block_with_early_return(&engine_state),
);
Self {
engine_state,
block,
env_vars,
env_hidden,
call_eval,
}
}
pub fn new_preserve_out_dest(
engine_state: &EngineState,
stack: &Stack,
closure: Closure,
) -> Self {
let engine_state = engine_state.clone();
let callee_stack = stack.captures_to_stack_preserve_out_dest(closure.captures);
let block = engine_state.get_block(closure.block_id).clone();
let env_vars = stack.env_vars.clone();
let env_hidden = stack.env_hidden.clone();
let call_eval = CallEval::new(
callee_stack,
Span::unknown(),
block.span.unwrap_or(Span::unknown()),
get_eval_block_with_early_return(&engine_state),
);
Self {
engine_state,
block,
env_vars,
env_hidden,
call_eval,
}
}
pub fn debug(&mut self, debug: bool) -> &mut Self {
self.call_eval.debug(debug);
self
}
pub fn add_arg(&mut self, value: Value) -> Result<&mut Self, ShellError> {
self.call_eval
.add_positional(&self.block.signature, Cow::Owned(value))?;
Ok(self)
}
pub fn run_with_input(&mut self, input: PipelineData) -> Result<PipelineData, ShellError> {
self.call_eval
.with_env(&self.env_vars, &self.env_hidden)
.run(&self.engine_state, &self.block, input)
}
pub fn run_with_value(&mut self, value: Value) -> Result<PipelineData, ShellError> {
self.call_eval
.add_positional(&self.block.signature, Cow::Borrowed(&value))?;
self.run_with_input(value.into_pipeline_data())
}
pub fn run_with_value_with_metadata(
&mut self,
value: Value,
metadata: Option<PipelineMetadata>,
) -> Result<PipelineData, ShellError> {
self.call_eval
.add_positional(&self.block.signature, Cow::Borrowed(&value))?;
self.run_with_input(value.into_pipeline_data_with_metadata(metadata))
}
}
pub struct ClosureEvalOnce<'a> {
engine_state: &'a EngineState,
block: &'a Block,
call_eval: CallEval,
caller_stack: Option<&'a mut Stack>,
}
impl<'a> ClosureEvalOnce<'a> {
pub fn new(engine_state: &'a EngineState, stack: &Stack, closure: Closure) -> Self {
let block = engine_state.get_block(closure.block_id);
let callee_stack = stack.captures_to_stack(closure.captures);
let call_eval = CallEval::new(
callee_stack,
Span::unknown(),
block.span.unwrap_or(Span::unknown()),
get_eval_block_with_early_return(engine_state),
);
Self {
engine_state,
block,
call_eval,
caller_stack: None,
}
}
pub fn new_preserve_out_dest(
engine_state: &'a EngineState,
stack: &Stack,
closure: Closure,
) -> Self {
let block = engine_state.get_block(closure.block_id);
let callee_stack = stack.captures_to_stack_preserve_out_dest(closure.captures);
let call_eval = CallEval::new(
callee_stack,
Span::unknown(),
block.span.unwrap_or(Span::unknown()),
get_eval_block_with_early_return(engine_state),
);
Self {
engine_state,
block,
call_eval,
caller_stack: None,
}
}
pub fn new_env_preserve_out_dest(
engine_state: &'a EngineState,
stack: &'a mut Stack,
closure: Closure,
) -> Self {
let block = engine_state.get_block(closure.block_id);
let callee_stack = stack.captures_to_stack_preserve_out_dest(closure.captures);
let call_eval = CallEval::new(
callee_stack,
Span::unknown(),
block.span.unwrap_or(Span::unknown()),
get_eval_block_with_early_return(engine_state),
);
Self {
engine_state,
block,
call_eval,
caller_stack: Some(stack),
}
}
pub fn debug(mut self, debug: bool) -> Self {
self.call_eval.debug(debug);
self
}
pub fn add_arg(mut self, value: Value) -> Result<Self, ShellError> {
self.call_eval
.add_positional(&self.block.signature, Cow::Owned(value))?;
Ok(self)
}
pub fn add_args(mut self, values: Vec<Value>) -> Result<Self, ShellError> {
for value in values {
self.call_eval
.add_positional(&self.block.signature, Cow::Owned(value))?;
}
Ok(self)
}
pub fn run_with_input(mut self, input: PipelineData) -> Result<PipelineData, ShellError> {
let result = self.call_eval.run(self.engine_state, self.block, input);
if let Some(caller) = self.caller_stack {
self.call_eval.redirect_env(self.engine_state, caller);
}
result
}
pub fn run_with_value(mut self, value: Value) -> Result<PipelineData, ShellError> {
self.call_eval
.add_positional(&self.block.signature, Cow::Borrowed(&value))?;
self.run_with_input(value.into_pipeline_data())
}
pub fn run_with_value_with_metadata(
mut self,
value: Value,
metadata: Option<PipelineMetadata>,
) -> Result<PipelineData, ShellError> {
self.call_eval
.add_positional(&self.block.signature, Cow::Borrowed(&value))?;
self.run_with_input(value.into_pipeline_data_with_metadata(metadata))
}
}