diff --git a/crates/nu-cli/src/cli.rs b/crates/nu-cli/src/cli.rs index 893293d8d1..796f95c54d 100644 --- a/crates/nu-cli/src/cli.rs +++ b/crates/nu-cli/src/cli.rs @@ -67,6 +67,7 @@ pub fn create_default_context(interactive: bool) -> Result { + context.scope.add_env_var(name, value); + InputStream::from_stream(futures::stream::iter(vec![])) + } CommandAction::AddPlugins(path) => { match crate::plugin::scan(vec![std::path::PathBuf::from(path)]) { Ok(plugins) => { diff --git a/crates/nu-cli/src/commands/set.rs b/crates/nu-cli/src/commands/set.rs index de85c489d1..d3f78225b4 100644 --- a/crates/nu-cli/src/commands/set.rs +++ b/crates/nu-cli/src/commands/set.rs @@ -85,10 +85,12 @@ pub async fn set(args: CommandArgs) -> Result { ctx.scope.enter_scope(); ctx.scope.add_vars(&captured.entries); - let value = evaluate_baseline_expr(&expr, &ctx).await?; + let value = evaluate_baseline_expr(&expr, &ctx).await; ctx.scope.exit_scope(); + let value = value?; + let name = if name.item.starts_with('$') { name.item.clone() } else { diff --git a/crates/nu-cli/src/commands/set_env.rs b/crates/nu-cli/src/commands/set_env.rs new file mode 100644 index 0000000000..1c5c8764bf --- /dev/null +++ b/crates/nu-cli/src/commands/set_env.rs @@ -0,0 +1,104 @@ +use crate::prelude::*; +use crate::{commands::WholeStreamCommand, evaluate::evaluate_baseline_expr}; + +use nu_errors::ShellError; +use nu_protocol::{ + hir::CapturedBlock, hir::ClassifiedCommand, CommandAction, ReturnSuccess, Signature, + SyntaxShape, +}; +use nu_source::Tagged; + +pub struct SetEnv; + +#[derive(Deserialize)] +pub struct SetEnvArgs { + pub name: Tagged, + pub equals: Tagged, + pub rhs: CapturedBlock, +} + +#[async_trait] +impl WholeStreamCommand for SetEnv { + fn name(&self) -> &str { + "set-env" + } + + fn signature(&self) -> Signature { + Signature::build("set-env") + .required( + "name", + SyntaxShape::String, + "the name of the environment variable", + ) + .required("equals", SyntaxShape::String, "the equals sign") + .required( + "expr", + SyntaxShape::Initializer, + "the value to set the environment variable to", + ) + } + + fn usage(&self) -> &str { + "Create an environment variable and set it to a value." + } + + async fn run(&self, args: CommandArgs) -> Result { + set_env(args).await + } + + fn examples(&self) -> Vec { + vec![] + } +} + +pub async fn set_env(args: CommandArgs) -> Result { + let tag = args.call_info.name_tag.clone(); + let ctx = EvaluationContext::from_args(&args); + + let (SetEnvArgs { name, rhs, .. }, _) = args.process().await?; + + let (expr, captured) = { + if rhs.block.block.len() != 1 { + return Err(ShellError::labeled_error( + "Expected a value", + "expected a value", + tag, + )); + } + match rhs.block.block[0].pipelines.get(0) { + Some(item) => match item.list.get(0) { + Some(ClassifiedCommand::Expr(expr)) => (expr.clone(), rhs.captured.clone()), + _ => { + return Err(ShellError::labeled_error( + "Expected a value", + "expected a value", + tag, + )); + } + }, + None => { + return Err(ShellError::labeled_error( + "Expected a value", + "expected a value", + tag, + )); + } + } + }; + + ctx.scope.enter_scope(); + ctx.scope.add_vars(&captured.entries); + + let value = evaluate_baseline_expr(&expr, &ctx).await; + + ctx.scope.exit_scope(); + + let value = value?; + let value = value.as_string()?; + + let name = name.item.clone(); + + Ok(OutputStream::one(ReturnSuccess::action( + CommandAction::AddEnvVariable(name, value), + ))) +} diff --git a/crates/nu-protocol/src/return_value.rs b/crates/nu-protocol/src/return_value.rs index fc52c24e7f..63eeb2ab2d 100644 --- a/crates/nu-protocol/src/return_value.rs +++ b/crates/nu-protocol/src/return_value.rs @@ -22,6 +22,8 @@ pub enum CommandAction { EnterHelpShell(Value), /// Add a variable into scope AddVariable(String, Value), + /// Add an environment variable into scope + AddEnvVariable(String, String), /// Add plugins from path given AddPlugins(String), /// Go to the previous shell in the shell ring buffer @@ -46,6 +48,7 @@ impl PrettyDebug for CommandAction { CommandAction::EnterValueShell(v) => b::typed("enter value shell", v.pretty()), CommandAction::EnterHelpShell(v) => b::typed("enter help shell", v.pretty()), CommandAction::AddVariable(..) => b::description("add variable"), + CommandAction::AddEnvVariable(..) => b::description("add environment variable"), CommandAction::AddPlugins(..) => b::description("add plugins"), CommandAction::PreviousShell => b::description("previous shell"), CommandAction::NextShell => b::description("next shell"), diff --git a/tests/shell/pipeline/commands/internal.rs b/tests/shell/pipeline/commands/internal.rs index 697120f61a..57e1536fb0 100644 --- a/tests/shell/pipeline/commands/internal.rs +++ b/tests/shell/pipeline/commands/internal.rs @@ -345,7 +345,7 @@ fn run_custom_command() { } #[test] -fn set_variables() { +fn set_variable() { let actual = nu!( cwd: ".", r#" @@ -358,6 +358,19 @@ fn set_variables() { assert_eq!(actual.out, "17"); } +#[test] +fn set_env_variable() { + let actual = nu!( + cwd: ".", + r#" + set-env TESTENVVAR = "hello world" + echo $nu.env.TESTENVVAR + "# + ); + + assert_eq!(actual.out, "hello world"); +} + #[cfg(feature = "which")] #[test] fn argument_invocation_reports_errors() {