1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#[cfg(windows)]
extern crate winapi;
#[cfg(not(target_os = "windows"))]
use std::process::{Command, Stdio};
use std::ffi::OsStr;
use std::io;
use std::process::ExitStatus;
use std::thread;
#[cfg(not(any(target_os = "windows", target_os = "macos")))]
pub fn that<T: AsRef<OsStr> + Sized>(path: T) -> io::Result<ExitStatus> {
let path_ref = path.as_ref();
let mut last_err: io::Error = io::Error::from_raw_os_error(0);
for program in &["xdg-open", "gnome-open", "kde-open", "wslview"] {
match Command::new(program)
.stdout(Stdio::null())
.stderr(Stdio::null())
.arg(path_ref)
.spawn()
{
Ok(mut child) => return child.wait(),
Err(err) => {
last_err = err;
continue;
}
}
}
Err(last_err)
}
#[cfg(target_os = "windows")]
pub fn that<T: AsRef<OsStr> + Sized>(path: T) -> io::Result<ExitStatus> {
use std::os::windows::ffi::OsStrExt;
use std::os::windows::process::ExitStatusExt;
use std::ptr;
use winapi::ctypes::c_int;
use winapi::um::shellapi::ShellExecuteW;
const SW_SHOW: c_int = 5;
let path = windows::convert_path(path.as_ref())?;
let operation: Vec<u16> = OsStr::new("open\0").encode_wide().collect();
let result = unsafe {
ShellExecuteW(
ptr::null_mut(),
operation.as_ptr(),
path.as_ptr(),
ptr::null(),
ptr::null(),
SW_SHOW,
)
};
if result as c_int > 32 {
Ok(ExitStatus::from_raw(0))
} else {
Err(io::Error::last_os_error())
}
}
#[cfg(target_os = "macos")]
pub fn that<T: AsRef<OsStr> + Sized>(path: T) -> io::Result<ExitStatus> {
Command::new("open")
.stdout(Stdio::null())
.stderr(Stdio::null())
.arg(path.as_ref())
.spawn()?
.wait()
}
pub fn that_in_background<T: AsRef<OsStr> + Sized>(
path: T,
) -> thread::JoinHandle<io::Result<ExitStatus>> {
let path = path.as_ref().to_os_string();
thread::spawn(|| that(path))
}
#[cfg(windows)]
mod windows {
use std::ffi::OsStr;
use std::io;
use std::os::windows::ffi::OsStrExt;
pub fn convert_path(path: &OsStr) -> io::Result<Vec<u16>> {
let mut maybe_result: Vec<_> = path.encode_wide().collect();
if maybe_result.iter().any(|&u| u == 0) {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"path contains NUL byte(s)",
));
}
maybe_result.push(0);
Ok(maybe_result)
}
}