Skip to content

Commit 0bdc6d6

Browse files
committed
fix!: Assure that(…) is non-blocking on linux
This change goes hand in hand with removing additional information from the error case which was the reason for the blocking issue on linux. Note that the top-level `Result` type was also removed.
1 parent bf6e99c commit 0bdc6d6

File tree

1 file changed

+54
-74
lines changed

1 file changed

+54
-74
lines changed

src/lib.rs

+54-74
Original file line numberDiff line numberDiff line change
@@ -72,12 +72,10 @@ compile_error!("open is not supported on this platform");
7272
use std::{
7373
ffi::OsStr,
7474
io,
75-
process::{Command, Output, Stdio},
75+
process::{Command, Stdio},
7676
thread,
7777
};
7878

79-
type Result = io::Result<()>;
80-
8179
/// Open path with the default application.
8280
///
8381
/// # Examples
@@ -95,7 +93,7 @@ type Result = io::Result<()>;
9593
///
9694
/// A [`std::io::Error`] is returned on failure. Because different operating systems
9795
/// handle errors differently it is recommend to not match on a certain error.
98-
pub fn that<T: AsRef<OsStr>>(path: T) -> Result {
96+
pub fn that<T: AsRef<OsStr>>(path: T) -> io::Result<()> {
9997
os::that(path)
10098
}
10199

@@ -117,14 +115,14 @@ pub fn that<T: AsRef<OsStr>>(path: T) -> Result {
117115
///
118116
/// A [`std::io::Error`] is returned on failure. Because different operating systems
119117
/// handle errors differently it is recommend to not match on a certain error.
120-
pub fn with<T: AsRef<OsStr>>(path: T, app: impl Into<String>) -> Result {
118+
pub fn with<T: AsRef<OsStr>>(path: T, app: impl Into<String>) -> io::Result<()> {
121119
os::with(path, app)
122120
}
123121

124122
/// Open path with the default application in a new thread.
125123
///
126124
/// See documentation of [`that`] for more details.
127-
pub fn that_in_background<T: AsRef<OsStr>>(path: T) -> thread::JoinHandle<Result> {
125+
pub fn that_in_background<T: AsRef<OsStr>>(path: T) -> thread::JoinHandle<io::Result<()>> {
128126
let path = path.as_ref().to_os_string();
129127
thread::spawn(|| that(path))
130128
}
@@ -135,7 +133,7 @@ pub fn that_in_background<T: AsRef<OsStr>>(path: T) -> thread::JoinHandle<Result
135133
pub fn with_in_background<T: AsRef<OsStr>>(
136134
path: T,
137135
app: impl Into<String>,
138-
) -> thread::JoinHandle<Result> {
136+
) -> thread::JoinHandle<io::Result<()>> {
139137
let path = path.as_ref().to_os_string();
140138
let app = app.into();
141139
thread::spawn(|| with(path, app))
@@ -145,68 +143,42 @@ trait IntoResult<T> {
145143
fn into_result(self) -> T;
146144
}
147145

148-
impl IntoResult<Result> for io::Result<Output> {
149-
fn into_result(self) -> Result {
146+
impl IntoResult<io::Result<()>> for io::Result<std::process::ExitStatus> {
147+
fn into_result(self) -> io::Result<()> {
150148
match self {
151-
Ok(o) if o.status.success() => Ok(()),
152-
Ok(o) => Err(from_output(o)),
149+
Ok(status) if status.success() => Ok(()),
150+
Ok(status) => Err(io::Error::new(
151+
io::ErrorKind::Other,
152+
format!("Launcher failed with {:?}", status),
153+
)),
153154
Err(err) => Err(err),
154155
}
155156
}
156157
}
157158

158159
#[cfg(windows)]
159-
impl IntoResult<Result> for std::os::raw::c_int {
160-
fn into_result(self) -> Result {
160+
impl IntoResult<io::Result<()>> for std::os::raw::c_int {
161+
fn into_result(self) -> io::Result<()> {
161162
match self {
162163
i if i > 32 => Ok(()),
163164
_ => Err(io::Error::last_os_error()),
164165
}
165166
}
166167
}
167168

168-
fn from_output(output: Output) -> io::Error {
169-
let error_msg = match output.stderr.is_empty() {
170-
true => output.status.to_string(),
171-
false => format!(
172-
"{} ({})",
173-
String::from_utf8_lossy(&output.stderr).trim(),
174-
output.status
175-
),
176-
};
177-
178-
io::Error::new(io::ErrorKind::Other, error_msg)
179-
}
180-
181169
trait CommandExt {
182-
fn output_stderr(&mut self) -> io::Result<Output>;
170+
fn status_without_output(&mut self) -> io::Result<std::process::ExitStatus>;
183171
}
184172

185173
impl CommandExt for Command {
186-
fn output_stderr(&mut self) -> io::Result<Output> {
174+
fn status_without_output(&mut self) -> io::Result<std::process::ExitStatus> {
187175
let mut process = self
188176
.stdin(Stdio::null())
189177
.stdout(Stdio::null())
190-
.stderr(Stdio::piped())
178+
.stderr(Stdio::null())
191179
.spawn()?;
192180

193-
// Consume all stderr - it's open just for a few programs which can't handle it being closed.
194-
use std::io::Read;
195-
let mut stderr = vec![0; 256];
196-
let mut stderr_src = process.stderr.take().expect("piped stderr");
197-
198-
let len = stderr_src.read(&mut stderr).unwrap_or(0);
199-
stderr.truncate(len);
200-
201-
// consume the rest to avoid blocking
202-
std::io::copy(&mut stderr_src, &mut std::io::sink()).ok();
203-
204-
let status = process.wait()?;
205-
Ok(Output {
206-
status,
207-
stderr,
208-
stdout: vec![],
209-
})
181+
process.wait()
210182
}
211183
}
212184

@@ -217,7 +189,7 @@ mod windows {
217189
use std::os::raw::c_int;
218190
use windows_sys::Win32::UI::Shell::ShellExecuteW;
219191

220-
use crate::{IntoResult, Result};
192+
use crate::IntoResult;
221193

222194
fn convert_path(path: &OsStr) -> io::Result<Vec<u16>> {
223195
let mut maybe_result: Vec<_> = path.encode_wide().collect();
@@ -231,7 +203,7 @@ mod windows {
231203
Ok(maybe_result)
232204
}
233205

234-
pub fn that<T: AsRef<OsStr>>(path: T) -> Result {
206+
pub fn that<T: AsRef<OsStr>>(path: T) -> io::Result<()> {
235207
const SW_SHOW: c_int = 5;
236208

237209
let path = convert_path(path.as_ref())?;
@@ -249,7 +221,7 @@ mod windows {
249221
(result as c_int).into_result()
250222
}
251223

252-
pub fn with<T: AsRef<OsStr>>(path: T, app: impl Into<String>) -> Result {
224+
pub fn with<T: AsRef<OsStr>>(path: T, app: impl Into<String>) -> io::Result<()> {
253225
const SW_SHOW: c_int = 5;
254226

255227
let path = convert_path(path.as_ref())?;
@@ -273,69 +245,69 @@ mod windows {
273245

274246
#[cfg(target_os = "macos")]
275247
mod macos {
276-
use std::{ffi::OsStr, process::Command};
248+
use std::{ffi::OsStr, io, process::Command};
277249

278-
use crate::{CommandExt, IntoResult, Result};
250+
use crate::{CommandExt, IntoResult};
279251

280-
pub fn that<T: AsRef<OsStr>>(path: T) -> Result {
252+
pub fn that<T: AsRef<OsStr>>(path: T) -> io::Result<()> {
281253
Command::new("/usr/bin/open")
282254
.arg(path.as_ref())
283-
.output_stderr()
255+
.status_without_output()
284256
.into_result()
285257
}
286258

287-
pub fn with<T: AsRef<OsStr>>(path: T, app: impl Into<String>) -> Result {
259+
pub fn with<T: AsRef<OsStr>>(path: T, app: impl Into<String>) -> io::Result<()> {
288260
Command::new("/usr/bin/open")
289261
.arg(path.as_ref())
290262
.arg("-a")
291263
.arg(app.into())
292-
.output_stderr()
264+
.status_without_output()
293265
.into_result()
294266
}
295267
}
296268

297269
#[cfg(target_os = "ios")]
298270
mod ios {
299-
use std::{ffi::OsStr, process::Command};
271+
use std::{ffi::OsStr, io, process::Command};
300272

301-
use crate::{CommandExt, IntoResult, Result};
273+
use crate::{CommandExt, IntoResult};
302274

303-
pub fn that<T: AsRef<OsStr>>(path: T) -> Result {
275+
pub fn that<T: AsRef<OsStr>>(path: T) -> io::Result<()> {
304276
Command::new("uiopen")
305277
.arg("--url")
306278
.arg(path.as_ref())
307-
.output_stderr()
279+
.status_without_output()
308280
.into_result()
309281
}
310282

311-
pub fn with<T: AsRef<OsStr>>(path: T, app: impl Into<String>) -> Result {
283+
pub fn with<T: AsRef<OsStr>>(path: T, app: impl Into<String>) -> io::Result<()> {
312284
Command::new("uiopen")
313285
.arg("--url")
314286
.arg(path.as_ref())
315287
.arg("--bundleid")
316288
.arg(app.into())
317-
.output_stderr()
289+
.status_without_output()
318290
.into_result()
319291
}
320292
}
321293

322294
#[cfg(target_os = "haiku")]
323295
mod haiku {
324-
use std::{ffi::OsStr, process::Command};
296+
use std::{ffi::OsStr, io, process::Command};
325297

326-
use crate::{CommandExt, IntoResult, Result};
298+
use crate::{CommandExt, IntoResult};
327299

328-
pub fn that<T: AsRef<OsStr>>(path: T) -> Result {
300+
pub fn that<T: AsRef<OsStr>>(path: T) -> io::Result<()> {
329301
Command::new("/bin/open")
330302
.arg(path.as_ref())
331-
.output_stderr()
303+
.status_without_output()
332304
.into_result()
333305
}
334306

335-
pub fn with<T: AsRef<OsStr>>(path: T, app: impl Into<String>) -> Result {
307+
pub fn with<T: AsRef<OsStr>>(path: T, app: impl Into<String>) -> io::Result<()> {
336308
Command::new(app.into())
337309
.arg(path.as_ref())
338-
.output_stderr()
310+
.status_without_output()
339311
.into_result()
340312
}
341313
}
@@ -354,13 +326,14 @@ mod unix {
354326
use std::{
355327
env,
356328
ffi::{OsStr, OsString},
329+
io,
357330
path::{Path, PathBuf},
358331
process::Command,
359332
};
360333

361-
use crate::{CommandExt, IntoResult, Result};
334+
use crate::{CommandExt, IntoResult};
362335

363-
pub fn that<T: AsRef<OsStr>>(path: T) -> Result {
336+
pub fn that<T: AsRef<OsStr>>(path: T) -> io::Result<()> {
364337
let path = path.as_ref();
365338
let open_handlers = [
366339
("xdg-open", &[path] as &[_]),
@@ -374,11 +347,18 @@ mod unix {
374347
let mut io_error = None;
375348

376349
for (command, args) in &open_handlers {
377-
let result = Command::new(command).args(*args).output_stderr();
350+
let result = Command::new(command).args(*args).status_without_output();
378351

379352
match result {
380-
Ok(o) if o.status.success() => return Ok(()),
381-
Ok(o) => unsuccessful = unsuccessful.or_else(|| Some(crate::from_output(o))),
353+
Ok(status) if status.success() => return Ok(()),
354+
Ok(status) => {
355+
unsuccessful = unsuccessful.or_else(|| {
356+
Some(std::io::Error::new(
357+
std::io::ErrorKind::Other,
358+
status.to_string(),
359+
))
360+
})
361+
}
382362
Err(err) => io_error = io_error.or(Some(err)),
383363
}
384364
}
@@ -388,10 +368,10 @@ mod unix {
388368
.expect("successful cases don't get here"))
389369
}
390370

391-
pub fn with<T: AsRef<OsStr>>(path: T, app: impl Into<String>) -> Result {
371+
pub fn with<T: AsRef<OsStr>>(path: T, app: impl Into<String>) -> io::Result<()> {
392372
Command::new(app.into())
393373
.arg(path.as_ref())
394-
.output_stderr()
374+
.status_without_output()
395375
.into_result()
396376
}
397377

0 commit comments

Comments
 (0)