Add support for load-env (#752)

This commit is contained in:
JT 2022-01-15 18:50:11 -05:00 committed by GitHub
parent 75db4a75bc
commit b78924c777
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 136 additions and 5 deletions

View file

@ -234,6 +234,7 @@ pub fn create_default_context(cwd: impl AsRef<Path>) -> EngineState {
// Conversions
bind_command! {
Fmt,
Into,
IntoBool,
IntoBinary,
@ -242,14 +243,14 @@ pub fn create_default_context(cwd: impl AsRef<Path>) -> EngineState {
IntoFilesize,
IntoInt,
IntoString,
Fmt,
};
// Env
bind_command! {
LetEnv,
WithEnv,
Env,
LetEnv,
LoadEnv,
WithEnv,
};
// Math

109
crates/nu-command/src/env/load_env.rs vendored Normal file
View file

@ -0,0 +1,109 @@
use nu_engine::{current_dir, CallExt};
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Value};
#[derive(Clone)]
pub struct LoadEnv;
impl Command for LoadEnv {
fn name(&self) -> &str {
"load-env"
}
fn usage(&self) -> &str {
"Loads an environment update from a record."
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("load-env")
.optional(
"update",
SyntaxShape::Record,
"the record to use for updates",
)
.category(Category::FileSystem)
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
let arg: Option<(Vec<String>, Vec<Value>)> = call.opt(engine_state, stack, 0)?;
let span = call.head;
match arg {
Some((cols, vals)) => {
for (env_var, rhs) in cols.into_iter().zip(vals) {
if env_var == "PWD" {
let cwd = current_dir(engine_state, stack)?;
let rhs = rhs.as_string()?;
let rhs = nu_path::expand_path_with(rhs, cwd);
stack.add_env_var(
env_var,
Value::String {
val: rhs.to_string_lossy().to_string(),
span: call.head,
},
);
} else {
stack.add_env_var(env_var, rhs);
}
}
Ok(PipelineData::new(call.head))
}
None => match input {
PipelineData::Value(Value::Record { cols, vals, .. }, ..) => {
for (env_var, rhs) in cols.into_iter().zip(vals) {
if env_var == "PWD" {
let cwd = current_dir(engine_state, stack)?;
let rhs = rhs.as_string()?;
let rhs = nu_path::expand_path_with(rhs, cwd);
stack.add_env_var(
env_var,
Value::String {
val: rhs.to_string_lossy().to_string(),
span: call.head,
},
);
} else {
stack.add_env_var(env_var, rhs);
}
}
Ok(PipelineData::new(call.head))
}
_ => Err(ShellError::UnsupportedInput("Record".into(), span)),
},
}
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Load variables from an input stream",
example: r#"{NAME: ABE, AGE: UNKNOWN} | load-env; echo $env.NAME"#,
result: Some(Value::test_string("ABE")),
},
Example {
description: "Load variables from an argument",
example: r#"load-env {NAME: ABE, AGE: UNKNOWN}; echo $env.NAME"#,
result: Some(Value::test_string("ABE")),
},
]
}
}
#[cfg(test)]
mod tests {
use super::LoadEnv;
#[test]
fn examples_work_as_expected() {
use crate::test_examples;
test_examples(LoadEnv {})
}
}

View file

@ -1,7 +1,9 @@
mod env_command;
mod let_env;
mod load_env;
mod with_env;
pub use env_command::Env;
pub use let_env::LetEnv;
pub use load_env::LoadEnv;
pub use with_env::WithEnv;

View file

@ -13,7 +13,6 @@ use std::path::Path;
#[derive(Clone)]
pub struct Open;
//NOTE: this is not a real implementation :D. It's just a simple one to test with until we port the real one.
impl Command for Open {
fn name(&self) -> &str {
"open"

View file

@ -9,7 +9,6 @@ use std::path::Path;
#[derive(Clone)]
pub struct Save;
//NOTE: this is not a real implementation :D. It's just a simple one to test with until we port the real one.
impl Command for Save {
fn name(&self) -> &str {
"save"

View file

@ -2970,6 +2970,8 @@ pub fn parse_value(
}
if matches!(shape, SyntaxShape::Block(_)) || matches!(shape, SyntaxShape::Any) {
return parse_block_expression(working_set, shape, span);
} else if matches!(shape, SyntaxShape::Record) {
return parse_record(working_set, span);
} else {
return (
Expression::garbage(span),

View file

@ -80,6 +80,9 @@ pub enum SyntaxShape {
/// A boolean value
Boolean,
/// A record value
Record,
/// A custom shape with custom completion logic
Custom(Box<SyntaxShape>, String),
}
@ -108,6 +111,7 @@ impl SyntaxShape {
SyntaxShape::Number => Type::Number,
SyntaxShape::Operator => Type::Unknown,
SyntaxShape::Range => Type::Unknown,
SyntaxShape::Record => Type::Record(vec![]), // FIXME: Add actual record type
SyntaxShape::RowCondition => Type::Bool,
SyntaxShape::Boolean => Type::Bool,
SyntaxShape::Signature => Type::Signature,
@ -138,6 +142,7 @@ impl Display for SyntaxShape {
SyntaxShape::Block(_) => write!(f, "block"),
SyntaxShape::Table => write!(f, "table"),
SyntaxShape::List(x) => write!(f, "list<{}>", x),
SyntaxShape::Record => write!(f, "record"),
SyntaxShape::Filesize => write!(f, "filesize"),
SyntaxShape::Duration => write!(f, "duration"),
SyntaxShape::Operator => write!(f, "operator"),

View file

@ -353,6 +353,20 @@ impl FromValue for Vec<Value> {
}
}
// A record
impl FromValue for (Vec<String>, Vec<Value>) {
fn from_value(v: &Value) -> Result<Self, ShellError> {
match v {
Value::Record { cols, vals, .. } => Ok((cols.clone(), vals.clone())),
v => Err(ShellError::CantConvert(
"Record".into(),
v.get_type().to_string(),
v.span()?,
)),
}
}
}
impl FromValue for CaptureBlock {
fn from_value(v: &Value) -> Result<Self, ShellError> {
match v {