use std::io;
use std::process::{Command, Stdio};
pub fn open(url: &str) -> io::Result<()> {
if !(url.starts_with("http://") || url.starts_with("https://")) {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"refusing to open a non-http(s) URL",
));
}
let (program, args): (&str, &[&str]) = if cfg!(target_os = "macos") {
("open", &[])
} else if cfg!(windows) {
("cmd", &["/C", "start", ""])
} else {
("xdg-open", &[])
};
let status = Command::new(program)
.args(args)
.arg(url)
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()?;
if status.success() {
Ok(())
} else {
Err(io::Error::other(format!(
"{program} exited with status {status}"
)))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn rejects_non_http_urls() {
for url in ["file:///etc/passwd", "javascript:alert(1)", "-nope"] {
assert_eq!(
open(url).unwrap_err().kind(),
io::ErrorKind::InvalidInput,
"{url}"
);
}
}
}