Rename builder to context builder and add context to eval scripts

pull/3/head
trivernis 2 years ago
parent 06f64abb18
commit 247550ac4e
Signed by: Trivernis
GPG Key ID: DFFFCC2C7A02DB45

@ -1,40 +1,44 @@
use crate::{
error::CrateResult,
into_value::IntoValue,
utils::{parse_nu_script, NewEmpty},
};
use std::env;
use nu_protocol::{
ast::Block,
engine::{EngineState, Stack, StateWorkingSet},
Span,
PipelineData, Span,
};
mod bindings;
mod command_group_config;
pub use command_group_config::CommandGroupConfig;
use crate::{error::CrateResult, into_value::IntoValue, utils::SpanEmpty};
use super::{CommandGroupConfig, Context};
/// Builder to create a new nu engine state
pub struct StateBuilder {
pub struct ContextBuilder {
engine_state: EngineState,
stack: Stack,
blocks: Vec<Block>,
}
impl Default for StateBuilder {
impl Default for ContextBuilder {
fn default() -> Self {
Self {
engine_state: EngineState::new(),
stack: Stack::new(),
blocks: Vec::new(),
}
}
}
impl StateBuilder {
impl ContextBuilder {
/// Enables certain command groups specified in the Config on the state
pub fn with_command_groups(&mut self, group_config: CommandGroupConfig) -> &mut Self {
pub fn with_command_groups(mut self, group_config: CommandGroupConfig) -> Self {
macro_rules! toggle_command_groups {
($($group:ident),*) => {
paste::item!(
$(
if group_config.$group {
bindings::[<bind_ $group _commands>](&mut self.engine_state);
super::bindings::[<bind_ $group _commands>](&mut self.engine_state);
}
)*
)
@ -70,11 +74,7 @@ impl StateBuilder {
}
/// Adds a variable to the state
pub fn add_var<S: ToString, V: IntoValue>(
&mut self,
name: S,
value: V,
) -> CrateResult<&mut Self> {
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(
@ -90,7 +90,7 @@ impl StateBuilder {
}
/// Adds an environment variable to the state
pub fn add_env_var<S: ToString, V: IntoValue>(&mut self, name: S, value: V) -> &mut Self {
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());
@ -99,11 +99,46 @@ impl StateBuilder {
/// Adds the environment variables of the parent process to the
/// states env variables
pub fn add_parent_env_vars(&mut self) -> &mut Self {
pub fn add_parent_env_vars(self) -> Self {
let mut builder = self;
for (name, val) in env::vars() {
self.add_env_var(name, val);
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)
}
}

@ -3,14 +3,14 @@ macro_rules! command_group_config {
($(#[doc=$doc:literal] $group:ident),*) => {
/// Enables or disables certain command groups
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Default)]
pub struct CommandGroupConfig {
$(pub(crate) $group: bool,)*
}
impl CommandGroupConfig {
/// Enables all commands
pub fn all_groups(&mut self, enabled: bool) -> &mut Self {
pub fn all_groups(mut self, enabled: bool) -> Self {
$(
self.$group = enabled;
)*
@ -22,7 +22,7 @@ macro_rules! command_group_config {
paste::item! {
#[doc=$doc]
#[inline]
pub fn [< $group _group>](&mut self, enabled: bool) -> &mut Self {
pub fn [< $group _group>](mut self, enabled: bool) -> Self {
self.$group = enabled;
self

@ -0,0 +1,68 @@
mod bindings;
mod builder;
mod command_group_config;
pub use builder::*;
pub use command_group_config::CommandGroupConfig;
use nu_protocol::{
ast::Block,
engine::{EngineState, Stack},
PipelineData,
};
use crate::{
error::{CrateError, CrateResult},
utils::parse_nu_script,
};
/// Represents the evaluation context of nu scripts and commands
/// This context is the state of the engine itself plus the stack
/// It stores variables on
#[derive(Clone)]
pub struct Context {
engine_state: EngineState,
stack: Stack,
}
impl Context {
pub fn builder() -> ContextBuilder {
ContextBuilder::default()
}
/// Evaluates the given block with the current engine context (stack plus engine state)
pub fn eval_block(&mut self, block: &Block, input: PipelineData) -> CrateResult<PipelineData> {
nu_engine::eval_block(
&self.engine_state,
&mut self.stack,
block,
input,
false,
false,
)
.map_err(CrateError::from)
}
/// Evals nu script as string with the current engine context
pub fn eval_raw<S: ToString>(
&mut self,
contents: S,
input: PipelineData,
) -> CrateResult<PipelineData> {
let block = parse_nu_script(&mut self.engine_state, contents.to_string())?;
self.eval_block(&block, input)
}
/// Prints the data of the given pipeline to stdout
pub fn print_pipeline(&mut self, pipeline: PipelineData) -> CrateResult<()> {
pipeline.print(&self.engine_state, &mut self.stack, false, false)?;
Ok(())
}
/// Prints the data of the given pipeline to stderr
pub fn print_pipeline_stderr(&mut self, pipeline: PipelineData) -> CrateResult<()> {
pipeline.print(&self.engine_state, &mut self.stack, false, true)?;
Ok(())
}
}

@ -1,3 +1,4 @@
use nu_parser::ParseError;
use nu_protocol::ShellError;
use thiserror::Error;
@ -7,4 +8,7 @@ pub type CrateResult<T> = std::result::Result<T, CrateError>;
pub enum CrateError {
#[error("Shell Error {0}")]
NuShellError(#[from] ShellError),
#[error("Parse Error {0}")]
NuParseError(#[from] ParseError),
}

@ -1,7 +1,7 @@
use nu_protocol::{Span, Value};
use rusty_value::{Fields, HashableValue, RustyValue};
use crate::utils::SpanEmpty;
use crate::utils::NewEmpty;
pub struct RawValue(Value);

@ -1,9 +1,10 @@
pub mod context;
pub(crate) mod error;
pub(crate) mod into_value;
pub mod state_builder;
pub(crate) mod utils;
pub use into_value::*;
pub use rusty_value;
pub use utils::NewEmpty;
pub type Error = error::CrateError;

@ -1,11 +1,39 @@
use nu_protocol::Span;
use nu_protocol::{
ast::Block,
engine::{EngineState, StateWorkingSet},
PipelineData, Span,
};
pub trait SpanEmpty {
use crate::error::{CrateError, CrateResult};
pub trait NewEmpty {
fn empty() -> Self;
}
impl SpanEmpty for Span {
impl NewEmpty for Span {
#[inline]
fn empty() -> Self {
Span::new(0, 0)
}
}
impl NewEmpty for PipelineData {
#[inline]
fn empty() -> Self {
Self::new(Span::empty())
}
}
pub fn parse_nu_script(engine_state: &mut EngineState, contents: String) -> CrateResult<Block> {
let mut working_set = StateWorkingSet::new(&engine_state);
let (block, err) = nu_parser::parse(&mut working_set, None, &contents.into_bytes(), false, &[]);
if let Some(err) = err {
Err(CrateError::from(err))
} else {
let delta = working_set.render();
engine_state.merge_delta(delta)?;
Ok(block)
}
}

@ -0,0 +1,22 @@
use embed_nu::{
context::{CommandGroupConfig, Context},
NewEmpty,
};
use nu_protocol::PipelineData;
#[test]
fn it_evals_strings() {
let mut ctx = get_context();
let pipeline = ctx
.eval_raw(r#"echo "Hello World""#, PipelineData::empty())
.unwrap();
ctx.print_pipeline(pipeline).unwrap()
}
fn get_context() -> Context {
Context::builder()
.with_command_groups(CommandGroupConfig::default().all_groups(true))
.add_parent_env_vars()
.build()
.unwrap()
}
Loading…
Cancel
Save