Add support for adding custom commands to the engine

pull/3/head
trivernis 2 years ago
parent aac17a0226
commit 875295f0f4
Signed by: Trivernis
GPG Key ID: DFFFCC2C7A02DB45

@ -16,6 +16,7 @@ use embed_nu::{rusty_value::*, CommandGroupConfig, Context, NewEmpty, PipelineDa
fn main() {
let mut ctx = Context::builder()
.with_command_groups(CommandGroupConfig::default().all_groups(true))
.unwrap()
.add_parent_env_vars()
.build()
.unwrap();
@ -73,7 +74,7 @@ fn main() {
bar: 12
};
// convert this struct into a nu value
// this is also done implicitely when passing the value to the nu context
// this is also done implicitly when passing the value to the nu context
// as function arguments or variables
let value = instance.into_value();
dbg!(value);

@ -1,15 +1,17 @@
use nu_command::*;
use nu_protocol::engine::{EngineState, StateWorkingSet};
use crate::error::CrateResult;
macro_rules! bind_commands {
($engine_state:expr, $( $command:expr),* $(,)? ) => {
bind($engine_state, |working_set| {
$( working_set.add_decl(Box::new($command)); )*
});
})
};
}
pub fn bind_core_commands(engine_state: &mut EngineState) {
pub fn bind_core_commands(engine_state: &mut EngineState) -> CrateResult<()> {
bind_commands!(
engine_state,
Alias,
@ -45,14 +47,14 @@ pub fn bind_core_commands(engine_state: &mut EngineState) {
Module,
Use,
Version,
);
)
}
pub fn bind_chart_commands(engine_state: &mut EngineState) {
bind_commands!(engine_state, Histogram);
pub fn bind_chart_commands(engine_state: &mut EngineState) -> CrateResult<()> {
bind_commands!(engine_state, Histogram)
}
pub fn bind_filter_commands(engine_state: &mut EngineState) {
pub fn bind_filter_commands(engine_state: &mut EngineState) -> CrateResult<()> {
bind_commands! {
engine_state,
All,
@ -116,14 +118,14 @@ pub fn bind_filter_commands(engine_state: &mut EngineState) {
Window,
Wrap,
Zip,
};
}
}
pub fn bind_misc_commands(engine_state: &mut EngineState) {
bind_commands!(engine_state, History, Tutor, HistorySession);
pub fn bind_misc_commands(engine_state: &mut EngineState) -> CrateResult<()> {
bind_commands!(engine_state, History, Tutor, HistorySession)
}
pub fn bind_path_commands(engine_state: &mut EngineState) {
pub fn bind_path_commands(engine_state: &mut EngineState) -> CrateResult<()> {
bind_commands! {
engine_state,
Path,
@ -136,10 +138,10 @@ pub fn bind_path_commands(engine_state: &mut EngineState) {
PathRelativeTo,
PathSplit,
PathType,
};
}
}
pub fn bind_system_commands(engine_state: &mut EngineState) {
pub fn bind_system_commands(engine_state: &mut EngineState) -> CrateResult<()> {
bind_commands! {
engine_state,
Benchmark,
@ -149,10 +151,10 @@ pub fn bind_system_commands(engine_state: &mut EngineState) {
NuCheck,
Sys,
Ps,
};
}
}
pub fn bind_string_commands(engine_state: &mut EngineState) {
pub fn bind_string_commands(engine_state: &mut EngineState) -> CrateResult<()> {
bind_commands! {
engine_state,
BuildString,
@ -195,10 +197,10 @@ pub fn bind_string_commands(engine_state: &mut EngineState) {
StrTrim,
StrTitleCase,
StrUpcase
};
}
}
pub fn bind_bit_commands(engine_state: &mut EngineState) {
pub fn bind_bit_commands(engine_state: &mut EngineState) -> CrateResult<()> {
bind_commands! {
engine_state,
Bits,
@ -213,7 +215,7 @@ pub fn bind_bit_commands(engine_state: &mut EngineState) {
}
}
pub fn bind_byte_commands(engine_state: &mut EngineState) {
pub fn bind_byte_commands(engine_state: &mut EngineState) -> CrateResult<()> {
bind_commands! {
engine_state,
Bytes,
@ -231,7 +233,7 @@ pub fn bind_byte_commands(engine_state: &mut EngineState) {
}
}
pub fn bind_file_system_commands(engine_state: &mut EngineState) {
pub fn bind_file_system_commands(engine_state: &mut EngineState) -> CrateResult<()> {
bind_commands! {
engine_state,
Cd,
@ -245,10 +247,10 @@ pub fn bind_file_system_commands(engine_state: &mut EngineState) {
Touch,
Glob,
Watch,
};
}
}
pub fn bind_platform_commands(engine_state: &mut EngineState) {
pub fn bind_platform_commands(engine_state: &mut EngineState) -> CrateResult<()> {
bind_commands! {
engine_state,
Ansi,
@ -264,10 +266,10 @@ pub fn bind_platform_commands(engine_state: &mut EngineState) {
KeybindingsList,
Sleep,
TermSize,
};
}
}
pub fn bind_date_commands(engine_state: &mut EngineState) {
pub fn bind_date_commands(engine_state: &mut EngineState) -> CrateResult<()> {
bind_commands! {
engine_state,
Date,
@ -278,10 +280,10 @@ pub fn bind_date_commands(engine_state: &mut EngineState) {
DateToRecord,
DateToTable,
DateToTimezone,
};
}
}
pub fn bind_shell_commands(engine_state: &mut EngineState) {
pub fn bind_shell_commands(engine_state: &mut EngineState) -> CrateResult<()> {
bind_commands! {
engine_state,
Enter,
@ -290,10 +292,10 @@ pub fn bind_shell_commands(engine_state: &mut EngineState) {
NextShell,
PrevShell,
Shells,
};
}
}
pub fn bind_format_commands(engine_state: &mut EngineState) {
pub fn bind_format_commands(engine_state: &mut EngineState) -> CrateResult<()> {
bind_commands! {
engine_state,
From,
@ -330,18 +332,18 @@ pub fn bind_format_commands(engine_state: &mut EngineState) {
ToUrl,
ToXml,
ToYaml,
};
}
}
pub fn bind_viewer_commands(engine_state: &mut EngineState) {
pub fn bind_viewer_commands(engine_state: &mut EngineState) -> CrateResult<()> {
bind_commands! {
engine_state,
Griddle,
Table,
};
}
}
pub fn bind_conversion_commands(engine_state: &mut EngineState) {
pub fn bind_conversion_commands(engine_state: &mut EngineState) -> CrateResult<()> {
bind_commands! {
engine_state,
Fmt,
@ -354,10 +356,10 @@ pub fn bind_conversion_commands(engine_state: &mut EngineState) {
IntoFilesize,
IntoInt,
IntoString,
};
}
}
pub fn bind_environment_commands(engine_state: &mut EngineState) {
pub fn bind_environment_commands(engine_state: &mut EngineState) -> CrateResult<()> {
bind_commands! {
engine_state,
Env,
@ -368,10 +370,10 @@ pub fn bind_environment_commands(engine_state: &mut EngineState) {
WithEnv,
// nu config commands have been removed as editing isn't possible
// in this environment
};
}
}
pub fn bind_math_commands(engine_state: &mut EngineState) {
pub fn bind_math_commands(engine_state: &mut EngineState) -> CrateResult<()> {
bind_commands! {
engine_state,
Math,
@ -390,10 +392,10 @@ pub fn bind_math_commands(engine_state: &mut EngineState) {
MathStddev,
MathSum,
MathVariance,
};
}
}
pub fn bind_network_commands(engine_state: &mut EngineState) {
pub fn bind_network_commands(engine_state: &mut EngineState) -> CrateResult<()> {
bind_commands! {
engine_state,
Fetch,
@ -407,7 +409,7 @@ pub fn bind_network_commands(engine_state: &mut EngineState) {
}
}
pub fn bind_random_commands(engine_state: &mut EngineState) {
pub fn bind_random_commands(engine_state: &mut EngineState) -> CrateResult<()> {
bind_commands! {
engine_state,
Random,
@ -417,42 +419,44 @@ pub fn bind_random_commands(engine_state: &mut EngineState) {
RandomDice,
RandomInteger,
RandomUuid,
};
}
}
pub fn bind_generator_commands(engine_state: &mut EngineState) {
pub fn bind_generator_commands(engine_state: &mut EngineState) -> CrateResult<()> {
bind_commands! {
engine_state,
Cal,
Seq,
SeqDate,
SeqChar,
};
}
}
pub fn bind_hash_commands(engine_state: &mut EngineState) {
pub fn bind_hash_commands(engine_state: &mut EngineState) -> CrateResult<()> {
bind_commands! {
engine_state,
Hash,
HashMd5::default(),
HashSha256::default(),
};
}
}
pub fn bind_experimental_commands(engine_state: &mut EngineState) {
pub fn bind_experimental_commands(engine_state: &mut EngineState) -> CrateResult<()> {
bind_commands! {
engine_state,
ViewSource,
IsAdmin,
};
}
}
#[inline]
fn bind<F: Fn(&mut StateWorkingSet)>(engine_state: &mut EngineState, bind_fn: F) {
fn bind<F: Fn(&mut StateWorkingSet)>(
engine_state: &mut EngineState,
bind_fn: F,
) -> CrateResult<()> {
let mut working_set = StateWorkingSet::new(engine_state);
bind_fn(&mut working_set);
let delta = working_set.render();
engine_state
.merge_delta(delta)
.expect("Failed to bind commands");
engine_state.merge_delta(delta)?;
Ok(())
}

@ -7,7 +7,7 @@ use std::env;
use nu_protocol::{
ast::Block,
engine::{EngineState, Stack, StateWorkingSet},
engine::{Command, EngineState, Stack, StateWorkingSet},
PipelineData, Span,
};
@ -32,13 +32,13 @@ impl Default for ContextBuilder {
impl ContextBuilder {
/// Enables certain command groups specified in the Config on the state
pub fn with_command_groups(mut self, group_config: CommandGroupConfig) -> Self {
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);
super::bindings::[<bind_ $group _commands>](&mut self.engine_state)?;
}
)*
)
@ -70,7 +70,19 @@ impl ContextBuilder {
hash,
experimental
);
self
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

@ -10,7 +10,7 @@ pub use argument::{Argument, IntoArgument};
pub use context::{CommandGroupConfig, Context, ContextBuilder};
pub use into_expression::*;
pub use into_value::*;
pub use nu_protocol::{PipelineData, Value};
pub use nu_protocol::{self, PipelineData, Value};
pub use rusty_value;
pub use utils::NewEmpty;

@ -1,5 +1,7 @@
use embed_nu::rusty_value::*;
use embed_nu::{rusty_value::*, IntoValue};
use embed_nu::{CommandGroupConfig, Context, NewEmpty, PipelineData};
use nu_protocol::engine::Command;
use nu_protocol::{Config, Signature, SyntaxShape};
#[test]
fn it_evals_strings() {
@ -45,10 +47,61 @@ fn it_executes_functions() {
ctx.print_pipeline(pipeline).unwrap();
}
#[test]
fn it_executes_custom_commands() {
let mut ctx = get_context();
let pipeline = ctx
.eval_raw(r#"custom_upper "Hello world""#, PipelineData::empty())
.unwrap();
let string_output = pipeline.collect_string(" ", &Config::default()).unwrap();
assert_eq!(string_output, String::from("HELLO WORLD"))
}
fn get_context() -> Context {
Context::builder()
.with_command_groups(CommandGroupConfig::default().all_groups(true))
.unwrap()
.add_command(CustomCommand)
.unwrap()
.add_parent_env_vars()
.build()
.unwrap()
}
#[derive(Clone)]
struct CustomCommand;
impl Command for CustomCommand {
fn name(&self) -> &str {
"custom_upper"
}
fn signature(&self) -> nu_protocol::Signature {
Signature::new("custom_upper")
.required(
"text",
SyntaxShape::String,
"Text to print in full uppercase.",
)
.category(nu_protocol::Category::Experimental)
}
fn usage(&self) -> &str {
"custom_upper <text>"
}
fn run(
&self,
_engine_state: &nu_protocol::engine::EngineState,
_stack: &mut nu_protocol::engine::Stack,
call: &nu_protocol::ast::Call,
_input: PipelineData,
) -> Result<PipelineData, nu_protocol::ShellError> {
let string_input = call.positional_nth(0).unwrap();
let string_input = string_input.as_string().unwrap();
let upper = string_input.to_uppercase();
println!("{upper}");
Ok(PipelineData::Value(upper.into_value(), None))
}
}

Loading…
Cancel
Save