cli/scriptengine/
shebang_script.rs

1//! # shebang_script
2//!
3//! Runs scripts using the executable defined in the shebang line.
4//!
5
6#[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)]
20/// Holds flow information
21pub(crate) struct Shebang {
22    /// The script runner
23    pub(crate) runner: Option<String>,
24    /// additional arguments
25    pub(crate) arguments: Option<Vec<String>>,
26}
27
28impl Shebang {
29    /// Creates and returns a new instance.
30    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}