1use std::{io::Read, path::Path};
4
5use crate::{
6 ExecutionControlFlow, ExecutionParameters, ExecutionResult, arithmetic::Evaluatable as _,
7 callstack, error, interp::Execute as _, openfiles, trace_categories,
8};
9
10impl<SE: crate::extensions::ShellExtensions> crate::Shell<SE> {
11 pub fn default_exec_params(&self) -> ExecutionParameters {
13 ExecutionParameters::default()
14 }
15
16 pub(super) async fn source_if_exists(
17 &mut self,
18 path: impl AsRef<Path>,
19 params: &ExecutionParameters,
20 ) -> Result<bool, error::Error> {
21 let path = path.as_ref();
22 if path.exists() {
23 self.source_script(path, std::iter::empty::<String>(), params)
24 .await?;
25 Ok(true)
26 } else {
27 tracing::debug!("skipping non-existent file: {}", path.display());
28 Ok(false)
29 }
30 }
31
32 pub async fn source_script<S: Into<String>, P: AsRef<Path>, I: Iterator<Item = S>>(
40 &mut self,
41 path: P,
42 args: I,
43 params: &ExecutionParameters,
44 ) -> Result<ExecutionResult, error::Error> {
45 self.parse_and_execute_script_file(
46 path.as_ref(),
47 args,
48 params,
49 callstack::ScriptCallType::Source,
50 )
51 .await
52 }
53
54 async fn parse_and_execute_script_file<
63 S: Into<String>,
64 P: AsRef<Path>,
65 I: Iterator<Item = S>,
66 >(
67 &mut self,
68 path: P,
69 args: I,
70 params: &ExecutionParameters,
71 call_type: callstack::ScriptCallType,
72 ) -> Result<ExecutionResult, error::Error> {
73 let path = path.as_ref();
74 tracing::debug!("sourcing: {}", path.display());
75
76 let mut options = std::fs::File::options();
77 options.read(true);
78
79 let opened_file: openfiles::OpenFile = self
80 .open_file(&options, path, params)
81 .map_err(|e| error::ErrorKind::FailedSourcingFile(path.to_owned(), e))?;
82
83 if opened_file.is_dir() {
84 return Err(error::ErrorKind::FailedSourcingFile(
85 path.to_owned(),
86 std::io::Error::from(std::io::ErrorKind::IsADirectory),
87 )
88 .into());
89 }
90
91 let source_info = crate::SourceInfo::from(path.to_owned());
92
93 let mut result = self
94 .source_file(opened_file, &source_info, args, params, call_type)
95 .await?;
96
97 if matches!(
101 result.next_control_flow,
102 ExecutionControlFlow::ReturnFromFunctionOrScript
103 ) {
104 result.next_control_flow = ExecutionControlFlow::Normal;
105 }
106
107 Ok(result)
108 }
109
110 async fn source_file<F: Read, S: Into<String>, I: Iterator<Item = S>>(
120 &mut self,
121 file: F,
122 source_info: &crate::SourceInfo,
123 args: I,
124 params: &ExecutionParameters,
125 call_type: callstack::ScriptCallType,
126 ) -> Result<ExecutionResult, error::Error> {
127 let mut reader = std::io::BufReader::new(file);
128 let mut parser = brush_parser::Parser::new(&mut reader, &self.parser_options());
129
130 tracing::debug!(target: trace_categories::PARSE, "Parsing sourced file: {}", source_info.source);
131 let parse_result = parser.parse_program();
132
133 let script_positional_args = args.map(Into::into);
134
135 self.call_stack
136 .push_script(call_type, source_info, script_positional_args);
137
138 let result = self
139 .run_parsed_result(parse_result, source_info, params)
140 .await;
141
142 self.call_stack.pop();
143
144 result
145 }
146
147 pub async fn run_string<S: Into<String>>(
155 &mut self,
156 command: S,
157 source_info: &crate::SourceInfo,
158 params: &ExecutionParameters,
159 ) -> Result<ExecutionResult, error::Error> {
160 let parse_result = self.parse_string(command);
161 self.run_parsed_result(parse_result, source_info, params)
162 .await
163 }
164
165 pub async fn run_script<S: Into<String>, P: AsRef<Path>, I: Iterator<Item = S>>(
172 &mut self,
173 script_path: P,
174 args: I,
175 ) -> Result<ExecutionResult, error::Error> {
176 let params = self.default_exec_params();
177 let result = self
178 .parse_and_execute_script_file(
179 script_path.as_ref(),
180 args,
181 ¶ms,
182 callstack::ScriptCallType::Run,
183 )
184 .await?;
185
186 let _ = self.on_exit().await;
187
188 Ok(result)
189 }
190
191 pub(crate) async fn run_parsed_result(
192 &mut self,
193 parse_result: Result<brush_parser::ast::Program, brush_parser::ParseError>,
194 source_info: &crate::SourceInfo,
195 params: &ExecutionParameters,
196 ) -> Result<ExecutionResult, error::Error> {
197 let result = match parse_result {
199 Ok(prog) => self.run_program(prog, params).await,
200 Err(parse_err) => Err(error::Error::from(error::ErrorKind::ParseError(
201 parse_err,
202 source_info.clone(),
203 ))
204 .into_fatal()),
205 };
206
207 match result {
209 Ok(result) => Ok(result),
210 Err(err) => {
211 let _ = self.display_error(&mut params.stderr(self), &err);
212
213 let result = err.into_result(self);
214 self.set_last_exit_status(result.exit_code.into());
215
216 Ok(result)
217 }
218 }
219 }
220
221 pub async fn run_program(
228 &mut self,
229 program: brush_parser::ast::Program,
230 params: &ExecutionParameters,
231 ) -> Result<ExecutionResult, error::Error> {
232 program.execute(self, params).await
233 }
234
235 pub fn eval_arithmetic(
237 &mut self,
238 expr: &brush_parser::ast::ArithmeticExpr,
239 ) -> Result<i64, error::Error> {
240 Ok(expr.eval(self)?)
241 }
242}