]> git.proxmox.com Git - rustc.git/blame - vendor/opener/src/lib.rs
New upstream version 1.55.0+dfsg1
[rustc.git] / vendor / opener / src / lib.rs
CommitLineData
136023e0
XL
1#![doc(html_root_url = "https://docs.rs/opener/0.5.0")]
2
f9f354fc 3//! This crate provides the [`open`] function, which opens a file or link with the default program
136023e0 4//! configured on the system:
f9f354fc
XL
5//!
6//! ```no_run
7//! # fn main() -> Result<(), ::opener::OpenError> {
8//! // open a website
9//! opener::open("https://www.rust-lang.org")?;
10//!
11//! // open a file
12//! opener::open("../Cargo.toml")?;
13//! # Ok(())
14//! # }
15//! ```
16//!
136023e0
XL
17//! An [`open_browser`] function is also provided, for when you intend on opening a file or link in a
18//! browser, specifically. This function works like the [`open`] function, but explicitly allows
19//! overriding the browser launched by setting the `$BROWSER` environment variable.
f9f354fc 20
3dfed10e
XL
21#![warn(
22 rust_2018_idioms,
23 deprecated_in_future,
24 macro_use_extern_crate,
25 missing_debug_implementations,
136023e0 26 unused_qualifications
3dfed10e 27)]
f9f354fc 28
136023e0
XL
29#[cfg(not(any(target_os = "windows", target_os = "macos")))]
30mod linux_and_more;
31#[cfg(target_os = "macos")]
32mod macos;
f9f354fc 33#[cfg(target_os = "windows")]
136023e0
XL
34mod windows;
35
36#[cfg(not(any(target_os = "windows", target_os = "macos")))]
37use crate::linux_and_more as sys;
38#[cfg(target_os = "macos")]
39use crate::macos as sys;
40#[cfg(target_os = "windows")]
41use crate::windows as sys;
42
43use std::error::Error;
44use std::ffi::{OsStr, OsString};
45use std::fmt::{self, Display, Formatter};
46use std::process::{Command, ExitStatus, Stdio};
47use std::{env, io};
48
49/// Opens a file or link with the system default program.
50///
51/// Note that a path like "rustup.rs" could potentially refer to either a file or a website. If you
52/// want to open the website, you should add the "http://" prefix, for example.
53///
54/// Also note that a result of `Ok(())` just means a way of opening the path was found, and no error
55/// occurred as a direct result of opening the path. Errors beyond that point aren't caught. For
56/// example, `Ok(())` would be returned even if a file was opened with a program that can't read the
57/// file, or a dead link was opened in a browser.
58///
59/// ## Platform Implementation Details
60///
61/// - On Windows the `ShellExecuteW` Windows API function is used.
62/// - On Mac the system `open` command is used.
63/// - On Windows Subsystem for Linux (WSL), the system `wslview` from [`wslu`] is used if available,
64/// otherwise the system `xdg-open` is used, if available.
65/// - On non-WSL Linux and other platforms,
66/// the system `xdg-open` script is used if available, otherwise an `xdg-open` script embedded in
67/// this library is used.
68///
69/// [`wslu`]: https://github.com/wslutilities/wslu/
70pub fn open<P>(path: P) -> Result<(), OpenError>
71where
72 P: AsRef<OsStr>,
73{
74 sys::open(path.as_ref())
75}
f9f354fc 76
136023e0
XL
77/// Opens a file or link with the system default program, using the `BROWSER` environment variable
78/// when set.
79///
80/// If the `BROWSER` environment variable is set, the program specified by it is used to open the
81/// path. If not, behavior is identical to [`open()`].
82pub fn open_browser<P>(path: P) -> Result<(), OpenError>
83where
84 P: AsRef<OsStr>,
85{
86 let mut path = path.as_ref();
87 if let Ok(browser_var) = env::var("BROWSER") {
88 let windows_path;
89 if is_wsl() && browser_var.ends_with(".exe") {
90 if let Some(windows_path_2) = wsl_to_windows_path(path) {
91 windows_path = windows_path_2;
92 path = &windows_path;
93 }
94 };
95
96 Command::new(&browser_var)
97 .arg(path)
98 .stdin(Stdio::null())
99 .stdout(Stdio::null())
100 .stderr(Stdio::piped())
101 .spawn()
102 .map_err(OpenError::Io)?;
103
104 Ok(())
105 } else {
106 sys::open(path)
107 }
108}
f9f354fc
XL
109
110/// An error type representing the failure to open a path. Possibly returned by the [`open`]
111/// function.
112///
113/// The `ExitStatus` variant will never be returned on Windows.
114#[derive(Debug)]
115pub enum OpenError {
116 /// An IO error occurred.
117 Io(io::Error),
118
136023e0 119 /// A command exited with a non-zero exit status.
f9f354fc
XL
120 ExitStatus {
121 /// A string that identifies the command.
122 cmd: &'static str,
123
124 /// The failed process's exit status.
125 status: ExitStatus,
126
127 /// Anything the process wrote to stderr.
128 stderr: String,
129 },
130}
131
132impl Display for OpenError {
3dfed10e 133 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f9f354fc
XL
134 match self {
135 OpenError::Io(_) => {
136 write!(f, "IO error")?;
137 }
138 OpenError::ExitStatus {
139 cmd,
140 status,
141 stderr,
142 } => {
143 write!(
144 f,
145 "command '{}' did not execute successfully; {}",
146 cmd, status
147 )?;
148
149 let stderr = stderr.trim();
150 if !stderr.is_empty() {
151 write!(f, "\ncommand stderr:\n{}", stderr)?;
152 }
153 }
154 }
155
156 Ok(())
157 }
158}
159
160impl Error for OpenError {
161 fn source(&self) -> Option<&(dyn Error + 'static)> {
162 match self {
163 OpenError::Io(inner) => Some(inner),
164 OpenError::ExitStatus { .. } => None,
165 }
166 }
167}
168
136023e0
XL
169#[cfg(target_os = "linux")]
170fn is_wsl() -> bool {
171 sys::is_wsl()
f9f354fc
XL
172}
173
136023e0
XL
174#[cfg(not(target_os = "linux"))]
175fn is_wsl() -> bool {
176 false
f9f354fc
XL
177}
178
136023e0
XL
179#[cfg(target_os = "linux")]
180fn wsl_to_windows_path(path: &OsStr) -> Option<OsString> {
181 use bstr::ByteSlice;
182 use std::os::unix::ffi::OsStringExt;
f9f354fc 183
136023e0
XL
184 let output = Command::new("wslpath")
185 .arg("-w")
186 .arg(path)
187 .stdin(Stdio::null())
188 .stdout(Stdio::piped())
189 .stderr(Stdio::null())
190 .output()
191 .ok()?;
192
193 if !output.status.success() {
194 return None;
f9f354fc 195 }
f9f354fc 196
136023e0 197 Some(OsString::from_vec(output.stdout.trim_end().to_vec()))
f9f354fc
XL
198}
199
136023e0
XL
200#[cfg(not(target_os = "linux"))]
201fn wsl_to_windows_path(_path: &OsStr) -> Option<OsString> {
202 unreachable!()
f9f354fc
XL
203}
204
205#[cfg(not(target_os = "windows"))]
136023e0
XL
206fn wait_child(child: &mut std::process::Child, cmd_name: &'static str) -> Result<(), OpenError> {
207 use std::io::Read;
f9f354fc 208
136023e0 209 let exit_status = child.wait().map_err(OpenError::Io)?;
f9f354fc
XL
210 if exit_status.success() {
211 Ok(())
212 } else {
136023e0
XL
213 let mut stderr_output = String::new();
214 if let Some(stderr) = child.stderr.as_mut() {
215 stderr.read_to_string(&mut stderr_output).ok();
216 }
f9f354fc
XL
217
218 Err(OpenError::ExitStatus {
136023e0 219 cmd: cmd_name,
f9f354fc 220 status: exit_status,
136023e0 221 stderr: stderr_output,
f9f354fc
XL
222 })
223 }
224}