//! Use this library to open a path or URL using the program configured on the system in a non-blocking fashion. //! //! # Usage //! //! Open the given URL in the default web browser, without blocking. //! //! ```no_run //! open::that("http://rust-lang.org").unwrap(); //! ``` //! //! Alternatively, specify the program to be used to open the path or URL. //! //! ```no_run //! open::with("http://rust-lang.org", "firefox").unwrap(); //! ``` //! //! # Notes //! //! ## Nonblocking operation //! //! The functions provided are nonblocking as it will return even though the //! launched child process is still running. Note that depending on the operating //! system, spawning launch helpers, which this library does under the hood, //! might still take 100's of milliseconds. //! //! ## Error handling //! //! As an operating system program is used, the open operation can fail. //! Therefore, you are advised to check the result and behave //! accordingly, e.g. by letting the user know that the open operation failed. //! //! ```no_run //! let path = "http://rust-lang.org"; //! //! match open::that(path) { //! Ok(()) => println!("Opened '{}' successfully.", path), //! Err(err) => eprintln!("An error occurred when opening '{}': {}", path, err), //! } //! ``` #[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 = "haiku")] use haiku 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" ))] 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 = "macos", target_os = "windows", target_os = "haiku" )))] compile_error!("open is not supported on this platform"); use std::{ ffi::OsStr, io, process::{Command, Stdio}, thread, }; /// Open path with the default application without blocking. /// /// # Examples /// /// ```no_run /// let path = "http://rust-lang.org"; /// /// match open::that(path) { /// Ok(()) => println!("Opened '{}' successfully.", path), /// Err(err) => panic!("An error occurred when opening '{}': {}", path, err), /// } /// ``` /// /// # Errors /// /// A [`std::io::Error`] is returned on failure. Because different operating systems /// handle errors differently it is recommend to not match on a certain error. pub fn that>(path: T) -> io::Result<()> { os::that(path) } /// Open path with the given application. /// /// This function may block if the application doesn't detach itself. /// In that case, consider using [`with_in_background()`]. /// /// # Examples /// /// ```no_run /// let path = "http://rust-lang.org"; /// let app = "firefox"; /// /// match open::with(path, app) { /// Ok(()) => println!("Opened '{}' successfully.", path), /// Err(err) => panic!("An error occurred when opening '{}': {}", path, err), /// } /// ``` /// /// # Errors /// /// A [`std::io::Error`] is returned on failure. Because different operating systems /// handle errors differently it is recommend to not match on a certain error. pub fn with>(path: T, app: impl Into) -> io::Result<()> { os::with(path, app) } /// Open path with the default application in a new thread. /// /// See documentation of [`that()`] for more details. #[deprecated = "Use `that()` as it is non-blocking while making error handling easy."] pub fn that_in_background>(path: T) -> thread::JoinHandle> { let path = path.as_ref().to_os_string(); thread::spawn(|| that(path)) } /// Open path with the given application in a new thread, which is useful if /// the program ends up to be blocking. Otherwise, prefer [`with()`] for /// straightforward error handling. /// /// See documentation of [`with()`] for more details. pub fn with_in_background>( path: T, app: impl Into, ) -> thread::JoinHandle> { let path = path.as_ref().to_os_string(); let app = app.into(); thread::spawn(|| with(path, app)) } trait IntoResult { fn into_result(self) -> T; } impl IntoResult> for io::Result { fn into_result(self) -> io::Result<()> { match self { Ok(status) if status.success() => Ok(()), Ok(status) => Err(io::Error::new( io::ErrorKind::Other, format!("Launcher failed with {:?}", status), )), Err(err) => Err(err), } } } #[cfg(windows)] impl IntoResult> for std::os::raw::c_int { fn into_result(self) -> io::Result<()> { match self { i if i > 32 => Ok(()), _ => Err(io::Error::last_os_error()), } } } trait CommandExt { fn status_without_output(&mut self) -> io::Result; } impl CommandExt for Command { fn status_without_output(&mut self) -> io::Result { let mut process = self .stdin(Stdio::null()) .stdout(Stdio::null()) .stderr(Stdio::null()) .spawn()?; process.wait() } } #[cfg(windows)] mod windows; #[cfg(target_os = "macos")] mod macos; #[cfg(target_os = "ios")] mod ios; #[cfg(target_os = "haiku")] mod haiku; #[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" ))] mod unix;