cli/scriptengine/
shebang_script.rs1#[cfg(test)]
7#[path = "shebang_script_test.rs"]
8mod shebang_script_test;
9
10use crate::error::CargoMakeError;
11use crate::scriptengine::generic_script;
12use std::path::Path;
13
14#[cfg(target_os = "windows")]
15const DEFAULT_EXTENSION: &'static str = "cmd.exe";
16#[cfg(not(target_os = "windows"))]
17const DEFAULT_EXTENSION: &'static str = "sh";
18
19#[derive(Debug, Clone)]
20pub(crate) struct Shebang {
22 pub(crate) runner: Option<String>,
24 pub(crate) arguments: Option<Vec<String>>,
26}
27
28impl Shebang {
29 fn new() -> Shebang {
31 Shebang {
32 runner: None,
33 arguments: None,
34 }
35 }
36}
37
38pub(crate) fn get_shebang(script_text: &Vec<String>) -> Shebang {
39 match script_text.first() {
40 Some(line) => {
41 let trimmed_line = line.trim();
42
43 if trimmed_line.starts_with("#!") {
44 let mut lines = trimmed_line.split("\n");
45 match lines.next() {
46 Some(first_line) => {
47 let mut shebang_line = first_line.replace("#!", "");
48 shebang_line = shebang_line.trim().to_string();
49
50 if !shebang_line.is_empty() {
51 let mut values = shebang_line.split_whitespace();
52 let runner = match values.next() {
53 Some(value) => Some(value.trim().to_string()),
54 _ => None,
55 };
56
57 let mut argument_values = vec![];
58 for arg in values {
59 argument_values.push(arg.trim().to_string());
60 }
61
62 let arguments = if !argument_values.is_empty() {
63 Some(argument_values)
64 } else {
65 None
66 };
67
68 Shebang { runner, arguments }
69 } else {
70 Shebang::new()
71 }
72 }
73 _ => Shebang::new(),
74 }
75 } else {
76 Shebang::new()
77 }
78 }
79 None => Shebang::new(),
80 }
81}
82
83fn get_extension_for_runner(runner: &str) -> String {
84 let runner_no_extension = match Path::new(runner).file_stem() {
85 Some(value) => value,
86 None => return DEFAULT_EXTENSION.to_string(),
87 };
88
89 let extension = if runner_no_extension == "python" {
90 "py"
91 } else if runner_no_extension == "perl" {
92 "pl"
93 } else if runner_no_extension == "node" {
94 "js"
95 } else if runner_no_extension == "powershell" || runner_no_extension == "pwsh" {
96 "ps1"
97 } else {
98 DEFAULT_EXTENSION
99 };
100
101 extension.to_string()
102}
103
104pub(crate) fn execute(
105 script_text: &Vec<String>,
106 extension: &Option<String>,
107 cli_arguments: &Vec<String>,
108 validate: bool,
109) -> Result<bool, CargoMakeError> {
110 let shebang = get_shebang(&script_text);
111
112 match shebang.runner {
113 Some(runner) => {
114 let extension_str = match extension {
115 Some(value) => value.to_string(),
116 None => get_extension_for_runner(&runner),
117 };
118
119 generic_script::execute(
120 &script_text,
121 runner,
122 extension_str,
123 shebang.arguments,
124 &cli_arguments,
125 validate,
126 )
127 }
128 None => {
129 if validate {
130 error!("Unable to execute script using shebang.");
131 }
132
133 Ok(false)
134 }
135 }
136}