1use crate::util::{eval_source, print_pipeline};
2use log::{info, trace};
3use nu_engine::eval_block;
4use nu_parser::parse;
5use nu_path::canonicalize_with;
6use nu_protocol::{
7 PipelineData, ShellError, Span, Value,
8 debugger::WithoutDebug,
9 engine::{EngineState, Stack, StateWorkingSet},
10 report_error::report_compile_error,
11 report_parse_error, report_parse_warning,
12 shell_error::io::*,
13};
14use std::{path::PathBuf, sync::Arc};
15
16pub fn evaluate_file(
21 path: String,
22 args: &[String],
23 engine_state: &mut EngineState,
24 stack: &mut Stack,
25 input: PipelineData,
26) -> Result<(), ShellError> {
27 let cwd = engine_state.cwd_as_string(Some(stack))?;
28
29 let file_path = {
30 match canonicalize_with(&path, cwd) {
31 Ok(t) => Ok(t),
32 Err(err) => {
33 let cmdline = format!("nu {path} {}", args.join(" "));
34 let mut working_set = StateWorkingSet::new(engine_state);
35 let file_id = working_set.add_file("<commandline>".into(), cmdline.as_bytes());
36 let span = working_set
37 .get_span_for_file(file_id)
38 .subspan(3, path.len() + 3)
39 .expect("<commandline> to contain script path");
40 engine_state.merge_delta(working_set.render())?;
41 let e = IoError::new(err.not_found_as(NotFound::File), span, PathBuf::from(&path));
42 Err(e)
43 }
44 }
45 }?;
46
47 let file_path_str = file_path
48 .to_str()
49 .ok_or_else(|| ShellError::NonUtf8Custom {
50 msg: format!(
51 "Input file name '{}' is not valid UTF8",
52 file_path.to_string_lossy()
53 ),
54 span: Span::unknown(),
55 })?;
56
57 let file = std::fs::read(&file_path).map_err(|err| {
58 IoError::new_internal_with_path(
59 err.not_found_as(NotFound::File),
60 "Could not read file",
61 nu_protocol::location!(),
62 file_path.clone(),
63 )
64 })?;
65 engine_state.file = Some(file_path.clone());
66
67 let parent = file_path.parent().ok_or_else(|| {
68 IoError::new_internal_with_path(
69 ErrorKind::DirectoryNotFound,
70 "The file path does not have a parent",
71 nu_protocol::location!(),
72 file_path.clone(),
73 )
74 })?;
75
76 stack.add_env_var(
77 "FILE_PWD".to_string(),
78 Value::string(parent.to_string_lossy(), Span::unknown()),
79 );
80 stack.add_env_var(
81 "CURRENT_FILE".to_string(),
82 Value::string(file_path.to_string_lossy(), Span::unknown()),
83 );
84 stack.add_env_var(
85 "PROCESS_PATH".to_string(),
86 Value::string(path, Span::unknown()),
87 );
88
89 let source_filename = file_path
90 .file_name()
91 .expect("internal error: missing filename");
92
93 let mut working_set = StateWorkingSet::new(engine_state);
94 trace!("parsing file: {file_path_str}");
95 let block = parse(&mut working_set, Some(file_path_str), &file, false);
96
97 if let Some(warning) = working_set.parse_warnings.first() {
98 report_parse_warning(&working_set, warning);
99 }
100
101 if let Some(err) = working_set.parse_errors.first() {
103 report_parse_error(&working_set, err);
104 std::process::exit(1);
105 }
106
107 if let Some(err) = working_set.compile_errors.first() {
108 report_compile_error(&working_set, err);
109 std::process::exit(1);
110 }
111
112 for block in working_set.delta.blocks.iter_mut().map(Arc::make_mut) {
114 if block.signature.name == "main" {
115 block.signature.name = source_filename.to_string_lossy().to_string();
116 } else if block.signature.name.starts_with("main ") {
117 block.signature.name =
118 source_filename.to_string_lossy().to_string() + " " + &block.signature.name[5..];
119 }
120 }
121
122 engine_state.merge_delta(working_set.delta)?;
124
125 let exit_code = if engine_state.find_decl(b"main", &[]).is_some() {
127 let pipeline =
129 match eval_block::<WithoutDebug>(engine_state, stack, &block, PipelineData::empty())
130 .map(|p| p.body)
131 {
132 Ok(data) => data,
133 Err(ShellError::Return { .. }) => {
134 return Ok(());
136 }
137 Err(err) => return Err(err),
138 };
139
140 print_pipeline(engine_state, stack, pipeline, true)?;
142
143 let args = format!("main {}", args.join(" "));
146 eval_source(
147 engine_state,
148 stack,
149 args.as_bytes(),
150 "<commandline>",
151 input,
152 true,
153 )
154 } else {
155 eval_source(engine_state, stack, &file, file_path_str, input, true)
156 };
157
158 if exit_code != 0 {
159 std::process::exit(exit_code);
160 }
161
162 info!("evaluate {}:{}:{}", file!(), line!(), column!());
163
164 Ok(())
165}