mirror of
https://github.com/nushell/nushell
synced 2024-12-26 21:13:19 +00:00
Allow "export-env" parsing in modules (#6382)
* Allow "export-env" parsing in modules * Fmt * Add test for importing module's environment
This commit is contained in:
parent
839b264261
commit
d97975e9fa
4 changed files with 126 additions and 12 deletions
|
@ -1211,6 +1211,87 @@ pub fn parse_export_in_module(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn parse_export_env(
|
||||||
|
working_set: &mut StateWorkingSet,
|
||||||
|
spans: &[Span],
|
||||||
|
expand_aliases_denylist: &[usize],
|
||||||
|
) -> (Pipeline, Option<ParseError>) {
|
||||||
|
// Just used to be allowed inside modules, otherwise does nothing in the parser.
|
||||||
|
|
||||||
|
if !spans.is_empty() && working_set.get_span_contents(spans[0]) != b"export-env" {
|
||||||
|
return (
|
||||||
|
garbage_pipeline(spans),
|
||||||
|
Some(ParseError::UnknownState(
|
||||||
|
"internal error: Wrong call name for 'export-env' command".into(),
|
||||||
|
span(spans),
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if spans.len() < 2 {
|
||||||
|
return (
|
||||||
|
garbage_pipeline(spans),
|
||||||
|
Some(ParseError::MissingPositional(
|
||||||
|
"block".into(),
|
||||||
|
span(spans),
|
||||||
|
"export-env <block>".into(),
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let call = match working_set.find_decl(b"export-env", &Type::Any) {
|
||||||
|
Some(decl_id) => {
|
||||||
|
let ParsedInternalCall {
|
||||||
|
call,
|
||||||
|
error: mut err,
|
||||||
|
output,
|
||||||
|
} = parse_internal_call(
|
||||||
|
working_set,
|
||||||
|
spans[0],
|
||||||
|
&[spans[1]],
|
||||||
|
decl_id,
|
||||||
|
expand_aliases_denylist,
|
||||||
|
);
|
||||||
|
let decl = working_set.get_decl(decl_id);
|
||||||
|
|
||||||
|
let call_span = span(spans);
|
||||||
|
|
||||||
|
err = check_call(call_span, &decl.signature(), &call).or(err);
|
||||||
|
if err.is_some() || call.has_flag("help") {
|
||||||
|
return (
|
||||||
|
Pipeline::from_vec(vec![Expression {
|
||||||
|
expr: Expr::Call(call),
|
||||||
|
span: call_span,
|
||||||
|
ty: output,
|
||||||
|
custom_completion: None,
|
||||||
|
}]),
|
||||||
|
err,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
call
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
return (
|
||||||
|
garbage_pipeline(spans),
|
||||||
|
Some(ParseError::UnknownState(
|
||||||
|
"internal error: 'export-env' declaration not found".into(),
|
||||||
|
span(spans),
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let pipeline = Pipeline::from_vec(vec![Expression {
|
||||||
|
expr: Expr::Call(call),
|
||||||
|
span: span(spans),
|
||||||
|
ty: Type::Any,
|
||||||
|
custom_completion: None,
|
||||||
|
}]);
|
||||||
|
|
||||||
|
(pipeline, None)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn parse_module_block(
|
pub fn parse_module_block(
|
||||||
working_set: &mut StateWorkingSet,
|
working_set: &mut StateWorkingSet,
|
||||||
span: Span,
|
span: Span,
|
||||||
|
@ -1282,14 +1363,6 @@ pub fn parse_module_block(
|
||||||
|
|
||||||
(pipeline, err)
|
(pipeline, err)
|
||||||
}
|
}
|
||||||
// TODO: Currently, it is not possible to define a private env var.
|
|
||||||
// TODO: Exported env vars are usable iside the module only if correctly
|
|
||||||
// exported by the user. For example:
|
|
||||||
//
|
|
||||||
// > module foo { export env a { "2" }; export def b [] { $env.a } }
|
|
||||||
//
|
|
||||||
// will work only if you call `use foo *; b` but not with `use foo; foo b`
|
|
||||||
// since in the second case, the name of the env var would be $env."foo a".
|
|
||||||
b"export" => {
|
b"export" => {
|
||||||
let (pipe, exportables, err) = parse_export_in_module(
|
let (pipe, exportables, err) = parse_export_in_module(
|
||||||
working_set,
|
working_set,
|
||||||
|
@ -1315,6 +1388,11 @@ pub fn parse_module_block(
|
||||||
|
|
||||||
(pipe, err)
|
(pipe, err)
|
||||||
}
|
}
|
||||||
|
b"export-env" => parse_export_env(
|
||||||
|
working_set,
|
||||||
|
&pipeline.commands[0].parts,
|
||||||
|
expand_aliases_denylist,
|
||||||
|
),
|
||||||
_ => (
|
_ => (
|
||||||
garbage_pipeline(&pipeline.commands[0].parts),
|
garbage_pipeline(&pipeline.commands[0].parts),
|
||||||
Some(ParseError::ExpectedKeyword(
|
Some(ParseError::ExpectedKeyword(
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
lex, lite_parse,
|
lex, lite_parse,
|
||||||
lite_parse::LiteCommand,
|
lite_parse::LiteCommand,
|
||||||
parse_export_in_block,
|
|
||||||
parse_keywords::{parse_extern, parse_for, parse_source},
|
|
||||||
type_check::{math_result_type, type_compatible},
|
type_check::{math_result_type, type_compatible},
|
||||||
LiteBlock, ParseError, Token, TokenContents,
|
LiteBlock, ParseError, Token, TokenContents,
|
||||||
};
|
};
|
||||||
|
@ -19,8 +17,8 @@ use nu_protocol::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::parse_keywords::{
|
use crate::parse_keywords::{
|
||||||
parse_alias, parse_def, parse_def_predecl, parse_hide, parse_let, parse_module, parse_overlay,
|
parse_alias, parse_def, parse_def_predecl, parse_export_in_block, parse_extern, parse_for,
|
||||||
parse_use,
|
parse_hide, parse_let, parse_module, parse_overlay, parse_source, parse_use,
|
||||||
};
|
};
|
||||||
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
|
|
@ -345,3 +345,21 @@ fn module_nested_imports_in_dirs_prefixed() {
|
||||||
assert_eq!(actual.out, "bar");
|
assert_eq!(actual.out, "bar");
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn module_eval_export_env() {
|
||||||
|
Playground::setup("module_eval_export_env", |dirs, sandbox| {
|
||||||
|
sandbox.with_files(vec![FileWithContentToBeTrimmed(
|
||||||
|
"spam.nu",
|
||||||
|
r#"
|
||||||
|
export-env { let-env FOO = 'foo' }
|
||||||
|
"#,
|
||||||
|
)]);
|
||||||
|
|
||||||
|
let inp = &[r#"source spam.nu"#, r#"$env.FOO"#];
|
||||||
|
|
||||||
|
let actual = nu!(cwd: dirs.test(), pipeline(&inp.join("; ")));
|
||||||
|
|
||||||
|
assert_eq!(actual.out, "foo");
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -151,3 +151,23 @@ fn parse_file_relative_to_parsed_file_dont_use_cwd_2() {
|
||||||
assert!(actual.err.contains("File not found"));
|
assert!(actual.err.contains("File not found"));
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_export_env_in_module() {
|
||||||
|
let actual = nu!(cwd: "tests/parsing/samples",
|
||||||
|
r#"
|
||||||
|
module spam { export-env { } }
|
||||||
|
"#);
|
||||||
|
|
||||||
|
assert!(actual.err.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_export_env_missing_block() {
|
||||||
|
let actual = nu!(cwd: "tests/parsing/samples",
|
||||||
|
r#"
|
||||||
|
module spam { export-env }
|
||||||
|
"#);
|
||||||
|
|
||||||
|
assert!(actual.err.contains("missing block"));
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue