diff --git a/src/haiku.rs b/src/haiku.rs index 1639c36..7c728d6 100644 --- a/src/haiku.rs +++ b/src/haiku.rs @@ -2,19 +2,24 @@ use std::{ffi::OsStr, io, process::Command}; use crate::{CommandExt, IntoResult}; -pub fn command>(path: T) -> Command { - let mut cmd = Command::new("/bin/open"); - cmd.arg(path.as_ref()); - cmd -} - pub fn that>(path: T) -> io::Result<()> { - command(path).status_without_output().into_result() + Command::new("/bin/open") + .arg(path.as_ref()) + .without_io() + .status() + .into_result() } pub fn with>(path: T, app: impl Into) -> io::Result<()> { Command::new(app.into()) .arg(path.as_ref()) - .status_without_output() + .without_io() + .status() .into_result() } + +pub fn commands>(path: T) -> impl Iterator { + let mut cmd = Command::new("/bin/open"); + cmd.arg(path.as_ref()); + Some(cmd).into_iter() +} diff --git a/src/ios.rs b/src/ios.rs index a3a71d7..bd0938e 100644 --- a/src/ios.rs +++ b/src/ios.rs @@ -2,14 +2,13 @@ use std::{ffi::OsStr, io, process::Command}; use crate::{CommandExt, IntoResult}; -pub fn command>(path: T) -> Command { - let mut cmd = Command::new("uiopen"); - cmd.arg("--url").arg(path.as_ref()); - cmd -} - pub fn that>(path: T) -> io::Result<()> { - command(path).status_without_output().into_result() + Command::new("uiopen") + .arg("--url") + .arg(path.as_ref()) + .without_io() + .status() + .into_result() } pub fn with>(path: T, app: impl Into) -> io::Result<()> { @@ -18,6 +17,13 @@ pub fn with>(path: T, app: impl Into) -> io::Result<()> .arg(path.as_ref()) .arg("--bundleid") .arg(app.into()) - .status_without_output() + .without_io() + .status() .into_result() } + +pub fn commands>(path: T) -> impl Iterator { + let mut cmd = Command::new("uiopen"); + cmd.arg("--url").arg(path.as_ref()); + Some(cmd).into_iter() +} diff --git a/src/lib.rs b/src/lib.rs index 1d9c102..f71bd35 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,10 +14,10 @@ //! open::with("http://rust-lang.org", "firefox").unwrap(); //! ``` //! -//! Or obtain the command without running it. +//! Or obtain commands for opening the URL without running them. //! //! ```no_run -//! let cmd = open::command("http://rust-lang.org"); +//! let cmds = open::commands("http://rust-lang.org"); //! ``` //! //! # Notes @@ -137,16 +137,19 @@ pub fn with>(path: T, app: impl Into) -> io::Result<()> os::with(path, app) } -/// Get command that opens path with the default application. +/// Get iterator over commands that open path with the default application. +/// Iterator over Commands is returned as for certain platforms there are +/// multiple command options. +/// It is the responsibility of the callee to try all commands. /// /// # Examples /// /// ```no_run /// let path = "http://rust-lang.org"; -/// let cmd = open::command(path); +/// let cmds = open::commands(path); /// ``` -pub fn command<'a, T: AsRef>(path: T) -> Command { - os::command(path) +pub fn commands>(path: T) -> impl Iterator { + os::commands(path) } /// Open path with the default application in a new thread. @@ -200,17 +203,19 @@ impl IntoResult> for std::os::raw::c_int { } trait CommandExt { - fn status_without_output(&mut self) -> io::Result; + fn without_io(&mut self) -> &mut Self; + fn status(&mut self) -> io::Result; } impl CommandExt for Command { - fn status_without_output(&mut self) -> io::Result { - let mut process = self - .stdin(Stdio::null()) + fn without_io(&mut self) -> &mut Self { + self.stdin(Stdio::null()) .stdout(Stdio::null()) .stderr(Stdio::null()) - .spawn()?; + } + fn status(&mut self) -> io::Result { + let mut process = self.spawn()?; process.wait() } } diff --git a/src/macos.rs b/src/macos.rs index 8b99ceb..4c98f1a 100644 --- a/src/macos.rs +++ b/src/macos.rs @@ -2,14 +2,12 @@ use std::{ffi::OsStr, io, process::Command}; use crate::{CommandExt, IntoResult}; -pub fn command>(path: T) -> Command { - let mut cmd = Command::new("/usr/bin/open"); - cmd.arg(path.as_ref()); - cmd -} - pub fn that>(path: T) -> io::Result<()> { - command(path).status_without_output().into_result() + Command::new("/usr/bin/open") + .arg(path.as_ref()) + .without_io() + .status() + .into_result() } pub fn with>(path: T, app: impl Into) -> io::Result<()> { @@ -17,6 +15,13 @@ pub fn with>(path: T, app: impl Into) -> io::Result<()> .arg(path.as_ref()) .arg("-a") .arg(app.into()) - .status_without_output() + .without_io() + .status() .into_result() } + +pub fn commands>(path: T) -> impl Iterator { + let mut cmd = Command::new("/usr/bin/open"); + cmd.arg(path.as_ref()); + Some(cmd).into_iter() +} diff --git a/src/unix.rs b/src/unix.rs index 044b63f..aeffd2c 100644 --- a/src/unix.rs +++ b/src/unix.rs @@ -8,7 +8,7 @@ use std::{ use crate::{CommandExt, IntoResult}; -pub fn command>(path: T) -> Command { +pub fn commands>(path: T) -> impl Iterator { let path = path.as_ref(); let open_handlers = [ ("xdg-open", &[path] as &[_]), @@ -18,33 +18,43 @@ pub fn command>(path: T) -> Command { ("wslview", &[&wsl_path(path)]), ]; - for (command, args) in &open_handlers { - let result = Command::new(command).status_without_output(); - - if let Ok(status) = result { - if status.success() { - let mut cmd = Command::new(command); - cmd.args(*args); - return cmd; - }; - }; - } - - // fallback to xdg-open - let (command, args) = &open_handlers[0]; - let mut cmd = Command::new(command); - cmd.args(*args); - cmd + open_handlers.into_iter().map(|(cmd, args)| { + let mut cmd = Command::new("/usr/bin/open"); + cmd.args(args); + cmd + }) } pub fn that>(path: T) -> io::Result<()> { - command(path).status_without_output().into_result() + let mut unsuccessful = None; + let mut io_error = None; + + for cmd in commands(path) { + let result = cmd.without_io().status().into_result(); + match result { + Ok(status) if status.success() => return Ok(()), + Ok(status) => { + unsuccessful = unsuccessful.or_else(|| { + Some(std::io::Error::new( + std::io::ErrorKind::Other, + status.to_string(), + )) + }) + } + Err(err) => io_error = io_error.or(Some(err)), + } + } + + Err(unsuccessful + .or(io_error) + .expect("successful cases don't get here")) } pub fn with>(path: T, app: impl Into) -> io::Result<()> { Command::new(app.into()) .arg(path.as_ref()) - .status_without_output() + .without_io() + .status() .into_result() } diff --git a/src/windows.rs b/src/windows.rs index 7d9cfa9..ed5fbb9 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -2,14 +2,13 @@ use std::{ffi::OsStr, io, process::Command}; use crate::{CommandExt, IntoResult}; -pub fn command>(path: T) -> Command { - let mut cmd = Command::new("cmd"); - cmd.arg("/c").arg("start").arg(path.as_ref()); - cmd -} - pub fn that>(path: T) -> io::Result<()> { - command(path).status_without_output().into_result() + Command::new("cmd") + .arg("/c") + .arg("start") + .arg(path.as_ref()) + .without_io() + .status() } pub fn with>(path: T, app: impl Into) -> io::Result<()> { @@ -17,6 +16,13 @@ pub fn with>(path: T, app: impl Into) -> io::Result<()> .arg("/c") .arg(app.into()) .arg(path.as_ref()) - .status_without_output() + .without_io() + .status() .into_result() } + +pub fn commands>(path: T) -> impl Iterator { + let mut cmd = Command::new("cmd"); + cmd.arg("/c").arg("start").arg(path.as_ref()); + Some(cmd).into_iter() +}