1pub(crate) mod duck_script;
7pub(crate) mod generic_script;
8mod os_script;
9mod rsscript;
10pub(crate) mod script_utils;
11mod shebang_script;
12mod shell_to_batch;
13
14#[cfg(test)]
15#[path = "mod_test.rs"]
16mod mod_test;
17
18use crate::environment;
19use crate::error::CargoMakeError;
20use crate::io;
21use crate::toolchain;
22use crate::types::{FlowInfo, FlowState, ScriptValue, Task};
23use std::cell::RefCell;
24use std::path::PathBuf;
25use std::rc::Rc;
26
27#[derive(Debug, Clone, PartialEq)]
28pub(crate) enum EngineType {
30 OS,
32 Duckscript,
34 Rust,
36 Shell2Batch,
38 Generic,
40 Shebang,
42 Unsupported,
44}
45
46pub(crate) fn get_script_text(script: &ScriptValue) -> Result<Vec<String>, CargoMakeError> {
47 match script {
48 ScriptValue::SingleLine(text) => Ok(vec![text.clone()]),
49 ScriptValue::Text(text) => Ok(text.clone()),
50 ScriptValue::File(info) => {
51 let mut file_path_string = String::new();
52 if !info.absolute_path.unwrap_or(false) {
53 file_path_string.push_str("${CARGO_MAKE_WORKING_DIRECTORY}/");
54 }
55 file_path_string.push_str(&info.file);
56
57 let expanded_value = environment::expand_value(&file_path_string);
59
60 let mut file_path = PathBuf::new();
61 file_path.push(expanded_value);
62
63 let script_text = io::read_text_file(&file_path)?;
64 let lines: Vec<&str> = script_text.split('\n').collect();
65
66 let mut script_lines: Vec<String> = vec![];
67
68 for line in lines.iter() {
69 script_lines.push(line.to_string());
70 }
71
72 Ok(script_lines)
73 }
74 ScriptValue::Sections(sections) => {
75 let mut script_lines = vec![];
76
77 if let Some(ref text) = sections.pre {
78 script_lines.push(text.to_string());
79 }
80 if let Some(ref text) = sections.main {
81 script_lines.push(text.to_string());
82 }
83 if let Some(ref text) = sections.post {
84 script_lines.push(text.to_string());
85 }
86
87 Ok(script_lines)
88 }
89 }
90}
91
92fn get_internal_runner(script_runner: &str) -> EngineType {
93 if script_runner == "@duckscript" {
94 debug!("Duckscript detected.");
95 EngineType::Duckscript
96 } else if script_runner == "@rust" {
97 debug!("Rust script detected.");
98 EngineType::Rust
99 } else if script_runner == "@shell" {
100 debug!("Shell to batch detected.");
101 EngineType::Shell2Batch
102 } else {
103 EngineType::Unsupported
104 }
105}
106
107pub(crate) fn get_engine_type(
108 script: &ScriptValue,
109 script_runner: &Option<String>,
110 script_extension: &Option<String>,
111) -> Result<EngineType, CargoMakeError> {
112 match script_runner {
113 Some(ref runner) => {
114 debug!("Checking script runner: {}", runner);
115
116 let engine_type = get_internal_runner(runner);
117
118 match engine_type {
119 EngineType::Unsupported => {
120 if script_extension.is_some() {
121 debug!("Generic script detected.");
123 Ok(EngineType::Generic)
124 } else {
125 debug!("OS script with custom runner detected.");
127 Ok(EngineType::OS)
128 }
129 }
130 _ => Ok(engine_type),
131 }
132 }
133 None => {
134 let script_text = get_script_text(&script)?;
136
137 let shebang = shebang_script::get_shebang(&script_text);
138 match shebang.runner {
139 Some(script_runner) => {
140 if shebang.arguments.is_none() {
141 let engine_type = get_internal_runner(&script_runner);
142
143 match engine_type {
144 EngineType::Unsupported => {
145 debug!("Shebang line does not point to an internal engine, using normal shebang script runner.");
146 Ok(EngineType::Shebang)
147 }
148 _ => Ok(engine_type),
149 }
150 } else {
151 Ok(EngineType::Shebang)
152 }
153 }
154 None => Ok(EngineType::OS),
155 }
156 }
157 }
158}
159
160pub(crate) fn invoke(
161 task: &Task,
162 flow_info: &FlowInfo,
163 flow_state: Rc<RefCell<FlowState>>,
164) -> Result<bool, CargoMakeError> {
165 match task.script {
166 Some(ref script) => {
167 let validate = !task.should_ignore_errors();
168
169 let (reset_env, original_cargo) = match task.toolchain {
171 Some(ref toolchain) => match toolchain::get_cargo_binary_path(toolchain) {
172 Some(cargo_binary) => (true, envmnt::get_set("CARGO", cargo_binary)),
173 None => (false, None),
174 },
175 None => (false, None),
176 };
177
178 let output = invoke_script_in_flow_context(
179 script,
180 task.script_runner.clone(),
181 task.script_runner_args.clone(),
182 task.script_extension.clone(),
183 validate,
184 Some(flow_info),
185 Some(flow_state),
186 );
187
188 if reset_env {
190 if let Some(value) = original_cargo {
191 envmnt::set("CARGO", value)
192 }
193 }
194
195 output
196 }
197 None => Ok(false),
198 }
199}
200
201pub(crate) fn invoke_script_in_flow_context(
202 script: &ScriptValue,
203 script_runner: Option<String>,
204 script_runner_args: Option<Vec<String>>,
205 script_extension: Option<String>,
206 validate: bool,
207 flow_info: Option<&FlowInfo>,
208 flow_state: Option<Rc<RefCell<FlowState>>>,
209) -> Result<bool, CargoMakeError> {
210 let cli_arguments = match flow_info {
211 Some(info) => match info.cli_arguments {
212 Some(ref args) => args.clone(),
213 None => vec![],
214 },
215 None => vec![],
216 };
217
218 invoke_script(
219 script,
220 script_runner,
221 script_runner_args,
222 script_extension,
223 validate,
224 flow_info,
225 flow_state,
226 &cli_arguments,
227 )
228}
229
230pub(crate) fn invoke_script_pre_flow(
231 script: &ScriptValue,
232 script_runner: Option<String>,
233 script_runner_args: Option<Vec<String>>,
234 script_extension: Option<String>,
235 validate: bool,
236 cli_arguments: &Vec<String>,
237) -> Result<bool, CargoMakeError> {
238 invoke_script(
239 script,
240 script_runner,
241 script_runner_args,
242 script_extension,
243 validate,
244 None,
245 None,
246 cli_arguments,
247 )
248}
249
250fn invoke_script(
251 script: &ScriptValue,
252 script_runner: Option<String>,
253 script_runner_args: Option<Vec<String>>,
254 script_extension: Option<String>,
255 validate: bool,
256 flow_info: Option<&FlowInfo>,
257 flow_state: Option<Rc<RefCell<FlowState>>>,
258 cli_arguments: &Vec<String>,
259) -> Result<bool, CargoMakeError> {
260 let expanded_script_runner = match script_runner {
261 Some(ref value) => Some(environment::expand_value(value)),
262 None => None,
263 };
264 let engine_type = get_engine_type(script, &expanded_script_runner, &script_extension)?;
265
266 match engine_type {
267 EngineType::OS => {
268 let script_text = get_script_text(script)?;
269 os_script::execute(
270 &script_text,
271 expanded_script_runner,
272 cli_arguments,
273 validate,
274 )
275 }
276 EngineType::Duckscript => {
277 let script_text = get_script_text(script)?;
278 duck_script::execute(&script_text, cli_arguments, flow_info, flow_state, validate)
279 }
280 EngineType::Rust => {
281 let script_text = get_script_text(script)?;
282 rsscript::execute(
283 &script_text,
284 script_runner_args.clone(),
285 cli_arguments,
286 validate,
287 )
288 }
289 EngineType::Shell2Batch => {
290 let script_text = get_script_text(script)?;
291 shell_to_batch::execute(&script_text, cli_arguments, validate)
292 }
293 EngineType::Generic => {
294 let script_text = get_script_text(script)?;
295 let extension = script_extension.clone().unwrap();
296 generic_script::execute(
297 &script_text,
298 expanded_script_runner.unwrap(),
299 extension,
300 script_runner_args.clone(),
301 cli_arguments,
302 validate,
303 )
304 }
305 EngineType::Shebang => {
306 let script_text = get_script_text(script)?;
307 let extension = script_extension.clone();
308 shebang_script::execute(&script_text, &extension, cli_arguments, validate)
309 }
310 EngineType::Unsupported => Ok(false),
311 }
312}