Skip to main content

onyx/process/
mod.rs

1//! Launches and relaunches the host application across platforms, hiding the
2//! per-OS command needed to open a fresh instance.
3
4use std::process::Command;
5
6use thiserror::Error;
7
8use crate::osinfo::Platform;
9use crate::shell::{Resolver, ShellError};
10
11/// Errors returned by [`relaunch`].
12#[derive(Debug, Error)]
13pub enum ProcessError {
14    /// The application path was empty.
15    #[error("process: application path required")]
16    EmptyPath,
17    /// The platform launcher (`open`) could not be located.
18    #[error("process: locate launcher: {0}")]
19    Locate(#[from] ShellError),
20    /// The new process could not be started.
21    #[error("process: relaunch: {0}")]
22    Spawn(#[from] std::io::Error),
23}
24
25/// relaunch starts a fresh instance of the application at the given path. The
26/// caller quits the current process afterwards. On macOS the path is the
27/// `.app` bundle, on Windows the executable, and on Linux the binary.
28pub fn relaunch(application_path: &str) -> Result<(), ProcessError> {
29    if application_path.is_empty() {
30        return Err(ProcessError::EmptyPath);
31    }
32    let platform = Platform::current();
33    if platform.is_darwin() {
34        let opener = Resolver::new()
35            .lookups(["open", "/usr/bin/open"])
36            .resolve()?;
37        Command::new(opener)
38            .args(["-n", application_path])
39            .spawn()?;
40        return Ok(());
41    }
42    if platform.is_windows() {
43        Command::new("cmd")
44            .args(["/c", "start", "", application_path])
45            .spawn()?;
46        return Ok(());
47    }
48    Command::new(application_path).spawn()?;
49    Ok(())
50}
51
52#[cfg(test)]
53mod tests {
54    use super::*;
55
56    #[test]
57    fn requires_path() {
58        assert!(matches!(relaunch(""), Err(ProcessError::EmptyPath)));
59    }
60}