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() { fn main() {
let mut ctx = Context::builder() let mut ctx = Context::builder()
.with_command_groups(CommandGroupConfig::default().all_groups(true)) .with_command_groups(CommandGroupConfig::default().all_groups(true))
.unwrap()
.add_parent_env_vars() .add_parent_env_vars()
.build() .build()
.unwrap(); .unwrap();
@ -73,7 +74,7 @@ fn main() {
bar: 12 bar: 12
}; };
// convert this struct into a nu value // 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 // as function arguments or variables
let value = instance.into_value(); let value = instance.into_value();
dbg!(value); dbg!(value);

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

@ -7,7 +7,7 @@ use std::env;
use nu_protocol::{ use nu_protocol::{
ast::Block, ast::Block,
engine::{EngineState, Stack, StateWorkingSet}, engine::{Command, EngineState, Stack, StateWorkingSet},
PipelineData, Span, PipelineData, Span,
}; };
@ -32,13 +32,13 @@ impl Default for ContextBuilder {
impl ContextBuilder { impl ContextBuilder {
/// Enables certain command groups specified in the Config on the state /// 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 { macro_rules! toggle_command_groups {
($($group:ident),*) => { ($($group:ident),*) => {
paste::item!( paste::item!(
$( $(
if group_config.$group { 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, hash,
experimental 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 /// Adds a variable to the state

@ -10,7 +10,7 @@ pub use argument::{Argument, IntoArgument};
pub use context::{CommandGroupConfig, Context, ContextBuilder}; pub use context::{CommandGroupConfig, Context, ContextBuilder};
pub use into_expression::*; pub use into_expression::*;
pub use into_value::*; pub use into_value::*;
pub use nu_protocol::{PipelineData, Value}; pub use nu_protocol::{self, PipelineData, Value};
pub use rusty_value; pub use rusty_value;
pub use utils::NewEmpty; 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 embed_nu::{CommandGroupConfig, Context, NewEmpty, PipelineData};
use nu_protocol::engine::Command;
use nu_protocol::{Config, Signature, SyntaxShape};
#[test] #[test]
fn it_evals_strings() { fn it_evals_strings() {
@ -45,10 +47,61 @@ fn it_executes_functions() {
ctx.print_pipeline(pipeline).unwrap(); 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 { fn get_context() -> Context {
Context::builder() Context::builder()
.with_command_groups(CommandGroupConfig::default().all_groups(true)) .with_command_groups(CommandGroupConfig::default().all_groups(true))
.unwrap()
.add_command(CustomCommand)
.unwrap()
.add_parent_env_vars() .add_parent_env_vars()
.build() .build()
.unwrap() .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