use std::process::Command;
pub fn open_command_for(os: &str, url: &str) -> (String, Vec<String>) {
match os {
"macos" => ("open".to_string(), vec![url.to_string()]),
"windows" => (
"cmd".to_string(),
vec![
"/C".to_string(),
"start".to_string(),
String::new(),
url.to_string(),
],
),
_ => ("xdg-open".to_string(), vec![url.to_string()]),
}
}
pub fn open_url_via(spawn: fn(&mut Command) -> std::io::Result<()>, url: &str) -> bool {
let (program, args) = open_command_for(std::env::consts::OS, url);
let mut cmd = Command::new(program);
cmd.args(args);
match spawn(&mut cmd) {
Ok(()) => true,
Err(e) => {
tracing::warn!(error = %e, "Failed to launch browser");
false
}
}
}
pub fn spawn_detached(cmd: &mut Command) -> std::io::Result<()> {
crate::process::hide_console_window(cmd);
cmd.stdin(std::process::Stdio::null())
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null())
.spawn()
.map(|_child| ())
}
pub fn open_url(url: &str) -> bool {
open_url_via(spawn_detached, url)
}
#[cfg(test)]
mod tests {
use super::*;
use leviath_testkit::with_tracing;
#[test]
fn macos_uses_open() {
let (program, args) = open_command_for("macos", "https://x.example");
assert_eq!(program, "open");
assert_eq!(args, vec!["https://x.example"]);
}
#[test]
fn windows_uses_start_with_a_placeholder_title() {
let (program, args) = open_command_for("windows", "https://x.example");
assert_eq!(program, "cmd");
assert_eq!(args, vec!["/C", "start", "", "https://x.example"]);
}
#[test]
fn other_unixes_fall_back_to_xdg_open() {
let (program, args) = open_command_for("linux", "https://x.example");
assert_eq!(program, "xdg-open");
assert_eq!(args, vec!["https://x.example"]);
assert_eq!(open_command_for("dragonfly", "https://x").0, "xdg-open");
}
#[test]
fn open_url_via_reports_spawn_success() {
fn ok(_: &mut Command) -> std::io::Result<()> {
Ok(())
}
assert!(open_url_via(ok, "https://x.example"));
}
#[test]
fn open_url_via_reports_spawn_failure() {
fn boom(_: &mut Command) -> std::io::Result<()> {
Err(std::io::Error::other("no browser"))
}
with_tracing(|| assert!(!open_url_via(boom, "https://x.example")));
}
#[test]
fn spawn_detached_launches_a_real_process() {
let mut cmd = Command::new("true");
let _ = spawn_detached(&mut cmd);
}
#[test]
fn spawn_detached_errors_on_a_missing_program() {
let mut cmd = Command::new("/nonexistent/browser/launcher");
assert!(spawn_detached(&mut cmd).is_err());
}
#[test]
fn open_url_runs_the_real_launcher_without_opening_a_browser() {
let _ = open_url("leviath-open-url-test-target");
}
}