You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
embed-nu/src/context/builder.rs

158 lines
4.3 KiB
Rust

use crate::{
error::CrateResult,
into_value::IntoValue,
utils::{parse_nu_script, NewEmpty},
};
use std::env;
use nu_protocol::{
ast::Block,
engine::{Command, EngineState, Stack, StateWorkingSet},
PipelineData, Span,
};
use super::{CommandGroupConfig, Context};
/// Builder to create a new nu engine state
pub struct ContextBuilder {
engine_state: EngineState,
stack: Stack,
blocks: Vec<Block>,
}
impl Default for ContextBuilder {
fn default() -> Self {
Self {
engine_state: EngineState::new(),
stack: Stack::new(),
blocks: Vec::new(),
}
}
}
impl ContextBuilder {
/// Enables certain command groups specified in the Config on the state
pub fn with_command_groups(mut self, group_config: CommandGroupConfig) -> CrateResult<Self> {
macro_rules! toggle_command_groups {
($($group:ident),*) => {
paste::item!(
$(
if group_config.$group {
super::bindings::[<bind_ $group _commands>](&mut self.engine_state)?;
}
)*
)
}
}
toggle_command_groups!(
core,
filter,
chart,
misc,
path,
system,
string,
bit,
byte,
file_system,
platform,
date,
shell,
format,
viewer,
conversion,
environment,
math,
network,
random,
generator,
hash,
experimental
);
Ok(self)
}
/// Adds a custom command to the engine that can be used inside nu expressions
/// This method can also be used to pass nu builtin commands to enable single commands
/// Instead of whole command groups
pub fn add_command<C: Command + 'static>(mut self, command: C) -> CrateResult<Self> {
let mut working_set = StateWorkingSet::new(&self.engine_state);
working_set.add_decl(Box::new(command));
let delta = working_set.render();
self.engine_state.merge_delta(delta)?;
Ok(self)
}
/// Adds a variable to the state
pub fn add_var<S: ToString, V: IntoValue>(mut self, name: S, value: V) -> CrateResult<Self> {
let mut working_set = StateWorkingSet::new(&self.engine_state);
let var_id = working_set.add_variable(
name.to_string().into_bytes(),
Span::empty(),
nu_protocol::Type::Any,
false,
);
self.stack.add_var(var_id, value.into_value());
let delta = working_set.render();
self.engine_state.merge_delta(delta)?;
Ok(self)
}
/// Adds an environment variable to the state
pub fn add_env_var<S: ToString, V: IntoValue>(mut self, name: S, value: V) -> Self {
self.engine_state
.add_env_var(name.to_string(), value.into_value());
self
}
/// Adds the environment variables of the parent process to the
/// states env variables
pub fn add_parent_env_vars(self) -> Self {
let mut builder = self;
for (name, val) in env::vars() {
builder = builder.add_env_var(name, val);
}
builder
}
/// Adds a block to the builder
/// This block is evaluated when building to put
/// the blocks contents in scope.
/// Note: Code not contained in declarations is being executed when building
/// the context
pub fn add_block(mut self, block: Block) -> Self {
self.blocks.push(block);
self
}
/// Adds a script to the context.
/// This script is being parsed so this operation can fail
pub fn add_script(mut self, contents: String) -> CrateResult<Self> {
let block = parse_nu_script(&mut self.engine_state, contents)?;
self.blocks.push(block);
Ok(self)
}
/// builds the context
pub fn build(self) -> CrateResult<Context> {
let mut ctx = Context {
engine_state: self.engine_state,
stack: self.stack,
};
for block in self.blocks {
ctx.eval_block(&block, PipelineData::empty())?;
}
Ok(ctx)
}
}