mirror of
https://github.com/nushell/nushell.git
synced 2025-04-22 20:28:22 +02:00
Require static path for source-env
(#6526)
This commit is contained in:
parent
1adebefc3e
commit
e76b3d61de
132
crates/nu-command/src/env/source_env.rs
vendored
132
crates/nu-command/src/env/source_env.rs
vendored
@ -1,11 +1,10 @@
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use nu_engine::{eval_block, find_in_dirs_env, redirect_env, CallExt};
|
use nu_engine::{eval_block, find_in_dirs_env, redirect_env, CallExt};
|
||||||
use nu_parser::parse;
|
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack, StateWorkingSet};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
Category, CliError, Example, PipelineData, ShellError, Signature, Spanned, SyntaxShape, Value,
|
Category, Example, PipelineData, ShellError, Signature, Spanned, SyntaxShape, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Source a file for environment variables.
|
/// Source a file for environment variables.
|
||||||
@ -40,96 +39,47 @@ impl Command for SourceEnv {
|
|||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let source_filename: Spanned<String> = call.req(engine_state, caller_stack, 0)?;
|
let source_filename: Spanned<String> = call.req(engine_state, caller_stack, 0)?;
|
||||||
|
|
||||||
if let Some(path) = find_in_dirs_env(&source_filename.item, engine_state, caller_stack)? {
|
// Note: this hidden positional is the block_id that corresponded to the 0th position
|
||||||
if let Ok(content) = std::fs::read_to_string(&path) {
|
// it is put here by the parser
|
||||||
let mut parent = PathBuf::from(&path);
|
let block_id: i64 = call.req(engine_state, caller_stack, 1)?;
|
||||||
parent.pop();
|
|
||||||
|
|
||||||
let mut new_engine_state = engine_state.clone();
|
// Set the currently evaluated directory (file-relative PWD)
|
||||||
|
let mut parent = if let Some(path) =
|
||||||
let (block, delta) = {
|
find_in_dirs_env(&source_filename.item, engine_state, caller_stack)?
|
||||||
let mut working_set = StateWorkingSet::new(&new_engine_state);
|
{
|
||||||
|
PathBuf::from(&path)
|
||||||
// Set the currently parsed directory
|
|
||||||
working_set.currently_parsed_cwd = Some(parent.clone());
|
|
||||||
|
|
||||||
let (block, err) = parse(&mut working_set, None, content.as_bytes(), true, &[]);
|
|
||||||
|
|
||||||
if let Some(err) = err {
|
|
||||||
// Because the error span points at new_engine_state, we must create the error message now
|
|
||||||
let msg = format!(
|
|
||||||
r#"Found this parser error: {:?}"#,
|
|
||||||
CliError(&err, &working_set)
|
|
||||||
);
|
|
||||||
|
|
||||||
return Err(ShellError::GenericError(
|
|
||||||
"Failed to parse content".to_string(),
|
|
||||||
"cannot parse this file".to_string(),
|
|
||||||
Some(source_filename.span),
|
|
||||||
Some(msg),
|
|
||||||
vec![],
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
(block, working_set.render())
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Merge parser changes to a temporary engine state
|
|
||||||
new_engine_state.merge_delta(delta)?;
|
|
||||||
|
|
||||||
// Set the currently evaluated directory
|
|
||||||
let file_pwd = Value::String {
|
|
||||||
val: parent.to_string_lossy().to_string(),
|
|
||||||
span: call.head,
|
|
||||||
};
|
|
||||||
|
|
||||||
caller_stack.add_env_var("FILE_PWD".to_string(), file_pwd);
|
|
||||||
|
|
||||||
// Evaluate the parsed file's block
|
|
||||||
let mut callee_stack = caller_stack.gather_captures(&block.captures);
|
|
||||||
|
|
||||||
let result = eval_block(
|
|
||||||
&new_engine_state,
|
|
||||||
&mut callee_stack,
|
|
||||||
&block,
|
|
||||||
input,
|
|
||||||
true,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
|
|
||||||
let result = if let Err(err) = result {
|
|
||||||
// Because the error span points at new_engine_state, we must create the error message now
|
|
||||||
let working_set = StateWorkingSet::new(&new_engine_state);
|
|
||||||
|
|
||||||
let msg = format!(
|
|
||||||
r#"Found this shell error: {:?}"#,
|
|
||||||
CliError(&err, &working_set)
|
|
||||||
);
|
|
||||||
|
|
||||||
Err(ShellError::GenericError(
|
|
||||||
"Failed to evaluate content".to_string(),
|
|
||||||
"cannot evaluate this file".to_string(),
|
|
||||||
Some(source_filename.span),
|
|
||||||
Some(msg),
|
|
||||||
vec![],
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
result
|
|
||||||
};
|
|
||||||
|
|
||||||
// Merge the block's environment to the current stack
|
|
||||||
redirect_env(engine_state, caller_stack, &callee_stack);
|
|
||||||
|
|
||||||
// Remove the file-relative PWD
|
|
||||||
caller_stack.remove_env_var(engine_state, "FILE_PWD");
|
|
||||||
|
|
||||||
result
|
|
||||||
} else {
|
|
||||||
Err(ShellError::FileNotFound(source_filename.span))
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Err(ShellError::FileNotFound(source_filename.span))
|
return Err(ShellError::FileNotFound(source_filename.span));
|
||||||
}
|
};
|
||||||
|
parent.pop();
|
||||||
|
|
||||||
|
let file_pwd = Value::String {
|
||||||
|
val: parent.to_string_lossy().to_string(),
|
||||||
|
span: call.head,
|
||||||
|
};
|
||||||
|
|
||||||
|
caller_stack.add_env_var("FILE_PWD".to_string(), file_pwd);
|
||||||
|
|
||||||
|
// Evaluate the block
|
||||||
|
let block = engine_state.get_block(block_id as usize).clone();
|
||||||
|
let mut callee_stack = caller_stack.gather_captures(&block.captures);
|
||||||
|
|
||||||
|
let result = eval_block(
|
||||||
|
engine_state,
|
||||||
|
&mut callee_stack,
|
||||||
|
&block,
|
||||||
|
input,
|
||||||
|
call.redirect_stdout,
|
||||||
|
call.redirect_stderr,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Merge the block's environment to the current stack
|
||||||
|
redirect_env(engine_state, caller_stack, &callee_stack);
|
||||||
|
|
||||||
|
// Remove the file-relative PWD
|
||||||
|
caller_stack.remove_env_var(engine_state, "FILE_PWD");
|
||||||
|
|
||||||
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
@ -143,6 +143,7 @@ fn sources_unicode_file_in_non_utf8_dir() {
|
|||||||
// How do I create non-UTF-8 path???
|
// How do I create non-UTF-8 path???
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[ignore]
|
||||||
#[test]
|
#[test]
|
||||||
fn can_source_dynamic_path() {
|
fn can_source_dynamic_path() {
|
||||||
Playground::setup("can_source_dynamic_path", |dirs, sandbox| {
|
Playground::setup("can_source_dynamic_path", |dirs, sandbox| {
|
||||||
@ -269,39 +270,26 @@ fn source_env_dont_cd_overlay() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn source_env_nice_parse_error() {
|
fn source_env_is_scoped() {
|
||||||
Playground::setup("source_env_nice_parse_error", |dirs, sandbox| {
|
Playground::setup("source_env_is_scoped", |dirs, sandbox| {
|
||||||
sandbox.with_files(vec![FileWithContentToBeTrimmed(
|
sandbox.with_files(vec![FileWithContentToBeTrimmed(
|
||||||
"spam.nu",
|
"spam.nu",
|
||||||
r#"
|
r#"
|
||||||
let x
|
def foo [] { 'foo' }
|
||||||
"#,
|
alias bar = 'bar'
|
||||||
|
"#,
|
||||||
)]);
|
)]);
|
||||||
|
|
||||||
let inp = &[r#"source-env spam.nu"#];
|
let inp = &[r#"source-env spam.nu"#, r#"foo"#];
|
||||||
|
|
||||||
let actual = nu!(cwd: dirs.test(), pipeline(&inp.join("; ")));
|
let actual = nu!(cwd: dirs.test(), pipeline(&inp.join("; ")));
|
||||||
|
|
||||||
assert!(actual.err.contains("cannot parse this file"));
|
assert!(actual.err.contains("did you mean"));
|
||||||
assert!(actual.err.contains("───"));
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
let inp = &[r#"source-env spam.nu"#, r#"bar"#];
|
||||||
fn source_env_nice_shell_error() {
|
|
||||||
Playground::setup("source_env_nice_shell_error", |dirs, sandbox| {
|
|
||||||
sandbox.with_files(vec![FileWithContentToBeTrimmed(
|
|
||||||
"spam.nu",
|
|
||||||
r#"
|
|
||||||
let-env FILE_PWD = 'foo'
|
|
||||||
"#,
|
|
||||||
)]);
|
|
||||||
|
|
||||||
let inp = &[r#"source-env spam.nu"#];
|
|
||||||
|
|
||||||
let actual = nu!(cwd: dirs.test(), pipeline(&inp.join("; ")));
|
let actual = nu!(cwd: dirs.test(), pipeline(&inp.join("; ")));
|
||||||
|
|
||||||
assert!(actual.err.contains("cannot evaluate this file"));
|
assert!(actual.err.contains("did you mean"));
|
||||||
assert!(actual.err.contains("───"));
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -2902,8 +2902,10 @@ pub fn parse_source(
|
|||||||
let mut error = None;
|
let mut error = None;
|
||||||
let name = working_set.get_span_contents(spans[0]);
|
let name = working_set.get_span_contents(spans[0]);
|
||||||
|
|
||||||
if name == b"source" {
|
if name == b"source" || name == b"source-env" {
|
||||||
if let Some(decl_id) = working_set.find_decl(b"source", &Type::Any) {
|
let scoped = name == b"source-env";
|
||||||
|
|
||||||
|
if let Some(decl_id) = working_set.find_decl(name, &Type::Any) {
|
||||||
let cwd = working_set.get_cwd();
|
let cwd = working_set.get_cwd();
|
||||||
|
|
||||||
// Is this the right call to be using here?
|
// Is this the right call to be using here?
|
||||||
@ -2958,7 +2960,7 @@ pub fn parse_source(
|
|||||||
working_set,
|
working_set,
|
||||||
path.file_name().and_then(|x| x.to_str()),
|
path.file_name().and_then(|x| x.to_str()),
|
||||||
&contents,
|
&contents,
|
||||||
false,
|
scoped,
|
||||||
expand_aliases_denylist,
|
expand_aliases_denylist,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -2983,7 +2985,7 @@ pub fn parse_source(
|
|||||||
|
|
||||||
let mut call_with_block = call;
|
let mut call_with_block = call;
|
||||||
|
|
||||||
// Adding this expression to the positional creates a syntax highlighting error
|
// FIXME: Adding this expression to the positional creates a syntax highlighting error
|
||||||
// after writing `source example.nu`
|
// after writing `source example.nu`
|
||||||
call_with_block.add_positional(Expression {
|
call_with_block.add_positional(Expression {
|
||||||
expr: Expr::Int(block_id as i64),
|
expr: Expr::Int(block_id as i64),
|
||||||
|
@ -4815,7 +4815,9 @@ pub fn parse_builtin_commands(
|
|||||||
(pipeline, err)
|
(pipeline, err)
|
||||||
}
|
}
|
||||||
b"overlay" => parse_overlay(working_set, &lite_command.parts, expand_aliases_denylist),
|
b"overlay" => parse_overlay(working_set, &lite_command.parts, expand_aliases_denylist),
|
||||||
b"source" => parse_source(working_set, &lite_command.parts, expand_aliases_denylist),
|
b"source" | b"source-env" => {
|
||||||
|
parse_source(working_set, &lite_command.parts, expand_aliases_denylist)
|
||||||
|
}
|
||||||
b"export" => parse_export_in_block(working_set, lite_command, expand_aliases_denylist),
|
b"export" => parse_export_in_block(working_set, lite_command, expand_aliases_denylist),
|
||||||
b"hide" => parse_hide(working_set, &lite_command.parts, expand_aliases_denylist),
|
b"hide" => parse_hide(working_set, &lite_command.parts, expand_aliases_denylist),
|
||||||
#[cfg(feature = "plugin")]
|
#[cfg(feature = "plugin")]
|
||||||
|
Loading…
Reference in New Issue
Block a user