use std::ffi::OsString;
use std::path::Path;
pub fn route(argv: Vec<OsString>, cli: &clap::Command) -> Vec<OsString> {
let mut at = 1;
let mut allow = Vec::new();
while let Some(word) = argv.get(at).and_then(|a| a.to_str()) {
if word == "--allow" {
let Some(cap) = argv.get(at + 1) else {
return argv;
};
allow.extend([OsString::from("--allow"), cap.clone()]);
at += 2;
} else if word.starts_with("--allow=") {
allow.push(argv[at].clone());
at += 1;
} else {
break;
}
}
let Some(first) = argv.get(at) else {
return argv;
};
let Some(name) = first.to_str() else {
return argv;
};
let subcommand = name == "help" || cli.find_subcommand(name).is_some();
if name.starts_with('-') || subcommand || !is_script(Path::new(name)) {
return argv;
}
let mut out = Vec::with_capacity(argv.len() + 3);
out.push(argv[0].clone());
out.extend(["run", "--quiet"].map(OsString::from));
out.extend(allow);
out.push(first.clone());
out.push(OsString::from("--"));
out.extend(argv.into_iter().skip(at + 1));
out
}
fn is_script(path: &Path) -> bool {
if path.extension().is_some_and(|e| e == "b") {
return true;
}
path.is_file() && std::fs::read(path).is_ok_and(|bytes| is_blue_shebang(&bytes))
}
pub fn is_blue_shebang(text: &[u8]) -> bool {
let Some(rest) = text.strip_prefix(b"#!") else {
return false;
};
let line = rest.split(|b| *b == b'\n').next().unwrap_or_default();
String::from_utf8_lossy(line)
.split_whitespace()
.any(|word| Path::new(word).file_name().is_some_and(|n| n == "blue"))
}
#[cfg(test)]
mod tests {
use super::*;
use clap::CommandFactory;
fn routed(args: &[&str]) -> Vec<String> {
let argv = args.iter().map(OsString::from).collect();
route(argv, &crate::Cli::command())
.into_iter()
.map(|a| a.to_string_lossy().into_owned())
.collect()
}
#[test]
fn a_b_file_runs_quietly_with_its_arguments_verbatim() {
assert_eq!(
routed(&["blue", "x.b", "-q", "--", "a"]),
["blue", "run", "--quiet", "x.b", "--", "-q", "--", "a"]
);
}
#[test]
fn allow_before_the_file_holds_the_script_to_those_capabilities() {
assert_eq!(
routed(&[
"blue",
"--allow",
"filesystem",
"--allow=clock",
"x.b",
"--allow",
"a"
]),
[
"blue",
"run",
"--quiet",
"--allow",
"filesystem",
"--allow=clock",
"x.b",
"--",
"--allow",
"a"
]
);
assert_eq!(routed(&["blue", "--allow"]), ["blue", "--allow"]);
assert_eq!(
routed(&["blue", "--allow", "none", "test", "x.b"]),
["blue", "--allow", "none", "test", "x.b"]
);
}
#[test]
fn subcommands_flags_and_other_files_are_left_to_clap() {
for args in [
&["blue"][..],
&["blue", "test", "x.b"],
&["blue", "help"],
&["blue", "--version"],
&["blue", "Cargo.toml"],
] {
assert_eq!(routed(args), args, "{args:?}");
}
}
#[test]
fn a_shebang_names_blue_as_the_program_or_an_argument() {
for yes in [
"#!/usr/bin/env blue\n1",
"#!/usr/bin/env -S blue\n",
"#!/nix/store/x-blue/bin/blue",
] {
assert!(is_blue_shebang(yes.as_bytes()), "{yes}");
}
for no in [
"#!/usr/bin/env bluez\n",
"#!/bin/sh\nblue x.b\n",
"# blue\n",
"",
] {
assert!(!is_blue_shebang(no.as_bytes()), "{no}");
}
}
}