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;