Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conditional Complication and Std Traits #6

Merged
merged 6 commits into from
Jun 16, 2018
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
149 changes: 113 additions & 36 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,6 @@
//! Open URLs in the web browsers available on a platform
//! Open URLs in the web browsers available on a platform.
//!
//! Inspired by the [webbrowser](https://docs.python.org/2/library/webbrowser.html) python library
//!
//! #Examples
//!
//! ```
//! use webbrowser;
//!
//! if webbrowser::open("http://github.com").is_ok() {
//! // ...
//! }
//! ```
//! Inspired by the [webbrowser](https://docs.python.org/2/library/webbrowser.html) python library.
//!
//! Currently state of platform support is:
//!
Expand All @@ -24,27 +14,110 @@
//! Important note:
//!
//! * This library requires availability of browsers and a graphical environment during runtime
//! * `cargo test` will actually open the browser locally
//! * `cargo test` will actually open the browser locally.
//!
//! # Examples
//!
//! ```
//! use webbrowser;
//!
//! if webbrowser::open("http://github.com").is_ok() {
//! // ...
//! }
//! ```

use std::process::{Command, Output};
use std::io::{Result, Error, ErrorKind};
use std::env;
use std::default::Default;
use std::fmt;
use std::str::FromStr;
use std::error;

#[derive(Debug)]
#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)]
/// Browser types available
pub enum Browser {
///Operating system's default browser
Default,

///Mozilla Firefox
Firefox,

///Microsoft's Internet Explorer
InternetExplorer,

///Google Chrome
Chrome,

///Opera
Opera,

///Mac OS Safari
Safari
}

///The Error type for parsing a string into a Browser.
#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)]
pub struct ParseBrowserError;

impl fmt::Display for ParseBrowserError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("Invalid browser given")
}
}

impl error::Error for ParseBrowserError {
fn description(&self) -> &str {
"invalid browser"
}
}

impl Default for Browser {
fn default() -> Self {
Browser::Default
}
}

impl fmt::Display for Browser {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Browser::Default => f.write_str("Default"),
Browser::Firefox => f.write_str("Firefox"),
Browser::InternetExplorer => f.write_str("Internet Explorer"),
Browser::Chrome => f.write_str("Chrome"),
Browser::Opera => f.write_str("Opera"),
Browser::Safari => f.write_str("Safari"),
}
}
}

impl FromStr for Browser {
type Err = ParseBrowserError;

fn from_str(s: &str) -> ::std::result::Result<Self, Self::Err> {
match s {
"firefox" => Ok(Browser::Firefox),
"default" => Ok(Browser::Default),
"ie" | "internet explorer" | "internetexplorer" => Ok(Browser::InternetExplorer),
"chrome" => Ok(Browser::Chrome),
"opera" => Ok(Browser::Opera),
"safari" => Ok(Browser::Safari),
_ => Err(ParseBrowserError)
}
}
}

/// Opens the URL on the default browser of this platform
///
/// Returns Ok(..) so long as the browser invocation was successful. An Err(..) is returned only if
/// there was an error in running the command, or if the browser was not found
/// there was an error in running the command, or if the browser was not found.
///
/// Equivalent to:
/// ```
/// # use webbrowser::{Browser, open_browser};
/// # let url = "http://example.com";
/// open_browser(Browser::Default, url);
/// ```
///
/// # Examples
/// ```
Expand All @@ -59,7 +132,7 @@ pub fn open(url: &str) -> Result<Output> {
}

/// Opens the specified URL on the specific browser (if available) requested. Return semantics are
/// the same as for [open](fn.open.html)
/// the same as for [open](fn.open.html).
///
/// # Examples
/// ```
Expand All @@ -70,17 +143,26 @@ pub fn open(url: &str) -> Result<Output> {
/// }
/// ```
pub fn open_browser(browser: Browser, url: &str) -> Result<Output> {
let os = std::env::consts::OS;
match os {
"macos" => open_on_macos(browser, url),
"windows" => open_on_windows(browser, url),
"linux" => open_on_linux(browser, url),
_ => Err(Error::new(ErrorKind::NotFound, format!("Platform {} not yet supported by this library", os)))
open_browser_internal(browser, url)
}

/// Deal with opening of browsers on Windows, using `start` command
#[cfg(target_os = "windows")]
#[inline]
fn open_browser_internal(browser: Browser, url: &str) -> Result<Output> {
match browser {
Browser::Default => Command::new("cmd").arg("/C").arg("start").arg(url).output(),
_ => Err(Error::new(
ErrorKind::NotFound,
"Only the default browser is supported on this platform right now"
))
}
}

/// Deal with opening of browsers on Mac OS X, using `open` command
fn open_on_macos(browser: Browser, url: &str) -> Result<Output> {
#[cfg(target_os = "macos")]
#[inline]
fn open_browser_internal(browser: Browser, url: &str) -> Result<Output> {
let mut cmd = Command::new("open");
match browser {
Browser::Default => cmd.arg(url).output(),
Expand All @@ -100,24 +182,15 @@ fn open_on_macos(browser: Browser, url: &str) -> Result<Output> {
}
}

/// Deal with opening of browsers on Windows, using `start` command
fn open_on_windows(browser: Browser, url: &str) -> Result<Output> {
match browser {
Browser::Default => Command::new("cmd").arg("/C").arg("start").arg(url).output(),
_ => Err(Error::new(
ErrorKind::NotFound,
"Only the default browser is supported on this platform right now"
))
}
}

/// Deal with opening of browsers on Linux - currently supports only the default browser
///
/// The mechanism of opening the default browser is as follows:
/// 1. Attempt to use $BROWSER env var if available
/// 2. Attempt to open the url via xdg-open, gvfs-open, gnome-open, respectively, whichever works
/// first
fn open_on_linux(browser: Browser, url: &str) -> Result<Output> {
#[cfg(target_os = "linux")]
#[inline]
fn open_browser_internal(browser: Browser, url: &str) -> Result<Output> {
match browser {
Browser::Default => open_on_linux_using_browser_env(url)
.or_else(|_| -> Result<Output> {Command::new("xdg-open").arg(url).output()})
Expand All @@ -131,8 +204,9 @@ fn open_on_linux(browser: Browser, url: &str) -> Result<Output> {
}

/// Open on Linux using the $BROWSER env var
#[cfg(target_os = "linux")]
fn open_on_linux_using_browser_env(url: &str) -> Result<Output> {
let browsers = try!(env::var("BROWSER").map_err(|_| -> Error {Error::new(ErrorKind::NotFound, format!("BROWSER env not set"))}));
let browsers = env::var("BROWSER").map_err(|_| -> Error { Error::new(ErrorKind::NotFound, format!("BROWSER env not set")) })?;
for browser in browsers.split(':') { // $BROWSER can contain ':' delimited options, each representing a potential browser command line
if !browser.is_empty() {
// each browser command can have %s to represent URL, while %c needs to be replaced
Expand All @@ -155,6 +229,9 @@ fn open_on_linux_using_browser_env(url: &str) -> Result<Output> {
return Err(Error::new(ErrorKind::NotFound, "No valid command in $BROWSER"));
}

#[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "linux")))]
compile_error!("Only Windows, Mac OS and Linux are currently supported");

#[test]
fn test_open_default() {
assert!(open("http://github.com").is_ok());
Expand Down