mirror of
https://github.com/nushell/nushell.git
synced 2024-11-22 00:13:21 +01:00
Allow recursive module dirs; Require mod.nu in dirs (#9185)
This commit is contained in:
parent
92c1051143
commit
8d8304cf91
@ -1825,14 +1825,22 @@ pub fn parse_module_file_or_dir(
|
|||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut file_paths = vec![];
|
let mod_nu_path = module_path.join("mod.nu");
|
||||||
|
|
||||||
|
if !(mod_nu_path.exists() && mod_nu_path.is_file()) {
|
||||||
|
working_set.error(ParseError::ModuleMissingModNuFile(path_span));
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut paths = vec![];
|
||||||
|
|
||||||
for entry in dir_contents.flatten() {
|
for entry in dir_contents.flatten() {
|
||||||
let entry_path = entry.path();
|
let entry_path = entry.path();
|
||||||
|
|
||||||
if entry_path.is_file()
|
if (entry_path.is_file()
|
||||||
&& entry_path.extension() == Some(OsStr::new("nu"))
|
&& entry_path.extension() == Some(OsStr::new("nu"))
|
||||||
&& entry_path.file_stem() != Some(OsStr::new("mod"))
|
&& entry_path.file_stem() != Some(OsStr::new("mod")))
|
||||||
|
|| (entry_path.is_dir() && entry_path.join("mod.nu").exists())
|
||||||
{
|
{
|
||||||
if entry_path.file_stem() == Some(OsStr::new(&module_name)) {
|
if entry_path.file_stem() == Some(OsStr::new(&module_name)) {
|
||||||
working_set.error(ParseError::InvalidModuleFileName(
|
working_set.error(ParseError::InvalidModuleFileName(
|
||||||
@ -1843,64 +1851,54 @@ pub fn parse_module_file_or_dir(
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
file_paths.push(entry_path);
|
paths.push(entry_path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
file_paths.sort();
|
paths.sort();
|
||||||
|
|
||||||
// working_set.enter_scope();
|
// working_set.enter_scope();
|
||||||
|
|
||||||
let mut submodules = vec![];
|
let mut submodules = vec![];
|
||||||
|
|
||||||
for file_path in file_paths {
|
for p in paths {
|
||||||
if let Some(submodule_id) =
|
if let Some(submodule_id) = parse_module_file_or_dir(
|
||||||
parse_module_file(working_set, file_path, path_span, None)
|
working_set,
|
||||||
{
|
p.to_string_lossy().as_bytes(),
|
||||||
|
path_span,
|
||||||
|
None,
|
||||||
|
) {
|
||||||
let submodule_name = working_set.get_module(submodule_id).name();
|
let submodule_name = working_set.get_module(submodule_id).name();
|
||||||
submodules.push((submodule_name, submodule_id));
|
submodules.push((submodule_name, submodule_id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mod_nu_path = module_path.join("mod.nu");
|
if let Some(module_id) = parse_module_file(
|
||||||
|
working_set,
|
||||||
if mod_nu_path.exists() && mod_nu_path.is_file() {
|
mod_nu_path,
|
||||||
if let Some(module_id) = parse_module_file(
|
path_span,
|
||||||
working_set,
|
name_override.or(Some(module_name)),
|
||||||
mod_nu_path,
|
) {
|
||||||
path_span,
|
let mut module = working_set.get_module(module_id).clone();
|
||||||
name_override.or(Some(module_name)),
|
|
||||||
) {
|
|
||||||
let mut module = working_set.get_module(module_id).clone();
|
|
||||||
|
|
||||||
for (submodule_name, submodule_id) in submodules {
|
|
||||||
module.add_submodule(submodule_name, submodule_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
let module_name = String::from_utf8_lossy(&module.name).to_string();
|
|
||||||
|
|
||||||
let module_comments =
|
|
||||||
if let Some(comments) = working_set.get_module_comments(module_id) {
|
|
||||||
comments.to_vec()
|
|
||||||
} else {
|
|
||||||
vec![]
|
|
||||||
};
|
|
||||||
|
|
||||||
let new_module_id =
|
|
||||||
working_set.add_module(&module_name, module, module_comments);
|
|
||||||
|
|
||||||
Some(new_module_id)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let mut module = Module::new(module_name.as_bytes().to_vec());
|
|
||||||
|
|
||||||
for (submodule_name, submodule_id) in submodules {
|
for (submodule_name, submodule_id) in submodules {
|
||||||
module.add_submodule(submodule_name, submodule_id);
|
module.add_submodule(submodule_name, submodule_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(working_set.add_module(&module_name, module, vec![]))
|
let module_name = String::from_utf8_lossy(&module.name).to_string();
|
||||||
|
|
||||||
|
let module_comments =
|
||||||
|
if let Some(comments) = working_set.get_module_comments(module_id) {
|
||||||
|
comments.to_vec()
|
||||||
|
} else {
|
||||||
|
vec![]
|
||||||
|
};
|
||||||
|
|
||||||
|
let new_module_id = working_set.add_module(&module_name, module, module_comments);
|
||||||
|
|
||||||
|
Some(new_module_id)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
working_set.error(ParseError::ModuleNotFound(path_span));
|
working_set.error(ParseError::ModuleNotFound(path_span));
|
||||||
|
@ -193,6 +193,13 @@ pub enum ParseError {
|
|||||||
)]
|
)]
|
||||||
ModuleNotFound(#[label = "module not found"] Span),
|
ModuleNotFound(#[label = "module not found"] Span),
|
||||||
|
|
||||||
|
#[error("Missing mod.nu file.")]
|
||||||
|
#[diagnostic(
|
||||||
|
code(nu::parser::module_missing_mod_nu_file),
|
||||||
|
help("When importing a directory as a Nushell module, it needs to contain a mod.nu file (can be empty). Alternatively, you can use .nu files in the directory as modules individually.")
|
||||||
|
)]
|
||||||
|
ModuleMissingModNuFile(#[label = "module directory is missing a mod.nu file"] Span),
|
||||||
|
|
||||||
#[error("Cyclical module import.")]
|
#[error("Cyclical module import.")]
|
||||||
#[diagnostic(code(nu::parser::cyclical_module_import), help("{0}"))]
|
#[diagnostic(code(nu::parser::cyclical_module_import), help("{0}"))]
|
||||||
CyclicalModuleImport(String, #[label = "detected cyclical module import"] Span),
|
CyclicalModuleImport(String, #[label = "detected cyclical module import"] Span),
|
||||||
@ -484,6 +491,7 @@ impl ParseError {
|
|||||||
ParseError::AliasNotValid(s) => *s,
|
ParseError::AliasNotValid(s) => *s,
|
||||||
ParseError::CommandDefNotValid(s) => *s,
|
ParseError::CommandDefNotValid(s) => *s,
|
||||||
ParseError::ModuleNotFound(s) => *s,
|
ParseError::ModuleNotFound(s) => *s,
|
||||||
|
ParseError::ModuleMissingModNuFile(s) => *s,
|
||||||
ParseError::NamedAsModule(_, _, _, s) => *s,
|
ParseError::NamedAsModule(_, _, _, s) => *s,
|
||||||
ParseError::ModuleDoubleMain(_, s) => *s,
|
ParseError::ModuleDoubleMain(_, s) => *s,
|
||||||
ParseError::InvalidModuleFileName(_, _, s) => *s,
|
ParseError::InvalidModuleFileName(_, _, s) => *s,
|
||||||
|
@ -641,6 +641,27 @@ fn module_dir() {
|
|||||||
assert_eq!(actual.out, "spambaz");
|
assert_eq!(actual.out, "spambaz");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn module_dir_deep() {
|
||||||
|
let import = "use samples/spam";
|
||||||
|
|
||||||
|
let inp = &[import, "spam bacon"];
|
||||||
|
let actual_repl = nu!(cwd: "tests/modules", pipeline(&inp.join("; ")));
|
||||||
|
assert_eq!(actual_repl.out, "bacon");
|
||||||
|
|
||||||
|
let inp = &[import, "spam bacon foo"];
|
||||||
|
let actual_repl = nu!(cwd: "tests/modules", pipeline(&inp.join("; ")));
|
||||||
|
assert_eq!(actual_repl.out, "bacon foo");
|
||||||
|
|
||||||
|
let inp = &[import, "spam bacon beans"];
|
||||||
|
let actual_repl = nu!(cwd: "tests/modules", pipeline(&inp.join("; ")));
|
||||||
|
assert_eq!(actual_repl.out, "beans");
|
||||||
|
|
||||||
|
let inp = &[import, "spam bacon beans foo"];
|
||||||
|
let actual_repl = nu!(cwd: "tests/modules", pipeline(&inp.join("; ")));
|
||||||
|
assert_eq!(actual_repl.out, "beans foo");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn module_dir_import_twice_no_panic() {
|
fn module_dir_import_twice_no_panic() {
|
||||||
let import = "use samples/spam";
|
let import = "use samples/spam";
|
||||||
@ -656,6 +677,13 @@ fn not_allowed_submodule_file() {
|
|||||||
assert!(actual.err.contains("invalid_module_file_name"));
|
assert!(actual.err.contains("invalid_module_file_name"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn module_dir_missing_mod_nu() {
|
||||||
|
let inp = &["use samples/missing_mod_nu"];
|
||||||
|
let actual = nu!(cwd: "tests/modules", pipeline(&inp.join("; ")));
|
||||||
|
assert!(actual.err.contains("module_missing_mod_nu_file"));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn allowed_local_module() {
|
fn allowed_local_module() {
|
||||||
let inp = &["module spam { module spam {} }"];
|
let inp = &["module spam { module spam {} }"];
|
||||||
|
0
tests/modules/samples/missing_mod_nu/test.nu
Normal file
0
tests/modules/samples/missing_mod_nu/test.nu
Normal file
0
tests/modules/samples/not_allowed/mod.nu
Normal file
0
tests/modules/samples/not_allowed/mod.nu
Normal file
1
tests/modules/samples/spam/bacon/beans/foo.nu
Normal file
1
tests/modules/samples/spam/bacon/beans/foo.nu
Normal file
@ -0,0 +1 @@
|
|||||||
|
export def main [] { 'beans foo' }
|
3
tests/modules/samples/spam/bacon/beans/mod.nu
Normal file
3
tests/modules/samples/spam/bacon/beans/mod.nu
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export def test [] { 'test' }
|
||||||
|
|
||||||
|
export def main [] { 'beans' }
|
1
tests/modules/samples/spam/bacon/foo.nu
Normal file
1
tests/modules/samples/spam/bacon/foo.nu
Normal file
@ -0,0 +1 @@
|
|||||||
|
export def main [] { 'bacon foo' }
|
3
tests/modules/samples/spam/bacon/mod.nu
Normal file
3
tests/modules/samples/spam/bacon/mod.nu
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export def test [] { 'test' }
|
||||||
|
|
||||||
|
export def main [] { 'bacon' }
|
Loading…
Reference in New Issue
Block a user