mirror of
https://github.com/nushell/nushell.git
synced 2025-08-09 06:35:56 +02:00
Add environment variable support for modules (#331)
* Add 'expor env' dummy command * (WIP) Abstract away module exportables as Overlay * Switch to Overlays for use/hide Works for decls only right now. * Fix passing import patterns of hide to eval * Simplify use/hide of decls * Add ImportPattern as Expr; Add use env eval Still no parsing of "export env" so I can't test it yet. * Refactor export parsing; Add InternalError * Add env var export and activation; Misc changes Now it is possible to `use` env var that was exported from a module. This commit also adds some new errors and other small changes. * Add env var hiding * Fix eval not recognizing hidden decls Without this change, calling `hide foo`, the evaluator does not know whether a custom command named "foo" was hidden during parsing, therefore, it is not possible to reliably throw an error about the "foo" name not found. * Add use/hide/export env var tests; Cleanup; Notes * Ignore hide env related tests for now * Fix main branch merge mess * Fixed multi-word export def * Fix hiding tests on Windows * Remove env var hiding for now
This commit is contained in:
41
crates/nu-command/src/core_commands/export.rs
Normal file
41
crates/nu-command/src/core_commands/export.rs
Normal file
@ -0,0 +1,41 @@
|
||||
use nu_engine::get_full_help;
|
||||
use nu_protocol::{
|
||||
ast::Call,
|
||||
engine::{Command, EngineState, Stack},
|
||||
IntoPipelineData, PipelineData, Signature, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ExportCommand;
|
||||
|
||||
impl Command for ExportCommand {
|
||||
fn name(&self) -> &str {
|
||||
"export"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("export")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Export custom commands or environment variables from a module."
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
_stack: &mut Stack,
|
||||
call: &Call,
|
||||
_input: PipelineData,
|
||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||
Ok(Value::String {
|
||||
val: get_full_help(
|
||||
&ExportCommand.signature(),
|
||||
&ExportCommand.examples(),
|
||||
engine_state,
|
||||
),
|
||||
span: call.head,
|
||||
}
|
||||
.into_pipeline_data())
|
||||
}
|
||||
}
|
@ -16,7 +16,7 @@ impl Command for ExportDef {
|
||||
|
||||
fn signature(&self) -> nu_protocol::Signature {
|
||||
Signature::build("export def")
|
||||
.required("target", SyntaxShape::String, "definition name")
|
||||
.required("name", SyntaxShape::String, "definition name")
|
||||
.required("params", SyntaxShape::Signature, "parameters")
|
||||
.required(
|
||||
"block",
|
||||
|
41
crates/nu-command/src/core_commands/export_env.rs
Normal file
41
crates/nu-command/src/core_commands/export_env.rs
Normal file
@ -0,0 +1,41 @@
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{PipelineData, Signature, SyntaxShape};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ExportEnv;
|
||||
|
||||
impl Command for ExportEnv {
|
||||
fn name(&self) -> &str {
|
||||
"export env"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Export a block from a module that will be evaluated as an environment variable when imported."
|
||||
}
|
||||
|
||||
fn signature(&self) -> nu_protocol::Signature {
|
||||
Signature::build("export env")
|
||||
.required(
|
||||
"name",
|
||||
SyntaxShape::String,
|
||||
"name of the environment variable",
|
||||
)
|
||||
.required(
|
||||
"block",
|
||||
SyntaxShape::Block(Some(vec![])),
|
||||
"body of the environment variable definition",
|
||||
)
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
_engine_state: &EngineState,
|
||||
_stack: &mut Stack,
|
||||
call: &Call,
|
||||
_input: PipelineData,
|
||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||
//TODO: Add the env to stack
|
||||
Ok(PipelineData::new(call.head))
|
||||
}
|
||||
}
|
@ -2,7 +2,9 @@ mod alias;
|
||||
mod def;
|
||||
mod do_;
|
||||
mod echo;
|
||||
mod export;
|
||||
mod export_def;
|
||||
mod export_env;
|
||||
mod for_;
|
||||
mod help;
|
||||
mod hide;
|
||||
@ -16,7 +18,9 @@ pub use alias::Alias;
|
||||
pub use def::Def;
|
||||
pub use do_::Do;
|
||||
pub use echo::Echo;
|
||||
pub use export::ExportCommand;
|
||||
pub use export_def::ExportDef;
|
||||
pub use export_env::ExportEnv;
|
||||
pub use for_::For;
|
||||
pub use help::Help;
|
||||
pub use hide::Hide;
|
||||
|
@ -1,6 +1,7 @@
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_engine::eval_block;
|
||||
use nu_protocol::ast::{Call, Expr, Expression, ImportPatternMember};
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{PipelineData, Signature, SyntaxShape};
|
||||
use nu_protocol::{PipelineData, ShellError, Signature, Span, SyntaxShape};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Use;
|
||||
@ -20,11 +21,85 @@ impl Command for Use {
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
_engine_state: &EngineState,
|
||||
_stack: &mut Stack,
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
_input: PipelineData,
|
||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let import_pattern = if let Some(Expression {
|
||||
expr: Expr::ImportPattern(pat),
|
||||
..
|
||||
}) = call.positional.get(0)
|
||||
{
|
||||
pat
|
||||
} else {
|
||||
return Err(ShellError::InternalError(
|
||||
"Got something else than import pattern".into(),
|
||||
));
|
||||
};
|
||||
|
||||
if let Some(block_id) = engine_state.find_module(&import_pattern.head.name) {
|
||||
let overlay = &engine_state.get_block(block_id).overlay;
|
||||
|
||||
let env_vars_to_use = if import_pattern.members.is_empty() {
|
||||
overlay.env_vars_with_head(&import_pattern.head.name)
|
||||
} else {
|
||||
match &import_pattern.members[0] {
|
||||
ImportPatternMember::Glob { .. } => overlay.env_vars(),
|
||||
ImportPatternMember::Name { name, span } => {
|
||||
let mut output = vec![];
|
||||
|
||||
if let Some(id) = overlay.get_env_var_id(name) {
|
||||
output.push((name.clone(), id));
|
||||
} else if !overlay.has_decl(name) {
|
||||
return Err(ShellError::EnvVarNotFoundAtRuntime(*span));
|
||||
}
|
||||
|
||||
output
|
||||
}
|
||||
ImportPatternMember::List { names } => {
|
||||
let mut output = vec![];
|
||||
|
||||
for (name, span) in names {
|
||||
if let Some(id) = overlay.get_env_var_id(name) {
|
||||
output.push((name.clone(), id));
|
||||
} else if !overlay.has_decl(name) {
|
||||
return Err(ShellError::EnvVarNotFoundAtRuntime(*span));
|
||||
}
|
||||
}
|
||||
|
||||
output
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for (name, block_id) in env_vars_to_use {
|
||||
let name = if let Ok(s) = String::from_utf8(name.clone()) {
|
||||
s
|
||||
} else {
|
||||
return Err(ShellError::NonUtf8(import_pattern.head.span));
|
||||
};
|
||||
|
||||
let block = engine_state.get_block(block_id);
|
||||
|
||||
// TODO: Add string conversions (e.g. int to string)
|
||||
// TODO: Later expand env to take all Values
|
||||
let val = if let Ok(s) =
|
||||
eval_block(engine_state, stack, block, PipelineData::new(call.head))?
|
||||
.into_value(Span::unknown())
|
||||
.as_string()
|
||||
{
|
||||
s
|
||||
} else {
|
||||
return Err(ShellError::EnvVarNotAString(import_pattern.span()));
|
||||
};
|
||||
|
||||
stack.add_env_var(name, val);
|
||||
}
|
||||
} else {
|
||||
return Err(ShellError::EnvVarNotFoundAtRuntime(call.positional[0].span));
|
||||
}
|
||||
|
||||
Ok(PipelineData::new(call.head))
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user