open/
lib.rs

1//! Use this library to open a path or URL using the program configured on the system in a non-blocking fashion.
2//!
3//! # Usage
4//!
5//! Open the given URL in the default web browser, without blocking.
6//!
7//! ```no_run
8//! open::that("http://rust-lang.org").unwrap();
9//! ```
10//!
11//! Alternatively, specify the program to be used to open the path or URL.
12//!
13//! ```no_run
14//! open::with("http://rust-lang.org", "firefox").unwrap();
15//! ```
16//!
17//! # Notes
18//!
19//! ## Nonblocking operation
20//!
21//! The functions provided are nonblocking as it will return even though the
22//! launched child process is still running. Note that depending on the operating
23//! system, spawning launch helpers, which this library does under the hood,
24//! might still take 100's of milliseconds.
25//!
26//! ## Error handling
27//!
28//! As an operating system program is used, the open operation can fail.
29//! Therefore, you are advised to check the result and behave
30//! accordingly, e.g. by letting the user know that the open operation failed.
31//!
32//! ```no_run
33//! let path = "http://rust-lang.org";
34//!
35//! match open::that(path) {
36//!     Ok(()) => println!("Opened '{}' successfully.", path),
37//!     Err(err) => eprintln!("An error occurred when opening '{}': {}", path, err),
38//! }
39//! ```
40
41#[cfg(target_os = "windows")]
42use windows as os;
43
44#[cfg(target_os = "macos")]
45use macos as os;
46
47#[cfg(target_os = "ios")]
48use ios as os;
49
50#[cfg(target_os = "haiku")]
51use haiku as os;
52
53#[cfg(any(
54    target_os = "linux",
55    target_os = "android",
56    target_os = "freebsd",
57    target_os = "dragonfly",
58    target_os = "netbsd",
59    target_os = "openbsd",
60    target_os = "illumos",
61    target_os = "solaris"
62))]
63use unix as os;
64
65#[cfg(not(any(
66    target_os = "linux",
67    target_os = "android",
68    target_os = "freebsd",
69    target_os = "dragonfly",
70    target_os = "netbsd",
71    target_os = "openbsd",
72    target_os = "illumos",
73    target_os = "solaris",
74    target_os = "ios",
75    target_os = "macos",
76    target_os = "windows",
77    target_os = "haiku"
78)))]
79compile_error!("open is not supported on this platform");
80
81use std::{
82    ffi::OsStr,
83    io,
84    process::{Command, Stdio},
85    thread,
86};
87
88/// Open path with the default application without blocking.
89///
90/// # Examples
91///
92/// ```no_run
93/// let path = "http://rust-lang.org";
94///
95/// match open::that(path) {
96///     Ok(()) => println!("Opened '{}' successfully.", path),
97///     Err(err) => panic!("An error occurred when opening '{}': {}", path, err),
98/// }
99/// ```
100///
101/// # Errors
102///
103/// A [`std::io::Error`] is returned on failure. Because different operating systems
104/// handle errors differently it is recommend to not match on a certain error.
105pub fn that<T: AsRef<OsStr>>(path: T) -> io::Result<()> {
106    os::that(path)
107}
108
109/// Open path with the given application.
110///
111/// This function may block if the application doesn't detach itself.
112/// In that case, consider using [`with_in_background()`].
113///
114/// # Examples
115///
116/// ```no_run
117/// let path = "http://rust-lang.org";
118/// let app = "firefox";
119///
120/// match open::with(path, app) {
121///     Ok(()) => println!("Opened '{}' successfully.", path),
122///     Err(err) => panic!("An error occurred when opening '{}': {}", path, err),
123/// }
124/// ```
125///
126/// # Errors
127///
128/// A [`std::io::Error`] is returned on failure. Because different operating systems
129/// handle errors differently it is recommend to not match on a certain error.
130pub fn with<T: AsRef<OsStr>>(path: T, app: impl Into<String>) -> io::Result<()> {
131    os::with(path, app)
132}
133
134/// Open path with the default application in a new thread.
135///
136/// See documentation of [`that()`] for more details.
137#[deprecated = "Use `that()` as it is non-blocking while making error handling easy."]
138pub fn that_in_background<T: AsRef<OsStr>>(path: T) -> thread::JoinHandle<io::Result<()>> {
139    let path = path.as_ref().to_os_string();
140    thread::spawn(|| that(path))
141}
142
143/// Open path with the given application in a new thread, which is useful if
144/// the program ends up to be blocking. Otherwise, prefer [`with()`] for
145/// straightforward error handling.
146///
147/// See documentation of [`with()`] for more details.
148pub fn with_in_background<T: AsRef<OsStr>>(
149    path: T,
150    app: impl Into<String>,
151) -> thread::JoinHandle<io::Result<()>> {
152    let path = path.as_ref().to_os_string();
153    let app = app.into();
154    thread::spawn(|| with(path, app))
155}
156
157trait IntoResult<T> {
158    fn into_result(self) -> T;
159}
160
161impl IntoResult<io::Result<()>> for io::Result<std::process::ExitStatus> {
162    fn into_result(self) -> io::Result<()> {
163        match self {
164            Ok(status) if status.success() => Ok(()),
165            Ok(status) => Err(io::Error::new(
166                io::ErrorKind::Other,
167                format!("Launcher failed with {:?}", status),
168            )),
169            Err(err) => Err(err),
170        }
171    }
172}
173
174#[cfg(windows)]
175impl IntoResult<io::Result<()>> for std::os::raw::c_int {
176    fn into_result(self) -> io::Result<()> {
177        match self {
178            i if i > 32 => Ok(()),
179            _ => Err(io::Error::last_os_error()),
180        }
181    }
182}
183
184trait CommandExt {
185    fn status_without_output(&mut self) -> io::Result<std::process::ExitStatus>;
186}
187
188impl CommandExt for Command {
189    fn status_without_output(&mut self) -> io::Result<std::process::ExitStatus> {
190        let mut process = self
191            .stdin(Stdio::null())
192            .stdout(Stdio::null())
193            .stderr(Stdio::null())
194            .spawn()?;
195
196        process.wait()
197    }
198}
199
200#[cfg(windows)]
201mod windows;
202
203#[cfg(target_os = "macos")]
204mod macos;
205
206#[cfg(target_os = "ios")]
207mod ios;
208
209#[cfg(target_os = "haiku")]
210mod haiku;
211
212#[cfg(any(
213    target_os = "linux",
214    target_os = "android",
215    target_os = "freebsd",
216    target_os = "dragonfly",
217    target_os = "netbsd",
218    target_os = "openbsd",
219    target_os = "illumos",
220    target_os = "solaris"
221))]
222mod unix;