diff --git a/crates/nu-command/src/commands.rs b/crates/nu-command/src/commands.rs index 9bebcccbf..bf02d3b81 100644 --- a/crates/nu-command/src/commands.rs +++ b/crates/nu-command/src/commands.rs @@ -79,6 +79,7 @@ pub(crate) mod length; pub(crate) mod let_; pub(crate) mod let_env; pub(crate) mod lines; +pub(crate) mod load_env; pub(crate) mod ls; pub(crate) mod math; pub(crate) mod merge; @@ -226,6 +227,7 @@ pub(crate) use length::Length; pub(crate) use let_::Let; pub(crate) use let_env::LetEnv; pub(crate) use lines::Lines; +pub(crate) use load_env::LoadEnv; pub(crate) use ls::Ls; pub(crate) use math::{ Math, MathAbs, MathAverage, MathCeil, MathEval, MathFloor, MathMaximum, MathMedian, diff --git a/crates/nu-command/src/commands/default_context.rs b/crates/nu-command/src/commands/default_context.rs index 5b7d3cd79..cfe39a11f 100644 --- a/crates/nu-command/src/commands/default_context.rs +++ b/crates/nu-command/src/commands/default_context.rs @@ -13,6 +13,7 @@ pub fn create_default_context(interactive: bool) -> Result &str { + "load-env" + } + + fn signature(&self) -> Signature { + Signature::build("load-env").optional( + "environ", + SyntaxShape::Any, + "Optional environment table to load in. If not provided, will use the table provided on the input stream", + ) + } + + fn usage(&self) -> &str { + "Set environment variables using a table stream" + } + + fn run_with_actions(&self, args: CommandArgs) -> Result { + load_env(args) + } + + fn examples(&self) -> Vec { + vec![ + Example { + description: "Load variables from an input stream", + example: r#"echo [[name, value]; ["NAME", "JT"] ["AGE", "UNKNOWN"]] | load-env; echo $nu.env.NAME"#, + result: Some(vec![Value::from("JT")]), + }, + Example { + description: "Load variables from an argument", + example: r#"load-env [[name, value]; ["NAME", "JT"] ["AGE", "UNKNOWN"]]; echo $nu.env.NAME"#, + result: Some(vec![Value::from("JT")]), + }, + Example { + description: "Load variables from an argument and an input stream", + example: r#"echo [[name, value]; ["NAME", "JT"]] | load-env [[name, value]; ["VALUE", "FOO"]]; echo $nu.env.NAME $nu.env.VALUE"#, + result: Some(vec![Value::from("JT"), Value::from("UNKNOWN")]), + }, + ] + } +} + +fn load_env_from_table( + values: impl IntoIterator, + ctx: &EvaluationContext, +) -> Result<(), ShellError> { + for value in values { + let mut var_name = None; + let mut var_value = None; + + let tag = value.tag(); + + for (key, value) in value.row_entries() { + if key == "name" { + var_name = Some(value.as_string()?); + } else if key == "value" { + var_value = Some(value.as_string()?); + } + } + + match (var_name, var_value) { + (Some(name), Some(value)) => ctx.scope.add_env_var(name, value), + _ => { + return Err(ShellError::labeled_error( + r#"Expected each row in the table to have a "name" and "value" field."#, + r#"expected a "name" and "value" field"#, + tag, + )) + } + } + } + + Ok(()) +} + +pub fn load_env(args: CommandArgs) -> Result { + let ctx = EvaluationContext::from_args(&args); + let args = args.evaluate_once()?; + + if let Some(values) = args.opt::>(0)? { + load_env_from_table(values, &ctx)?; + } + + load_env_from_table(args.input, &ctx)?; + + Ok(ActionStream::empty()) +} diff --git a/tests/shell/pipeline/commands/internal.rs b/tests/shell/pipeline/commands/internal.rs index dd78059ad..197b8cdc0 100644 --- a/tests/shell/pipeline/commands/internal.rs +++ b/tests/shell/pipeline/commands/internal.rs @@ -368,6 +368,68 @@ fn proper_shadow_let_env_aliases() { assert_eq!(actual.out, "truefalsetrue"); } +#[test] +fn load_env_variable() { + let actual = nu!( + cwd: ".", + r#" + echo [[name, value]; [TESTENVVAR, "hello world"]] | load-env + echo $nu.env.TESTENVVAR + "# + ); + + assert_eq!(actual.out, "hello world"); +} + +#[test] +fn load_env_variable_arg() { + let actual = nu!( + cwd: ".", + r#" + load-env [[name, value]; [TESTENVVAR, "hello world"]] + echo $nu.env.TESTENVVAR + "# + ); + + assert_eq!(actual.out, "hello world"); +} + +#[test] +fn load_env_variable_arg_and_stream() { + let actual = nu!( + cwd: ".", + r#" + echo [[name, value]; [TESTVARSTREAM, "true"]] | load-env [[name, value]; [TESTVARARG, "false"]] + echo $nu.env | format "{TESTVARSTREAM} {TESTVARARG}" + "# + ); + + assert_eq!(actual.out, "true false"); +} + +#[test] +fn load_env_doesnt_leak() { + let actual = nu!( + cwd: ".", + r#" + do { echo [[name, value]; [xyz, "my message"]] | load-env }; echo $nu.env.xyz + "# + ); + + assert!(actual.err.contains("did you mean")); +} + +#[test] +fn proper_shadow_load_env_aliases() { + let actual = nu!( + cwd: ".", + r#" + let-env DEBUG = true; echo $nu.env.DEBUG | autoview; do { echo [[name, value]; [DEBUG, false]] | load-env; echo $nu.env.DEBUG } | autoview; echo $nu.env.DEBUG + "# + ); + assert_eq!(actual.out, "truefalsetrue"); +} + #[test] fn proper_shadow_let_aliases() { let actual = nu!(