Skip to content

Commit

Permalink
Merge branch 'hybras/master'
Browse files Browse the repository at this point in the history
Thanks a lot for your contribution!
  • Loading branch information
Byron committed Feb 28, 2021
2 parents c3d8262 + 6c6bad0 commit e8bb206
Show file tree
Hide file tree
Showing 2 changed files with 121 additions and 110 deletions.
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,6 @@ name = "open"

[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", features = ["shellapi"] }

[target.'cfg(all(unix, not(macos)))'.dependencies]
which = "4"
228 changes: 118 additions & 110 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,120 +46,24 @@
//! }
//! # }
//! ```
#[cfg(windows)]
extern crate winapi;

#[cfg(not(windows))]
use std::process::{Command, Stdio};

use std::{ffi::OsStr, io, process::ExitStatus, 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())
}
}
pub use windows::{that, with};

#[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()
}

#[cfg(not(any(target_os = "windows", target_os = "macos")))]
pub fn with<T: AsRef<OsStr> + Sized>(path: T, app: impl Into<String>) -> io::Result<ExitStatus> {
Command::new(app.into()).arg(path.as_ref()).spawn()?.wait()
}

#[cfg(target_os = "windows")]
pub fn with<T: AsRef<OsStr> + Sized>(path: T, app: impl Into<String>) -> 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 app_name: Vec<u16> = OsStr::new(&format!("{}\0", app.into()))
.encode_wide()
.collect();
let result = unsafe {
ShellExecuteW(
ptr::null_mut(),
operation.as_ptr(),
app_name.as_ptr(),
path.as_ptr(),
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 with<T: AsRef<OsStr> + Sized>(path: T, app: impl Into<String>) -> io::Result<ExitStatus> {
Command::new("open")
.arg(path.as_ref())
.arg("-a")
.arg(app.into())
.spawn()?
.wait()
}
pub use macos::{that, with};

#[cfg(any(
target_os = "linux",
target_os = "freebsd",
target_os = "dragonfly",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris"
))]
pub use unix::{that, with};

/// Convenience function for opening the passed path in a new thread.
/// See documentation of `that(...)` for more details.
Expand All @@ -183,9 +87,14 @@ pub fn with_in_background<T: AsRef<OsStr> + Sized>(
mod windows {
use std::ffi::OsStr;
use std::io;
use std::os::windows::ffi::OsStrExt;
use std::os::windows::{ffi::OsStrExt, process::ExitStatusExt};
use std::process::{Command, ExitStatus};
use std::ptr;

pub fn convert_path(path: &OsStr) -> io::Result<Vec<u16>> {
use winapi::ctypes::c_int;
use winapi::um::shellapi::ShellExecuteW;

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(
Expand All @@ -196,4 +105,103 @@ mod windows {
maybe_result.push(0);
Ok(maybe_result)
}

pub fn that<T: AsRef<OsStr> + Sized>(path: T) -> io::Result<ExitStatus> {
Command::new("explorer").arg(path).spawn()?.wait()
}

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

let path = convert_path(path.as_ref())?;
let operation: Vec<u16> = OsStr::new("open\0").encode_wide().collect();
let app_name: Vec<u16> = OsStr::new(&format!("{}\0", app.into()))
.encode_wide()
.collect();
let result = unsafe {
ShellExecuteW(
ptr::null_mut(),
operation.as_ptr(),
app_name.as_ptr(),
path.as_ptr(),
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")]
mod macos {

use std::{
ffi::OsStr,
io::Result,
process::{Command, ExitStatus, Stdio},
};

pub fn that<T: AsRef<OsStr> + Sized>(path: T) -> Result<ExitStatus> {
Command::new("open")
.stdout(Stdio::null())
.stderr(Stdio::null())
.arg(path.as_ref())
.spawn()?
.wait()
}

pub fn with<T: AsRef<OsStr> + Sized>(path: T, app: impl Into<String>) -> Result<ExitStatus> {
Command::new("open")
.arg(path.as_ref())
.arg("-a")
.arg(app.into())
.spawn()?
.wait()
}
}

#[cfg(any(
target_os = "linux",
target_os = "freebsd",
target_os = "dragonfly",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris"
))]
mod unix {

use std::{
ffi::OsStr,
io::{Error, Result},
process::{Command, ExitStatus, Stdio},
};

use which::which;

pub fn that<T: AsRef<OsStr> + Sized>(path: T) -> Result<ExitStatus> {
["xdg-open", "gnome-open", "kde-open", "wslview"] // Open handlers
.iter()
.find(|it| which(it).is_ok()) // find the first handler that exists
.ok_or(Error::from_raw_os_error(0)) // If not found, return err
.and_then(|program| {
// If found run the handler
Command::new(program)
.stdout(Stdio::null())
.stderr(Stdio::null())
.arg(path.as_ref())
.spawn()?
.wait()
})
}

pub fn with<T: AsRef<OsStr> + Sized>(path: T, app: impl Into<String>) -> Result<ExitStatus> {
Command::new(app.into()).arg(path.as_ref()).spawn()?.wait()
}
}

0 comments on commit e8bb206

Please sign in to comment.