Skip to content

Commit

Permalink
feat: commands() function to obtain a list of launchers to open the…
Browse files Browse the repository at this point in the history
… given path.

This allows async applications to control the application launch in an async way,
for instance with `tokio`.
  • Loading branch information
Byron committed Mar 6, 2023
1 parent 5644f17 commit 245c95e
Show file tree
Hide file tree
Showing 6 changed files with 51 additions and 45 deletions.
6 changes: 3 additions & 3 deletions src/haiku.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ use std::{ffi::OsStr, io, process::Command};

use crate::{CommandExt, IntoResult};

pub fn command<T: AsRef<OsStr>>(path: T) -> Command {
pub fn commands<T: AsRef<OsStr>>(path: T) -> Vec<Command> {
let mut cmd = Command::new("/bin/open");
cmd.arg(path.as_ref());
cmd
vec![cmd]
}

pub fn that<T: AsRef<OsStr>>(path: T) -> io::Result<()> {
command(path).status_without_output().into_result()
commands(path)[0].status_without_output().into_result()
}

pub fn with<T: AsRef<OsStr>>(path: T, app: impl Into<String>) -> io::Result<()> {
Expand Down
6 changes: 3 additions & 3 deletions src/ios.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ use std::{ffi::OsStr, io, process::Command};

use crate::{CommandExt, IntoResult};

pub fn command<T: AsRef<OsStr>>(path: T) -> Command {
pub fn commands<T: AsRef<OsStr>>(path: T) -> Vec<Command> {
let mut cmd = Command::new("uiopen");
cmd.arg("--url").arg(path.as_ref());
cmd
vec![cmd]
}

pub fn that<T: AsRef<OsStr>>(path: T) -> io::Result<()> {
command(path).status_without_output().into_result()
commands(path)[0].status_without_output().into_result()
}

pub fn with<T: AsRef<OsStr>>(path: T, app: impl Into<String>) -> io::Result<()> {
Expand Down
32 changes: 19 additions & 13 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@
//! open::with("http://rust-lang.org", "firefox").unwrap();
//! ```
//!
//! Or obtain the command without running it.
//! Or obtain the commands to launch a file or path without running them.
//!
//! ```no_run
//! let cmd = open::command("http://rust-lang.org");
//! let cmd = open::commands("http://rust-lang.org");
//! ```
//!
//! # Notes
Expand Down Expand Up @@ -108,6 +108,11 @@ use std::{
///
/// A [`std::io::Error`] is returned on failure. Because different operating systems
/// handle errors differently it is recommend to not match on a certain error.
///
/// # Beware
///
/// Sometimes, depending on the platform and system configuration, launchers *can* block.
/// If you want to be sure they don't, use [`that_in_background()`] instead.
pub fn that<T: AsRef<OsStr>>(path: T) -> io::Result<()> {
os::that(path)
}
Expand Down Expand Up @@ -137,22 +142,26 @@ pub fn with<T: AsRef<OsStr>>(path: T, app: impl Into<String>) -> io::Result<()>
os::with(path, app)
}

/// Get command that opens path with the default application.
/// Get multiple commands that open `path` with the default application.
///
/// Each command represents a launcher to try.
///
/// # Examples
///
/// ```no_run
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let path = "http://rust-lang.org";
/// let cmd = open::command(path);
/// assert!(open::commands(path)[0].status()?.success());
/// # Ok(())
/// # }
/// ```
pub fn command<'a, T: AsRef<OsStr>>(path: T) -> Command {
os::command(path)
pub fn commands<'a, T: AsRef<OsStr>>(path: T) -> Vec<Command> {
os::commands(path)
}

/// Open path with the default application in a new thread.
/// Open path with the default application in a new thread to assure it's non-blocking.
///
/// See documentation of [`that()`] for more details.
#[deprecated = "Use `that()` as it is non-blocking while making error handling easy."]
pub fn that_in_background<T: AsRef<OsStr>>(path: T) -> thread::JoinHandle<io::Result<()>> {
let path = path.as_ref().to_os_string();
thread::spawn(|| that(path))
Expand Down Expand Up @@ -205,13 +214,10 @@ trait CommandExt {

impl CommandExt for Command {
fn status_without_output(&mut self) -> io::Result<std::process::ExitStatus> {
let mut process = self
.stdin(Stdio::null())
self.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
.spawn()?;

process.wait()
.status()
}
}

Expand Down
6 changes: 3 additions & 3 deletions src/macos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ use std::{ffi::OsStr, io, process::Command};

use crate::{CommandExt, IntoResult};

pub fn command<T: AsRef<OsStr>>(path: T) -> Command {
pub fn commands<T: AsRef<OsStr>>(path: T) -> Vec<Command> {
let mut cmd = Command::new("/usr/bin/open");
cmd.arg(path.as_ref());
cmd
vec![cmd]
}

pub fn that<T: AsRef<OsStr>>(path: T) -> io::Result<()> {
command(path).status_without_output().into_result()
commands(path)[0].status_without_output().into_result()
}

pub fn with<T: AsRef<OsStr>>(path: T, app: impl Into<String>) -> io::Result<()> {
Expand Down
40 changes: 20 additions & 20 deletions src/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,35 +8,35 @@ use std::{

use crate::{CommandExt, IntoResult};

pub fn command<T: AsRef<OsStr>>(path: T) -> Command {
pub fn commands<T: AsRef<OsStr>>(path: T) -> Vec<Command> {
let path = path.as_ref();
let open_handlers = [
[
("xdg-open", &[path] as &[_]),
("gio", &[OsStr::new("open"), path]),
("gnome-open", &[path]),
("kde-open", &[path]),
("wslview", &[&wsl_path(path)]),
];

for (command, args) in &open_handlers {
let result = Command::new(command).status_without_output();

if let Ok(_) = result {
let mut cmd = Command::new(command);
cmd.args(*args);
return cmd;
};
}

// fallback to xdg-open, even though we know it's probably not working at this point.
let (command, args) = &open_handlers[0];
let mut cmd = Command::new(command);
cmd.args(*args);
cmd
]
.iter()
.map(|(command, args)| {
let mut cmd = Command::new(command);
cmd.args(*args);
cmd
})
.collect()
}

pub fn that<T: AsRef<OsStr>>(path: T) -> io::Result<()> {
command(path).status_without_output().into_result()
let mut last_err = None;
for mut command in commands(path) {
match command.status_without_output() {
Ok(status) => {
return Ok(status).into_result();
}
Err(err) => last_err = Some(err),
}
}
Err(last_err.expect("no launcher worked, at least one error"))
}

pub fn with<T: AsRef<OsStr>>(path: T, app: impl Into<String>) -> io::Result<()> {
Expand Down
6 changes: 3 additions & 3 deletions src/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ use std::{ffi::OsStr, io, process::Command};

use crate::{CommandExt, IntoResult};

pub fn command<T: AsRef<OsStr>>(path: T) -> Command {
pub fn commands<T: AsRef<OsStr>>(path: T) -> Vec<Command> {
let mut cmd = Command::new("cmd");
cmd.arg("/c").arg("start").arg(path.as_ref());
cmd
vec![cmd]
}

pub fn that<T: AsRef<OsStr>>(path: T) -> io::Result<()> {
command(path).status_without_output().into_result()
commands(path)[0].status_without_output().into_result()
}

pub fn with<T: AsRef<OsStr>>(path: T, app: impl Into<String>) -> io::Result<()> {
Expand Down

0 comments on commit 245c95e

Please sign in to comment.