diff --git a/crates/nu-command/src/core_commands/export_env.rs b/crates/nu-command/src/core_commands/export_env.rs index 2122be399..cc79c5d14 100644 --- a/crates/nu-command/src/core_commands/export_env.rs +++ b/crates/nu-command/src/core_commands/export_env.rs @@ -3,9 +3,9 @@ use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Value}; #[derive(Clone)] -pub struct ExportEnv; +pub struct ExportEnvModule; -impl Command for ExportEnv { +impl Command for ExportEnvModule { fn name(&self) -> &str { "export env" } diff --git a/crates/nu-command/src/core_commands/mod.rs b/crates/nu-command/src/core_commands/mod.rs index 54bb4b108..fcdbb545a 100644 --- a/crates/nu-command/src/core_commands/mod.rs +++ b/crates/nu-command/src/core_commands/mod.rs @@ -40,7 +40,7 @@ pub use export::ExportCommand; pub use export_alias::ExportAlias; pub use export_def::ExportDef; pub use export_def_env::ExportDefEnv; -pub use export_env::ExportEnv; +pub use export_env::ExportEnvModule; pub use export_extern::ExportExtern; pub use export_use::ExportUse; pub use extern_::Extern; diff --git a/crates/nu-command/src/default_context.rs b/crates/nu-command/src/default_context.rs index e41b96834..c3388f394 100644 --- a/crates/nu-command/src/default_context.rs +++ b/crates/nu-command/src/default_context.rs @@ -40,7 +40,7 @@ pub fn create_default_context() -> EngineState { ExportCommand, ExportDef, ExportDefEnv, - ExportEnv, + ExportEnvModule, ExportExtern, ExportUse, Extern, @@ -352,6 +352,7 @@ pub fn create_default_context() -> EngineState { // Env bind_command! { Env, + ExportEnv, LetEnv, LoadEnv, WithEnv, diff --git a/crates/nu-command/src/env/export_env.rs b/crates/nu-command/src/env/export_env.rs new file mode 100644 index 000000000..60635013d --- /dev/null +++ b/crates/nu-command/src/env/export_env.rs @@ -0,0 +1,74 @@ +use nu_engine::{eval_block, redirect_env, CallExt}; +use nu_protocol::{ + ast::Call, + engine::{CaptureBlock, Command, EngineState, Stack}, + Category, Example, PipelineData, Signature, Span, SyntaxShape, Value, +}; + +#[derive(Clone)] +pub struct ExportEnv; + +impl Command for ExportEnv { + fn name(&self) -> &str { + "export-env" + } + + fn signature(&self) -> Signature { + Signature::build("export-env") + .required( + "block", + SyntaxShape::Block(Some(vec![])), + "the block to run to set the environment", + ) + .category(Category::Env) + } + + fn usage(&self) -> &str { + "Run a block and preserve its environment in a current scope." + } + + fn run( + &self, + engine_state: &EngineState, + caller_stack: &mut Stack, + call: &Call, + input: PipelineData, + ) -> Result { + let capture_block: CaptureBlock = call.req(engine_state, caller_stack, 0)?; + let block = engine_state.get_block(capture_block.block_id); + let mut callee_stack = caller_stack.captures_to_stack(&capture_block.captures); + + let _ = eval_block( + engine_state, + &mut callee_stack, + block, + input, + call.redirect_stdout, + call.redirect_stderr, + ); + + redirect_env(engine_state, caller_stack, &callee_stack); + + Ok(PipelineData::new(call.head)) + } + + fn examples(&self) -> Vec { + vec![Example { + description: "Set an environment", + example: r#"export-env { let-env SPAM = 'eggs' }; $env.SPAM"#, + result: Some(Value::string("eggs", Span::test_data())), + }] + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_examples() { + use crate::test_examples; + + test_examples(ExportEnv {}) + } +} diff --git a/crates/nu-command/src/env/mod.rs b/crates/nu-command/src/env/mod.rs index 6bd891485..e04e7228d 100644 --- a/crates/nu-command/src/env/mod.rs +++ b/crates/nu-command/src/env/mod.rs @@ -1,5 +1,6 @@ mod config; mod env_command; +mod export_env; mod let_env; mod load_env; mod with_env; @@ -9,6 +10,7 @@ pub use config::ConfigMeta; pub use config::ConfigNu; pub use config::ConfigReset; pub use env_command::Env; +pub use export_env::ExportEnv; pub use let_env::LetEnv; pub use load_env::LoadEnv; pub use with_env::WithEnv; diff --git a/crates/nu-command/src/example_test.rs b/crates/nu-command/src/example_test.rs index a16e88be1..e57013612 100644 --- a/crates/nu-command/src/example_test.rs +++ b/crates/nu-command/src/example_test.rs @@ -13,8 +13,8 @@ use crate::To; #[cfg(test)] use super::{ - Ansi, Date, From, If, Into, Math, Path, Random, Split, SplitColumn, SplitRow, Str, StrCollect, - StrLength, StrReplace, Url, Wrap, + Ansi, Date, From, If, Into, LetEnv, Math, Path, Random, Split, SplitColumn, SplitRow, Str, + StrCollect, StrLength, StrReplace, Url, Wrap, }; #[cfg(test)] @@ -47,6 +47,7 @@ pub fn test_examples(cmd: impl Command + 'static) { working_set.add_decl(Box::new(Url)); working_set.add_decl(Box::new(Ansi)); working_set.add_decl(Box::new(Wrap)); + working_set.add_decl(Box::new(LetEnv)); use super::Echo; working_set.add_decl(Box::new(Echo)); diff --git a/crates/nu-engine/src/eval.rs b/crates/nu-engine/src/eval.rs index 71df99426..74e96f0b1 100644 --- a/crates/nu-engine/src/eval.rs +++ b/crates/nu-engine/src/eval.rs @@ -159,20 +159,7 @@ pub fn eval_call( ); if block.redirect_env { - let caller_env_vars = caller_stack.get_env_var_names(engine_state); - - // remove env vars that are present in the caller but not in the callee - // (the callee hid them) - for var in caller_env_vars.iter() { - if !callee_stack.has_env_var(engine_state, var) { - caller_stack.remove_env_var(engine_state, var); - } - } - - // add new env vars from callee to caller - for (var, value) in callee_stack.get_stack_env_vars() { - caller_stack.add_env_var(var, value); - } + redirect_env(engine_state, caller_stack, &callee_stack); } result @@ -184,6 +171,24 @@ pub fn eval_call( } } +/// Redirect the environment from callee to the caller. +pub fn redirect_env(engine_state: &EngineState, caller_stack: &mut Stack, callee_stack: &Stack) { + let caller_env_vars = caller_stack.get_env_var_names(engine_state); + + // remove env vars that are present in the caller but not in the callee + // (the callee hid them) + for var in caller_env_vars.iter() { + if !callee_stack.has_env_var(engine_state, var) { + caller_stack.remove_env_var(engine_state, var); + } + } + + // add new env vars from callee to caller + for (var, value) in callee_stack.get_stack_env_vars() { + caller_stack.add_env_var(var, value); + } +} + /// Eval extarnal expression /// /// It returns PipelineData with a boolean flag, indicate that if the external runs to failed. diff --git a/crates/nu-engine/src/lib.rs b/crates/nu-engine/src/lib.rs index 69f18cbc0..b1ca1a880 100644 --- a/crates/nu-engine/src/lib.rs +++ b/crates/nu-engine/src/lib.rs @@ -11,6 +11,6 @@ pub use documentation::get_full_help; pub use env::*; pub use eval::{ eval_block, eval_call, eval_expression, eval_expression_with_input, eval_operator, - eval_subexpression, eval_variable, + eval_subexpression, eval_variable, redirect_env, }; pub use glob_from::glob_from;