From 69288f9203f5311d958058322523cf5d393f21a0 Mon Sep 17 00:00:00 2001 From: Hatter Jiang Date: Fri, 24 Jan 2025 23:02:23 +0800 Subject: [PATCH] feat: v1.1.4, fix runts shebang issue --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/run_rs.rs | 8 ++--- src/run_ts.rs | 97 ++++++++++++++++++++++++++++++++++++++++++++++----- 4 files changed, 95 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1267396..2243c8e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1221,7 +1221,7 @@ dependencies = [ [[package]] name = "runrs" -version = "1.1.3" +version = "1.1.4" dependencies = [ "argh", "reqwest", diff --git a/Cargo.toml b/Cargo.toml index 9e85212..fc38eb0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "runrs" -version = "1.1.3" +version = "1.1.4" edition = "2018" license = "MIT/Apache-2.0" description = "A Tool for Run Rust Scripts" diff --git a/src/run_rs.rs b/src/run_rs.rs index 6749eb0..5728895 100644 --- a/src/run_rs.rs +++ b/src/run_rs.rs @@ -2,11 +2,11 @@ use crate::{util, verify, RunScriptArgs}; use rust_util::util_env::is_env_on; use rust_util::util_os::get_user_home; -pub fn do_run_script(rs_args: &RunScriptArgs) { - if rs_args.arguments.is_empty() { +pub fn do_run_script(args: &RunScriptArgs) { + if args.arguments.is_empty() { failure_and_exit!("Must assign a script file name"); } - let script_file = &rs_args.arguments[0]; + let script_file = &args.arguments[0]; verify::verify_script(script_file, is_env_on("RUNRS_SKIP_VERIFY")); let (_, script_sha256) = util::read_file_and_digest(script_file); @@ -21,7 +21,7 @@ pub fn do_run_script(rs_args: &RunScriptArgs) { &script_sha256, &cache_script_bin_name, ); - for arg in rs_args.arguments.iter().skip(1) { + for arg in args.arguments.iter().skip(1) { run_script_cmd.arg(arg); } util::run_script_command(script_file, &cache_script_bin_name, &mut run_script_cmd) diff --git a/src/run_ts.rs b/src/run_ts.rs index 94c7628..4534edd 100644 --- a/src/run_ts.rs +++ b/src/run_ts.rs @@ -1,26 +1,36 @@ use crate::{verify, RunScriptArgs}; use rust_util::util_cmd; use rust_util::util_env::is_env_on; +use std::fs; use std::process::Command; -pub fn do_run_script(ts_args: &RunScriptArgs) { - if ts_args.arguments.is_empty() { +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: {:?}", ts_args.arguments); - let script_file = (|| { - for arg in &ts_args.arguments { + debugging!("Run ts args: {:?}", args.arguments); + let (script_file, first_arg) = (|| { + for (i, arg) in args.arguments.iter().enumerate() { if !arg.starts_with("--") { - return arg; + return (arg, i == 0); } } - &ts_args.arguments[ts_args.arguments.len() - 1] + (&args.arguments[args.arguments.len() - 1], false) })(); verify::verify_script(script_file, is_env_on("RUNTS_SKIP_VERIFY")); let mut cmd = Command::new("/usr/bin/env"); cmd.args(["-S", "deno", "run"]); - for arg in &ts_args.arguments { + if first_arg { + let first_line_args = read_first_line_parse_args(script_file); + let left_first_line_args = first_line_args + .iter() + .skip_while(|arg| *arg != "--" && *arg != "run") + .skip(1) + .collect::>(); + cmd.args(left_first_line_args); + } + for arg in &args.arguments { cmd.arg(arg); } @@ -29,3 +39,74 @@ pub fn do_run_script(ts_args: &RunScriptArgs) { failure_and_exit!("Run deno: {script_file} failed: {e}"); } } + +fn read_first_line_parse_args(script_file: &str) -> Vec { + 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 { + let mut args = vec![]; + + let mut ln = String::new(); + let mut in_quota = false; + let mut single_quota = false; + let chars = line.chars().collect::>(); + 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") + ); +}