use std::path::Path;
use std::process::Command;
pub fn run(kind: &'static str, spec: &str, path: &Path) {
run_with_extra(kind, spec, &[], path);
}
pub fn run_with_extra(kind: &'static str, spec: &str, extra: &[String], path: &Path) {
let Some((program, args)) = split_command(spec) else {
tracing::warn!(kind, "open command is empty; cannot open a file");
return;
};
match Command::new(program)
.args(args)
.args(extra)
.arg(path)
.status()
{
Ok(status) if status.success() => {}
Ok(status) => {
tracing::warn!(kind, command = %spec, %status, "external program exited non-zero")
}
Err(err) => {
tracing::warn!(kind, command = %spec, %err, "failed to launch external program")
}
}
}
fn split_command(spec: &str) -> Option<(&str, Vec<&str>)> {
let mut parts = spec.split_whitespace();
let program = parts.next()?;
Some((program, parts.collect()))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn splits_a_bare_program() {
assert_eq!(split_command("vi"), Some(("vi", vec![])));
assert_eq!(split_command("less"), Some(("less", vec![])));
}
#[test]
fn splits_a_program_with_flags() {
assert_eq!(split_command("code -w"), Some(("code", vec!["-w"])));
assert_eq!(split_command("less -R"), Some(("less", vec!["-R"])));
assert_eq!(
split_command("emacsclient -t -a "),
Some(("emacsclient", vec!["-t", "-a"]))
);
}
#[test]
fn blank_spec_is_none() {
assert_eq!(split_command(""), None);
assert_eq!(split_command(" "), None);
}
}