#[cfg(target_os = "windows")]
use windows as os;
#[cfg(target_os = "macos")]
use macos as os;
#[cfg(target_os = "ios")]
use ios as os;
#[cfg(target_os = "visionos")]
use ios as os;
#[cfg(target_os = "haiku")]
use haiku as os;
#[cfg(target_os = "redox")]
use redox as os;
#[cfg(any(
target_os = "linux",
target_os = "android",
target_os = "freebsd",
target_os = "dragonfly",
target_os = "netbsd",
target_os = "openbsd",
target_os = "illumos",
target_os = "solaris",
target_os = "aix",
target_os = "hurd"
))]
use unix as os;
#[cfg(not(any(
target_os = "linux",
target_os = "android",
target_os = "freebsd",
target_os = "dragonfly",
target_os = "netbsd",
target_os = "openbsd",
target_os = "illumos",
target_os = "solaris",
target_os = "ios",
target_os = "visionos",
target_os = "macos",
target_os = "windows",
target_os = "haiku",
target_os = "redox",
target_os = "aix",
target_os = "hurd"
)))]
compile_error!("open is not supported on this platform");
use std::{
ffi::OsStr,
io,
process::{Command, Stdio},
thread,
};
pub fn that(path: impl AsRef<OsStr>) -> io::Result<()> {
let mut last_err = None;
for mut cmd in commands(path) {
match cmd.status_without_output() {
Ok(status) => {
return Ok(status).into_result(&cmd);
}
Err(err) => last_err = Some(err),
}
}
Err(last_err.expect("no launcher worked, at least one error"))
}
pub fn with(path: impl AsRef<OsStr>, app: impl Into<String>) -> io::Result<()> {
let mut cmd = with_command(path, app);
cmd.status_without_output().into_result(&cmd)
}
pub fn commands(path: impl AsRef<OsStr>) -> Vec<Command> {
os::commands(path)
}
pub fn with_command(path: impl AsRef<OsStr>, app: impl Into<String>) -> Command {
os::with_command(path, app)
}
pub fn that_in_background(path: impl AsRef<OsStr>) -> thread::JoinHandle<io::Result<()>> {
let path = path.as_ref().to_os_string();
thread::spawn(|| that(path))
}
pub fn with_in_background<T: AsRef<OsStr>>(
path: T,
app: impl Into<String>,
) -> thread::JoinHandle<io::Result<()>> {
let path = path.as_ref().to_os_string();
let app = app.into();
thread::spawn(|| with(path, app))
}
pub fn that_detached(path: impl AsRef<OsStr>) -> io::Result<()> {
#[cfg(any(not(feature = "shellexecute-on-windows"), not(windows)))]
{
let mut last_err = None;
for mut cmd in commands(path) {
match cmd.spawn_detached() {
Ok(_) => {
return Ok(());
}
Err(err) => last_err = Some(err),
}
}
Err(last_err.expect("no launcher worked, at least one error"))
}
#[cfg(all(windows, feature = "shellexecute-on-windows"))]
{
windows::that_detached(path)
}
}
pub fn with_detached<T: AsRef<OsStr>>(path: T, app: impl Into<String>) -> io::Result<()> {
#[cfg(any(not(feature = "shellexecute-on-windows"), not(windows)))]
{
let mut cmd = with_command(path, app);
cmd.spawn_detached()
}
#[cfg(all(windows, feature = "shellexecute-on-windows"))]
{
windows::with_detached(path, app)
}
}
trait IntoResult<T> {
fn into_result(self, cmd: &Command) -> T;
}
impl IntoResult<io::Result<()>> for io::Result<std::process::ExitStatus> {
fn into_result(self, cmd: &Command) -> io::Result<()> {
match self {
Ok(status) if status.success() => Ok(()),
Ok(status) => Err(io::Error::new(
io::ErrorKind::Other,
format!("Launcher {cmd:?} failed with {:?}", status),
)),
Err(err) => Err(err),
}
}
}
trait CommandExt {
fn status_without_output(&mut self) -> io::Result<std::process::ExitStatus>;
fn spawn_detached(&mut self) -> io::Result<()>;
}
impl CommandExt for Command {
fn status_without_output(&mut self) -> io::Result<std::process::ExitStatus> {
self.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()
}
fn spawn_detached(&mut self) -> io::Result<()> {
self.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null());
#[cfg(unix)]
unsafe {
use std::os::unix::process::CommandExt as _;
self.pre_exec(move || {
match libc::fork() {
-1 => return Err(io::Error::last_os_error()),
0 => (),
_ => libc::_exit(0),
}
if libc::setsid() == -1 {
return Err(io::Error::last_os_error());
}
Ok(())
});
}
#[cfg(windows)]
{
use std::os::windows::process::CommandExt;
const CREATE_NEW_PROCESS_GROUP: u32 = 0x00000200;
const CREATE_NO_WINDOW: u32 = 0x08000000;
self.creation_flags(CREATE_NEW_PROCESS_GROUP | CREATE_NO_WINDOW);
}
self.spawn().map(|_| ())
}
}
#[cfg(windows)]
mod windows;
#[cfg(target_os = "macos")]
mod macos;
#[cfg(target_os = "ios")]
mod ios;
#[cfg(target_os = "visionos")]
mod ios;
#[cfg(target_os = "haiku")]
mod haiku;
#[cfg(target_os = "redox")]
mod redox;
#[cfg(any(
target_os = "linux",
target_os = "android",
target_os = "freebsd",
target_os = "dragonfly",
target_os = "netbsd",
target_os = "openbsd",
target_os = "illumos",
target_os = "solaris",
target_os = "aix",
target_os = "hurd"
))]
mod unix;