diff --git a/crates/nu-command/tests/commands/mod.rs b/crates/nu-command/tests/commands/mod.rs index 4f7a604fa..6952da764 100644 --- a/crates/nu-command/tests/commands/mod.rs +++ b/crates/nu-command/tests/commands/mod.rs @@ -52,6 +52,7 @@ mod select; mod semicolon; mod skip; mod sort_by; +mod source; mod split_by; mod split_column; mod split_row; diff --git a/crates/nu-command/tests/commands/source.rs b/crates/nu-command/tests/commands/source.rs new file mode 100644 index 000000000..d67d3aae8 --- /dev/null +++ b/crates/nu-command/tests/commands/source.rs @@ -0,0 +1,95 @@ +use nu_test_support::fs::Stub::FileWithContent; +use nu_test_support::nu; +use nu_test_support::playground::Playground; + +fn try_source_foo_with_double_quotes_in(testdir: &str, playdir: &str) { + Playground::setup(playdir, |dirs, sandbox| { + let testdir = String::from(testdir); + let mut foo_file = testdir.clone(); + foo_file.push_str("/foo.nu"); + + sandbox.mkdir(&testdir); + sandbox.with_files(vec![FileWithContent(&foo_file, "echo foo")]); + + let cmd = String::from("source ") + r#"""# + &foo_file + r#"""#; + + let actual = nu!(cwd: dirs.test(), &cmd); + + assert_eq!(actual.out, "foo"); + }); +} + +fn try_source_foo_with_single_quotes_in(testdir: &str, playdir: &str) { + Playground::setup(playdir, |dirs, sandbox| { + let testdir = String::from(testdir); + let mut foo_file = testdir.clone(); + foo_file.push_str("/foo.nu"); + + sandbox.mkdir(&testdir); + sandbox.with_files(vec![FileWithContent(&foo_file, "echo foo")]); + + let cmd = String::from("source ") + r#"'"# + &foo_file + r#"'"#; + + let actual = nu!(cwd: dirs.test(), &cmd); + + assert_eq!(actual.out, "foo"); + }); +} + +fn try_source_foo_without_quotes_in(testdir: &str, playdir: &str) { + Playground::setup(playdir, |dirs, sandbox| { + let testdir = String::from(testdir); + let mut foo_file = testdir.clone(); + foo_file.push_str("/foo.nu"); + + sandbox.mkdir(&testdir); + sandbox.with_files(vec![FileWithContent(&foo_file, "echo foo")]); + + let cmd = String::from("source ") + &foo_file; + + let actual = nu!(cwd: dirs.test(), &cmd); + + assert_eq!(actual.out, "foo"); + }); +} + +#[test] +fn sources_unicode_file_in_normal_dir() { + try_source_foo_with_single_quotes_in("foo", "source_test_1"); + try_source_foo_with_double_quotes_in("foo", "source_test_2"); + try_source_foo_without_quotes_in("foo", "source_test_3"); +} + +#[test] +fn sources_unicode_file_in_unicode_dir_without_spaces_1() { + try_source_foo_with_single_quotes_in("πŸš’", "source_test_4"); + try_source_foo_with_double_quotes_in("πŸš’", "source_test_5"); + try_source_foo_without_quotes_in("πŸš’", "source_test_6"); +} + +#[cfg(not(windows))] // ':' is not allowed in Windows paths +#[test] +fn sources_unicode_file_in_unicode_dir_without_spaces_2() { + try_source_foo_with_single_quotes_in(":fire_engine:", "source_test_7"); + try_source_foo_with_double_quotes_in(":fire_engine:", "source_test_8"); + try_source_foo_without_quotes_in(":fire_engine:", "source_test_9"); +} + +#[test] +fn sources_unicode_file_in_unicode_dir_with_spaces_1() { + try_source_foo_with_single_quotes_in("e-$ Γ¨Ρ€Ρ‚πŸš’β™žδΈ­η‰‡-j", "source_test_8"); + try_source_foo_with_double_quotes_in("e-$ Γ¨Ρ€Ρ‚πŸš’β™žδΈ­η‰‡-j", "source_test_9"); +} + +#[cfg(not(windows))] // ':' is not allowed in Windows paths +#[test] +fn sources_unicode_file_in_unicode_dir_with_spaces_2() { + try_source_foo_with_single_quotes_in("e-$ Γ¨Ρ€Ρ‚:fire_engine:β™žδΈ­η‰‡-j", "source_test_10"); + try_source_foo_with_double_quotes_in("e-$ Γ¨Ρ€Ρ‚:fire_engine:β™žδΈ­η‰‡-j", "source_test_11"); +} + +#[ignore] +#[test] +fn sources_unicode_file_in_non_utf8_dir() { + // How do I create non-UTF-8 path??? +} diff --git a/crates/nu-parser/src/parse.rs b/crates/nu-parser/src/parse.rs index f12340349..ac3c2ec33 100644 --- a/crates/nu-parser/src/parse.rs +++ b/crates/nu-parser/src/parse.rs @@ -1889,9 +1889,20 @@ fn parse_call( )), ); } - if let Ok(contents) = std::fs::read_to_string(&expand_path(Cow::Borrowed(Path::new( - &lite_cmd.parts[1].item, - )))) { + + let script_path = if let Some(ref positional_args) = internal_command.args.positional { + if let Expression::FilePath(ref p) = positional_args[0].expr { + p + } else { + Path::new(&lite_cmd.parts[1].item) + } + } else { + Path::new(&lite_cmd.parts[1].item) + }; + + if let Ok(contents) = + std::fs::read_to_string(&expand_path(Cow::Borrowed(Path::new(script_path)))) + { let _ = parse(&contents, 0, scope); } else { return (