From f1000a17b4b71218f0a33b3aa56d90edfe917cdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20=C5=BD=C3=A1dn=C3=ADk?= Date: Sat, 10 Dec 2022 19:23:44 +0200 Subject: [PATCH] Add FILE_PWD environment variable when running 'nu script.nu' (#7424) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Description When running `nu script.nu`, the `$env.FILE_PWD` will be set to the directory where the script is. Also makes the error message a bit nicer: ``` > target/debug/nu asdihga Error: nu::shell::file_not_found (link) × File not found ╭─[source:1:1] 1 │ nu · ▲ · ╰── Could not access file 'asdihga': "No such file or directory (os error 2)" ╰──── ``` # User-Facing Changes `FILE_PWD` environment variable is available when running a script as `nu script.nu`. # Tests + Formatting Don't forget to add tests that cover your changes. Make sure you've run and fixed any issues with these commands: - `cargo fmt --all -- --check` to check standard code formatting (`cargo fmt --all` applies these changes) - `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect` to check that you're using the standard code style - `cargo test --workspace` to check that all tests pass # After Submitting If your PR had any user-facing changes, update [the documentation](https://github.com/nushell/nushell.github.io) after the PR is merged, if necessary. This will help us keep the docs up to date. --- crates/nu-cli/src/eval_file.rs | 87 ++++++++++++++++++++++++--- crates/nu-protocol/src/shell_error.rs | 9 +++ tests/shell/environment/env.rs | 11 ++++ 3 files changed, 97 insertions(+), 10 deletions(-) diff --git a/crates/nu-cli/src/eval_file.rs b/crates/nu-cli/src/eval_file.rs index 8b629bfaf8..f856281b47 100644 --- a/crates/nu-cli/src/eval_file.rs +++ b/crates/nu-cli/src/eval_file.rs @@ -2,13 +2,13 @@ use crate::util::{eval_source, report_error}; use log::info; use log::trace; use miette::{IntoDiagnostic, Result}; -use nu_engine::convert_env_values; +use nu_engine::{convert_env_values, current_dir}; use nu_parser::parse; -use nu_protocol::Type; +use nu_path::canonicalize_with; use nu_protocol::{ ast::Call, engine::{EngineState, Stack, StateWorkingSet}, - Config, PipelineData, Span, Value, + Config, PipelineData, ShellError, Span, Type, Value, }; use nu_utils::stdout_write_all_and_flush; @@ -27,25 +27,92 @@ pub fn evaluate_file( std::process::exit(1); } - let file = std::fs::read(&path).into_diagnostic()?; + let cwd = current_dir(engine_state, stack)?; - engine_state.start_in_file(Some(&path)); + let file_path = { + match canonicalize_with(&path, &cwd) { + Ok(p) => p, + Err(e) => { + let working_set = StateWorkingSet::new(engine_state); + report_error( + &working_set, + &ShellError::FileNotFoundCustom( + format!("Could not access file '{}': {:?}", path, e.to_string()), + Span::unknown(), + ), + ); + std::process::exit(1); + } + } + }; + + let file_path_str = match file_path.to_str() { + Some(s) => s, + None => { + let working_set = StateWorkingSet::new(engine_state); + report_error( + &working_set, + &ShellError::NonUtf8Custom( + format!( + "Input file name '{}' is not valid UTF8", + file_path.to_string_lossy() + ), + Span::unknown(), + ), + ); + std::process::exit(1); + } + }; + + let file = match std::fs::read(&file_path).into_diagnostic() { + Ok(p) => p, + Err(e) => { + let working_set = StateWorkingSet::new(engine_state); + report_error( + &working_set, + &ShellError::FileNotFoundCustom( + format!( + "Could not read file '{}': {:?}", + file_path_str, + e.to_string() + ), + Span::unknown(), + ), + ); + std::process::exit(1); + } + }; + + engine_state.start_in_file(Some(file_path_str)); + + let mut parent = file_path.clone(); + parent.pop(); + + stack.add_env_var( + "FILE_PWD".to_string(), + Value::string(parent.to_string_lossy(), Span::unknown()), + ); let mut working_set = StateWorkingSet::new(engine_state); - trace!("parsing file: {}", path); - - let _ = parse(&mut working_set, Some(&path), &file, false, &[]); + trace!("parsing file: {}", file_path_str); + let _ = parse(&mut working_set, Some(file_path_str), &file, false, &[]); if working_set.find_decl(b"main", &Type::Any).is_some() { let args = format!("main {}", args.join(" ")); - if !eval_source(engine_state, stack, &file, &path, PipelineData::empty()) { + if !eval_source( + engine_state, + stack, + &file, + file_path_str, + PipelineData::empty(), + ) { std::process::exit(1); } if !eval_source(engine_state, stack, args.as_bytes(), "", input) { std::process::exit(1); } - } else if !eval_source(engine_state, stack, &file, &path, input) { + } else if !eval_source(engine_state, stack, &file, file_path_str, input) { std::process::exit(1); } diff --git a/crates/nu-protocol/src/shell_error.rs b/crates/nu-protocol/src/shell_error.rs index 62e99609b8..43aa0bf801 100644 --- a/crates/nu-protocol/src/shell_error.rs +++ b/crates/nu-protocol/src/shell_error.rs @@ -749,6 +749,15 @@ Either make sure {0} is a string, or add a 'to_string' entry for it in ENV_CONVE #[diagnostic(code(nu::parser::non_utf8), url(docsrs))] NonUtf8(#[label = "non-UTF8 string"] Span), + /// The given input must be valid UTF-8 for further processing. + /// + /// ## Resolution + /// + /// Check your input's encoding. Are there any funny characters/bytes? + #[error("Non-UTF8 string")] + #[diagnostic(code(nu::parser::non_utf8_custom), url(docsrs))] + NonUtf8Custom(String, #[label = "{0}"] Span), + /// A custom value could not be converted to a Dataframe. /// /// ## Resolution diff --git a/tests/shell/environment/env.rs b/tests/shell/environment/env.rs index 50f5673ddc..8bf5ee854b 100644 --- a/tests/shell/environment/env.rs +++ b/tests/shell/environment/env.rs @@ -118,6 +118,17 @@ fn passes_with_env_env_var_to_external_process() { assert_eq!(actual.out, "foo"); } +#[test] +fn has_file_pwd() { + Playground::setup("has_file_pwd", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContent("spam.nu", "$env.FILE_PWD")]); + + let actual = nu!(cwd: dirs.test(), "nu spam.nu"); + + assert!(actual.out.ends_with("has_file_pwd")); + }) +} + // FIXME: autoenv not currently implemented #[ignore] #[test]