Files
runrs/src/run_ts.rs
2026-02-10 00:05:34 +08:00

149 lines
4.6 KiB
Rust

use crate::resolver::resolve_file;
use crate::{verify, RunScriptArgs};
use rust_util::util_cmd;
use rust_util::util_env::is_env_on;
use std::fs;
use std::process::Command;
const RUNTIME_DENO: &str = "--runtime-deno";
const RUNTIME_BUN: &str = "--runtime-bun";
pub fn do_run_script(args: &RunScriptArgs) {
if args.arguments.is_empty() {
failure_and_exit!("Must assign a script file name");
}
debugging!("Run ts args: {:?}", args.arguments);
let mut is_runtime_deno = true;
let (raw_script_file, first_arg) = (|| {
for (i, arg) in args.arguments.iter().enumerate() {
if arg == RUNTIME_DENO || arg == RUNTIME_BUN {
is_runtime_deno = arg == RUNTIME_DENO;
}
if !arg.starts_with("--") {
return (arg, i == 0);
}
}
(&args.arguments[args.arguments.len() - 1], false)
})();
let script_file = resolve_file(raw_script_file, args.force_update)
.unwrap_or_else(|e| failure_and_exit!("Failed to resolve script: {}", e));
let script_file = &script_file;
verify::verify_script(script_file, is_env_on("RUNTS_SKIP_VERIFY") || is_env_on("RSV"));
let mut cmd = Command::new("/usr/bin/env");
// #!/usr/bin/env runts -- --allow-env
// #!/usr/bin/env runts -- [--runtime-deno | --runtime-bun] <args>
if first_arg {
let first_line_args = read_first_line_parse_args(script_file);
debugging!("Runts first line args: {:?}", first_line_args);
let left_first_line_args = first_line_args
.iter()
.skip_while(|arg| *arg != "--" && *arg != "run")
.skip(1)
.filter(|arg| {
if *arg == RUNTIME_DENO || *arg == RUNTIME_BUN {
is_runtime_deno = *arg == RUNTIME_DENO;
debugging!(
"Runts runtime arg: {}, is runtime deno: {}",
*arg,
is_runtime_deno
);
return false;
}
true
})
.collect::<Vec<_>>();
cmd.args(["-S", iff!(is_runtime_deno, "deno", "bun"), "run"]);
cmd.args(left_first_line_args);
} else {
cmd.args(["-S", iff!(is_runtime_deno, "deno", "bun"), "run"]);
}
for arg in &args.arguments {
if arg == RUNTIME_DENO || arg == RUNTIME_BUN {
continue;
}
if arg == raw_script_file { // replace remote file with local file
cmd.arg(script_file);
} else {
cmd.arg(arg);
}
}
debugging!("Run command: {cmd:?}");
if let Err(e) = util_cmd::run_command_and_wait(&mut cmd) {
failure_and_exit!("Run deno: {script_file} failed: {e}");
}
}
fn read_first_line_parse_args(script_file: &str) -> Vec<String> {
if let Ok(script_content) = fs::read_to_string(script_file) {
if let Some(first_line) = script_content.lines().next() {
if first_line.starts_with("#!") {
return parse_args_line(&first_line);
}
}
}
vec![]
}
fn parse_args_line(line: &str) -> Vec<String> {
let mut args = vec![];
let mut ln = String::new();
let mut in_quota = false;
let mut single_quota = false;
let chars = line.chars().collect::<Vec<_>>();
let mut i = 0;
while i < chars.len() {
let c = chars[i];
if in_quota {
if (single_quota && c == '\'') || (!single_quota && c == '"') {
in_quota = false;
} else if c == '\\' {
if i + 1 < chars.len() {
i += 1;
let nc = chars[i];
ln.push(nc);
} else {
ln.push(c);
}
} else {
ln.push(c);
}
} else if c == ' ' || c == '\t' {
if !ln.is_empty() {
args.push(ln.clone());
ln.clear();
}
} else if c == '\'' {
in_quota = true;
single_quota = true;
} else if c == '"' {
in_quota = true;
single_quota = false;
} else {
ln.push(c);
}
i += 1;
}
if !ln.is_empty() {
args.push(ln);
}
args
}
#[test]
fn test_parse_args_line() {
assert!(parse_args_line("").is_empty());
assert_eq!(vec!["a".to_string()], parse_args_line("a"));
assert_eq!(
vec!["a".to_string(), "aaaa'bbb".to_string()],
parse_args_line("a 'aaaa\\'bbb'")
);
assert_eq!(
vec!["--allow-a".to_string(), "--allow-b".to_string()],
parse_args_line(" --allow-a --allow-b")
);
}