diff --git a/crates/nu-cli/src/eval_file.rs b/crates/nu-cli/src/eval_file.rs index 8b629bfaf..f856281b4 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 62e99609b..43aa0bf80 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 50f5673dd..8bf5ee854 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]