use crate::{
EvalBlockWithEarlyReturnFn, eval_block_with_early_return, get_eval_block_with_early_return,
};
use nu_protocol::{
IntoPipelineData, PipelineData, ShellError, Value,
ast::Block,
debugger::{WithDebug, WithoutDebug},
engine::{Closure, EngineState, EnvName, EnvVars, Stack},
};
use std::{
borrow::Cow,
collections::{HashMap, HashSet},
sync::Arc,
};
fn eval_fn(debug: bool) -> EvalBlockWithEarlyReturnFn {
if debug {
eval_block_with_early_return::<WithDebug>
} else {
eval_block_with_early_return::<WithoutDebug>
}
}
#[derive(Clone)]
pub struct ClosureEval {
engine_state: EngineState,
stack: Stack,
block: Arc<Block>,
arg_index: usize,
env_vars: Vec<Arc<EnvVars>>,
env_hidden: Arc<HashMap<String, HashSet<EnvName>>>,
eval: EvalBlockWithEarlyReturnFn,
}
impl ClosureEval {
pub fn new(engine_state: &EngineState, stack: &Stack, closure: Closure) -> Self {
let engine_state = engine_state.clone();
let 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 eval = get_eval_block_with_early_return(&engine_state);
Self {
engine_state,
stack,
block,
arg_index: 0,
env_vars,
env_hidden,
eval,
}
}
pub fn new_preserve_out_dest(
engine_state: &EngineState,
stack: &Stack,
closure: Closure,
) -> Self {
let engine_state = engine_state.clone();
let 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 eval = get_eval_block_with_early_return(&engine_state);
Self {
engine_state,
stack,
block,
arg_index: 0,
env_vars,
env_hidden,
eval,
}
}
pub fn debug(&mut self, debug: bool) -> &mut Self {
self.eval = eval_fn(debug);
self
}
fn try_add_arg(&mut self, value: Cow<Value>) {
if let Some(var_id) = self
.block
.signature
.get_positional(self.arg_index)
.and_then(|var| var.var_id)
{
self.stack.add_var(var_id, value.into_owned());
self.arg_index += 1;
}
}
pub fn add_arg(&mut self, value: Value) -> &mut Self {
self.try_add_arg(Cow::Owned(value));
self
}
pub fn run_with_input(&mut self, input: PipelineData) -> Result<PipelineData, ShellError> {
self.arg_index = 0;
self.stack.with_env(&self.env_vars, &self.env_hidden);
(self.eval)(&self.engine_state, &mut self.stack, &self.block, input).map(|p| p.body)
}
pub fn run_with_value(&mut self, value: Value) -> Result<PipelineData, ShellError> {
self.try_add_arg(Cow::Borrowed(&value));
self.run_with_input(value.into_pipeline_data())
}
}
pub struct ClosureEvalOnce<'a> {
engine_state: &'a EngineState,
stack: Stack,
block: &'a Block,
arg_index: usize,
eval: EvalBlockWithEarlyReturnFn,
}
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 eval = get_eval_block_with_early_return(engine_state);
Self {
engine_state,
stack: stack.captures_to_stack(closure.captures),
block,
arg_index: 0,
eval,
}
}
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 eval = get_eval_block_with_early_return(engine_state);
Self {
engine_state,
stack: stack.captures_to_stack_preserve_out_dest(closure.captures),
block,
arg_index: 0,
eval,
}
}
pub fn debug(mut self, debug: bool) -> Self {
self.eval = eval_fn(debug);
self
}
fn try_add_arg(&mut self, value: Cow<Value>) {
if let Some(var_id) = self
.block
.signature
.get_positional(self.arg_index)
.and_then(|var| var.var_id)
{
self.stack.add_var(var_id, value.into_owned());
self.arg_index += 1;
}
}
pub fn add_arg(mut self, value: Value) -> Self {
self.try_add_arg(Cow::Owned(value));
self
}
pub fn run_with_input(mut self, input: PipelineData) -> Result<PipelineData, ShellError> {
(self.eval)(self.engine_state, &mut self.stack, self.block, input).map(|p| p.body)
}
pub fn run_with_value(mut self, value: Value) -> Result<PipelineData, ShellError> {
self.try_add_arg(Cow::Borrowed(&value));
self.run_with_input(value.into_pipeline_data())
}
}