<commit_before>//! Open URLs in the web browsers available on a platform.
//!
//! Inspired by the [webbrowser](https://docs.python.org/2/library/webbrowser.html) python library.
//!
//! Currently state of platform support is:
//!
//! * macos => default, as well as browsers listed under [Browser](enum.Browser.html)
//! * windows => default browser only
//! * linux or *bsd => default browser only (uses $BROWSER env var, failing back to xdg-open, gvfs-open and
//! gnome-open, in that order)
//! * android => not supported right now
//! * ios => not supported right now
//!
//! Important note:
//!
//! * This library requires availability of browsers and a graphical environment during runtime
//! * `cargo test` will actually open the browser locally.
//!
//! # Examples
//!
//! ```no_run
//! use webbrowser;
//!
//! if webbrowser::open("http://github.com").is_ok() {
//!     // ...
//! }
//! ```

#[cfg(windows)]
extern crate widestring;
#[cfg(windows)]
extern crate winapi;

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

#[cfg(windows)]
use std::os::windows::process::ExitStatusExt;
#[cfg(windows)]
use std::ptr;

#[cfg(unix)]
use std::os::unix::process::ExitStatusExt;

#[cfg(not(windows))]
use std::process::Command;

#[cfg(windows)]
use widestring::U16CString;

#[cfg(target_arch = "wasm32")]
use web_sys::{Window};

#[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,

    ///Haiku's WebPositive
    WebPositive,
}

///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"),
            Browser::WebPositive => f.write_str("WebPositive"),
        }
    }
}

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),
            "webpositive" => Ok(Browser::WebPositive),
            _ => 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.
///
/// Equivalent to:
/// ```no_run
/// # use webbrowser::{Browser, open_browser};
/// # let url = "http://example.com";
/// open_browser(Browser::Default, url);
/// ```
///
/// # Examples
/// ```no_run
/// use webbrowser;
///
/// if webbrowser::open("http://github.com").is_ok() {
///     // ...
/// }
/// ```
#[cfg(not(target_arch = "wasm32"))]
pub fn open(url: &str) -> Result<Output> {
    open_browser(Browser::Default, url)
}

#[cfg(target_arch = "wasm32")]
pub fn open(url: &str) -> Result<()> {
    let window = web_sys::window();
    match window {
      Some(w) => {
        w.open_with_url(url);
        Ok(())
      },
      None => Err(std::io::Error::new(ErrorKind::Other, "should have a window in this context"))
    }
}

/// Opens the specified URL on the specific browser (if available) requested. Return semantics are
/// the same as for [open](fn.open.html).
///
/// # Examples
/// ```no_run
/// use webbrowser::{open_browser, Browser};
///
/// if open_browser(Browser::Firefox, "http://github.com").is_ok() {
///     // ...
/// }
/// ```
#[cfg(not(target_arch = "wasm32"))]
pub fn open_browser(browser: Browser, url: &str) -> Result<Output> {
    open_browser_internal(browser, url).and_then(|status| {
        if let Some(code) = status.code() {
            if code == 0 {
                Ok(Output {
                    status: ExitStatus::from_raw(0),
                    stdout: vec![],
                    stderr: vec![],
                })
            } else {
                Err(Error::new(
                    ErrorKind::Other,
                    format!("return code {}", code),
                ))
            }
        } else {
            Err(Error::new(ErrorKind::Other, "interrupted by signal"))
        }
    })
}

/// Deal with opening of browsers on Windows, using [`ShellExecuteW`](
/// https://docs.microsoft.com/en-us/windows/desktop/api/shellapi/nf-shellapi-shellexecutew)
/// fucntion.
#[cfg(target_os = "windows")]
#[inline]
fn open_browser_internal(browser: Browser, url: &str) -> Result<ExitStatus> {
    use winapi::shared::winerror::SUCCEEDED;
    use winapi::um::combaseapi::{CoInitializeEx, CoUninitialize};
    use winapi::um::objbase::{COINIT_APARTMENTTHREADED, COINIT_DISABLE_OLE1DDE};
    use winapi::um::shellapi::ShellExecuteW;
    use winapi::um::winuser::SW_SHOWNORMAL;
    match browser {
        Browser::Default => {
            static OPEN: &[u16] = &['o' as u16, 'p' as u16, 'e' as u16, 'n' as u16, 0x0000];
            let url =
                U16CString::from_str(url).map_err(|e| Error::new(ErrorKind::InvalidInput, e))?;
            let code = unsafe {
                let coinitializeex_result = CoInitializeEx(
                    ptr::null_mut(),
                    COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE,
                );
                let code = ShellExecuteW(
                    ptr::null_mut(),
                    OPEN.as_ptr(),
                    url.as_ptr(),
                    ptr::null(),
                    ptr::null(),
                    SW_SHOWNORMAL,
                ) as usize as i32;
                if SUCCEEDED(coinitializeex_result) {
                    CoUninitialize();
                }
                code
            };
            if code > 32 {
                Ok(ExitStatus::from_raw(0))
            } else {
                Err(Error::last_os_error())
            }
        }
        _ => Err(Error::new(
            ErrorKind::NotFound,
            "Only the default browser is supported on this platform right now",
        )),
    }
}

/// Deal with opening of browsers on Android
#[cfg(target_os = "android")]
#[inline]
fn open_browser_internal(browser: Browser, url: &str) -> Result<ExitStatus> {
    Command::new("am").arg("start").arg("--user").arg("0").arg("-a").arg("android.intent.action.VIEW").arg("-d").arg(url).status()
}

/// Deal with opening of browsers on Mac OS X, using `open` command
#[cfg(target_os = "macos")]
#[inline]
fn open_browser_internal(browser: Browser, url: &str) -> Result<ExitStatus> {
    let mut cmd = Command::new("open");
    match browser {
        Browser::Default => cmd.arg(url).status(),
        _ => {
            let app: Option<&str> = match browser {
                Browser::Firefox => Some("Firefox"),
                Browser::Chrome => Some("Google Chrome"),
                Browser::Opera => Some("Opera"),
                Browser::Safari => Some("Safari"),
                Browser::WebPositive => Some("WebPositive"),
                _ => None,
            };
            match app {
                Some(name) => cmd.arg("-a").arg(name).arg(url).status(),
                None => Err(Error::new(
                    ErrorKind::NotFound,
                    format!("Unsupported browser {:?}", browser),
                )),
            }
        }
    }
}

/// Deal with opening of browsers on Linux and *BSD - 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, open, respectively, whichever works
///    first
#[cfg(any(
    target_os = "linux",
    target_os = "freebsd",
    target_os = "netbsd",
    target_os = "openbsd",
    target_os = "haiku"
))]
#[inline]
fn open_browser_internal(browser: Browser, url: &str) -> Result<ExitStatus> {
    match browser {
        Browser::Default => open_on_unix_using_browser_env(url)
            .or_else(|_| -> Result<ExitStatus> { Command::new("xdg-open").arg(url).status() })
            .or_else(|r| -> Result<ExitStatus> {
                if let Ok(desktop) = ::std::env::var("XDG_CURRENT_DESKTOP") {
                    if desktop == "KDE" {
                        return Command::new("kioclient").arg("exec").arg(url).status();
                    }
                }
                Err(r) // If either `if` check fails, fall through to the next or_else
            })
            .or_else(|_| -> Result<ExitStatus> { Command::new("gvfs-open").arg(url).status() })
            .or_else(|_| -> Result<ExitStatus> { Command::new("gnome-open").arg(url).status() })
            .or_else(|_| -> Result<ExitStatus> { Command::new("open").arg(url).status() })
            .or_else(|_| -> Result<ExitStatus> {
                Command::new("kioclient").arg("exec").arg(url).status()
            })
            .or_else(|e| -> Result<ExitStatus> {
                if let Ok(_child) = Command::new("x-www-browser").arg(url).spawn() {
                    return Ok(ExitStatusExt::from_raw(0));
                }
                Err(e)
            }),
        _ => Err(Error::new(
            ErrorKind::NotFound,
            "Only the default browser is supported on this platform right now",
        )),
    }
}
#[cfg(any(
    target_os = "linux",
    target_os = "freebsd",
    target_os = "netbsd",
    target_os = "openbsd",
    target_os = "haiku"
))]
fn open_on_unix_using_browser_env(url: &str) -> Result<ExitStatus> {
    let browsers = ::std::env::var("BROWSER")
        .map_err(|_| -> Error { Error::new(ErrorKind::NotFound, "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
            // with ':' and %% with '%'
            let cmdline = browser
                .replace("%s", url)
                .replace("%c", ":")
                .replace("%%", "%");
            let cmdarr: Vec<&str> = cmdline.split_whitespace().collect();
            let mut cmd = Command::new(&cmdarr[0]);
            if cmdarr.len() > 1 {
                cmd.args(&cmdarr[1..cmdarr.len()]);
            }
            if !browser.contains("%s") {
                // append the url as an argument only if it was not already set via %s
                cmd.arg(url);
            }
            if let Ok(status) = cmd.status() {
                return Ok(status);
            }
        }
    }
    Err(Error::new(
        ErrorKind::NotFound,
        "No valid command in $BROWSER",
    ))
}

#[cfg(not(any(
    target_os = "android",
    target_os = "windows",
    target_os = "macos",
    target_os = "linux",
    target_os = "freebsd",
    target_os = "netbsd",
    target_os = "openbsd",
    target_os = "haiku",
    target_arch = "wasm32"
)))]
compile_error!("Only Windows, Mac OS, Linux, *BSD and Haiku and Wasm32 are currently supported");

#[test]
fn test_open_default() {
    assert!(open("http://github.com").is_ok());
    assert!(open("http://github.com?dummy_query1=0&dummy_query2=ｎｏｎａｓｃｉｉ").is_ok());
}

#[test]
#[ignore]
fn test_open_firefox() {
    assert!(open_browser(Browser::Firefox, "http://github.com").is_ok());
}

#[test]
#[ignore]
fn test_open_chrome() {
    assert!(open_browser(Browser::Chrome, "http://github.com").is_ok());
}

#[test]
#[ignore]
#[cfg(target_os = "windows")]
fn test_open_internet_explorer() {
    assert!(open_browser(Browser::InternetExplorer, "http://github.com").is_ok());
}

#[test]
#[cfg(target_arch = "wasm32")]
fn test_open_default_wasm() {
    assert!(open("http://github.com").is_ok());
}

#[test]
#[ignore]
fn test_open_safari() {
    assert!(open_browser(Browser::Safari, "http://github.com").is_ok());
}

#[test]
#[ignore]
fn test_open_webpositive() {
    assert!(open_browser(Browser::WebPositive, "http://github.com").is_ok());
}
<commit_msg>Fix fmt<commit_after>//! Open URLs in the web browsers available on a platform.
//!
//! Inspired by the [webbrowser](https://docs.python.org/2/library/webbrowser.html) python library.
//!
//! Currently state of platform support is:
//!
//! * macos => default, as well as browsers listed under [Browser](enum.Browser.html)
//! * windows => default browser only
//! * linux or *bsd => default browser only (uses $BROWSER env var, failing back to xdg-open, gvfs-open and
//! gnome-open, in that order)
//! * android => not supported right now
//! * ios => not supported right now
//!
//! Important note:
//!
//! * This library requires availability of browsers and a graphical environment during runtime
//! * `cargo test` will actually open the browser locally.
//!
//! # Examples
//!
//! ```no_run
//! use webbrowser;
//!
//! if webbrowser::open("http://github.com").is_ok() {
//!     // ...
//! }
//! ```

#[cfg(windows)]
extern crate widestring;
#[cfg(windows)]
extern crate winapi;

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

#[cfg(windows)]
use std::os::windows::process::ExitStatusExt;
#[cfg(windows)]
use std::ptr;

#[cfg(unix)]
use std::os::unix::process::ExitStatusExt;

#[cfg(not(windows))]
use std::process::Command;

#[cfg(windows)]
use widestring::U16CString;

#[cfg(target_arch = "wasm32")]
use web_sys::Window;

#[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,

    ///Haiku's WebPositive
    WebPositive,
}

///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"),
            Browser::WebPositive => f.write_str("WebPositive"),
        }
    }
}

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),
            "webpositive" => Ok(Browser::WebPositive),
            _ => 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.
///
/// Equivalent to:
/// ```no_run
/// # use webbrowser::{Browser, open_browser};
/// # let url = "http://example.com";
/// open_browser(Browser::Default, url);
/// ```
///
/// # Examples
/// ```no_run
/// use webbrowser;
///
/// if webbrowser::open("http://github.com").is_ok() {
///     // ...
/// }
/// ```
#[cfg(not(target_arch = "wasm32"))]
pub fn open(url: &str) -> Result<Output> {
    open_browser(Browser::Default, url)
}

#[cfg(target_arch = "wasm32")]
pub fn open(url: &str) -> Result<()> {
    let window = web_sys::window();
    match window {
        Some(w) => {
            w.open_with_url(url);
            Ok(())
        }
        None => Err(std::io::Error::new(
            ErrorKind::Other,
            "should have a window in this context",
        )),
    }
}

/// Opens the specified URL on the specific browser (if available) requested. Return semantics are
/// the same as for [open](fn.open.html).
///
/// # Examples
/// ```no_run
/// use webbrowser::{open_browser, Browser};
///
/// if open_browser(Browser::Firefox, "http://github.com").is_ok() {
///     // ...
/// }
/// ```
#[cfg(not(target_arch = "wasm32"))]
pub fn open_browser(browser: Browser, url: &str) -> Result<Output> {
    open_browser_internal(browser, url).and_then(|status| {
        if let Some(code) = status.code() {
            if code == 0 {
                Ok(Output {
                    status: ExitStatus::from_raw(0),
                    stdout: vec![],
                    stderr: vec![],
                })
            } else {
                Err(Error::new(
                    ErrorKind::Other,
                    format!("return code {}", code),
                ))
            }
        } else {
            Err(Error::new(ErrorKind::Other, "interrupted by signal"))
        }
    })
}

/// Deal with opening of browsers on Windows, using [`ShellExecuteW`](
/// https://docs.microsoft.com/en-us/windows/desktop/api/shellapi/nf-shellapi-shellexecutew)
/// fucntion.
#[cfg(target_os = "windows")]
#[inline]
fn open_browser_internal(browser: Browser, url: &str) -> Result<ExitStatus> {
    use winapi::shared::winerror::SUCCEEDED;
    use winapi::um::combaseapi::{CoInitializeEx, CoUninitialize};
    use winapi::um::objbase::{COINIT_APARTMENTTHREADED, COINIT_DISABLE_OLE1DDE};
    use winapi::um::shellapi::ShellExecuteW;
    use winapi::um::winuser::SW_SHOWNORMAL;
    match browser {
        Browser::Default => {
            static OPEN: &[u16] = &['o' as u16, 'p' as u16, 'e' as u16, 'n' as u16, 0x0000];
            let url =
                U16CString::from_str(url).map_err(|e| Error::new(ErrorKind::InvalidInput, e))?;
            let code = unsafe {
                let coinitializeex_result = CoInitializeEx(
                    ptr::null_mut(),
                    COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE,
                );
                let code = ShellExecuteW(
                    ptr::null_mut(),
                    OPEN.as_ptr(),
                    url.as_ptr(),
                    ptr::null(),
                    ptr::null(),
                    SW_SHOWNORMAL,
                ) as usize as i32;
                if SUCCEEDED(coinitializeex_result) {
                    CoUninitialize();
                }
                code
            };
            if code > 32 {
                Ok(ExitStatus::from_raw(0))
            } else {
                Err(Error::last_os_error())
            }
        }
        _ => Err(Error::new(
            ErrorKind::NotFound,
            "Only the default browser is supported on this platform right now",
        )),
    }
}

/// Deal with opening of browsers on Android
#[cfg(target_os = "android")]
#[inline]
fn open_browser_internal(browser: Browser, url: &str) -> Result<ExitStatus> {
    Command::new("am")
        .arg("start")
        .arg("--user")
        .arg("0")
        .arg("-a")
        .arg("android.intent.action.VIEW")
        .arg("-d")
        .arg(url)
        .status()
}

/// Deal with opening of browsers on Mac OS X, using `open` command
#[cfg(target_os = "macos")]
#[inline]
fn open_browser_internal(browser: Browser, url: &str) -> Result<ExitStatus> {
    let mut cmd = Command::new("open");
    match browser {
        Browser::Default => cmd.arg(url).status(),
        _ => {
            let app: Option<&str> = match browser {
                Browser::Firefox => Some("Firefox"),
                Browser::Chrome => Some("Google Chrome"),
                Browser::Opera => Some("Opera"),
                Browser::Safari => Some("Safari"),
                Browser::WebPositive => Some("WebPositive"),
                _ => None,
            };
            match app {
                Some(name) => cmd.arg("-a").arg(name).arg(url).status(),
                None => Err(Error::new(
                    ErrorKind::NotFound,
                    format!("Unsupported browser {:?}", browser),
                )),
            }
        }
    }
}

/// Deal with opening of browsers on Linux and *BSD - 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, open, respectively, whichever works
///    first
#[cfg(any(
    target_os = "linux",
    target_os = "freebsd",
    target_os = "netbsd",
    target_os = "openbsd",
    target_os = "haiku"
))]
#[inline]
fn open_browser_internal(browser: Browser, url: &str) -> Result<ExitStatus> {
    match browser {
        Browser::Default => open_on_unix_using_browser_env(url)
            .or_else(|_| -> Result<ExitStatus> { Command::new("xdg-open").arg(url).status() })
            .or_else(|r| -> Result<ExitStatus> {
                if let Ok(desktop) = ::std::env::var("XDG_CURRENT_DESKTOP") {
                    if desktop == "KDE" {
                        return Command::new("kioclient").arg("exec").arg(url).status();
                    }
                }
                Err(r) // If either `if` check fails, fall through to the next or_else
            })
            .or_else(|_| -> Result<ExitStatus> { Command::new("gvfs-open").arg(url).status() })
            .or_else(|_| -> Result<ExitStatus> { Command::new("gnome-open").arg(url).status() })
            .or_else(|_| -> Result<ExitStatus> { Command::new("open").arg(url).status() })
            .or_else(|_| -> Result<ExitStatus> {
                Command::new("kioclient").arg("exec").arg(url).status()
            })
            .or_else(|e| -> Result<ExitStatus> {
                if let Ok(_child) = Command::new("x-www-browser").arg(url).spawn() {
                    return Ok(ExitStatusExt::from_raw(0));
                }
                Err(e)
            }),
        _ => Err(Error::new(
            ErrorKind::NotFound,
            "Only the default browser is supported on this platform right now",
        )),
    }
}
#[cfg(any(
    target_os = "linux",
    target_os = "freebsd",
    target_os = "netbsd",
    target_os = "openbsd",
    target_os = "haiku"
))]
fn open_on_unix_using_browser_env(url: &str) -> Result<ExitStatus> {
    let browsers = ::std::env::var("BROWSER")
        .map_err(|_| -> Error { Error::new(ErrorKind::NotFound, "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
            // with ':' and %% with '%'
            let cmdline = browser
                .replace("%s", url)
                .replace("%c", ":")
                .replace("%%", "%");
            let cmdarr: Vec<&str> = cmdline.split_whitespace().collect();
            let mut cmd = Command::new(&cmdarr[0]);
            if cmdarr.len() > 1 {
                cmd.args(&cmdarr[1..cmdarr.len()]);
            }
            if !browser.contains("%s") {
                // append the url as an argument only if it was not already set via %s
                cmd.arg(url);
            }
            if let Ok(status) = cmd.status() {
                return Ok(status);
            }
        }
    }
    Err(Error::new(
        ErrorKind::NotFound,
        "No valid command in $BROWSER",
    ))
}

#[cfg(not(any(
    target_os = "android",
    target_os = "windows",
    target_os = "macos",
    target_os = "linux",
    target_os = "freebsd",
    target_os = "netbsd",
    target_os = "openbsd",
    target_os = "haiku",
    target_arch = "wasm32"
)))]
compile_error!("Only Windows, Mac OS, Linux, *BSD and Haiku and Wasm32 are currently supported");

#[test]
fn test_open_default() {
    assert!(open("http://github.com").is_ok());
    assert!(open("http://github.com?dummy_query1=0&dummy_query2=ｎｏｎａｓｃｉｉ").is_ok());
}

#[test]
#[ignore]
fn test_open_firefox() {
    assert!(open_browser(Browser::Firefox, "http://github.com").is_ok());
}

#[test]
#[ignore]
fn test_open_chrome() {
    assert!(open_browser(Browser::Chrome, "http://github.com").is_ok());
}

#[test]
#[ignore]
#[cfg(target_os = "windows")]
fn test_open_internet_explorer() {
    assert!(open_browser(Browser::InternetExplorer, "http://github.com").is_ok());
}

#[test]
#[cfg(target_arch = "wasm32")]
fn test_open_default_wasm() {
    assert!(open("http://github.com").is_ok());
}

#[test]
#[ignore]
fn test_open_safari() {
    assert!(open_browser(Browser::Safari, "http://github.com").is_ok());
}

#[test]
#[ignore]
fn test_open_webpositive() {
    assert!(open_browser(Browser::WebPositive, "http://github.com").is_ok());
}
<|endoftext|>


<commit_before>//! This crate provides an API for talking to repositories that implement The Update Framework
//! (TUF).
//!
//! If you are unfamiliar with TUF, you should read up on it via the [official
//! website](http://theupdateframework.github.io/). This crate aims to implement the entirety of
//! the specification as defined at the [head of the `develop`
//! branch](https://github.com/theupdateframework/tuf/blob/develop/docs/tuf-spec.txt) in the
//! official TUF git repository.
//!
//! Additionally, the following two papers are valuable supplements in understanding how to
//! actually implement TUF for a community repository.
//!
//! - [The Diplomat paper
//! (2016)](https://www.usenix.org/conference/nsdi16/technical-sessions/presentation/kuppusamy)
//! - [The Mercury paper
//! (2017)](https://www.usenix.org/conference/atc17/technical-sessions/presentation/kuppusamy)
//!
//! Failure to read the spec and the above papers will likely lead to an implementation that does
//! not take advantage of all the security guarantees that TUF offers.
//!
//! # Interoperability
//!
//! It should be noted that historically the TUF spec defined exactly one metadata format and one
//! way of organizing metadata within a repository. Thus, all TUF implementation could perfectly
//! interoperate. The TUF spec has moved to describing *how a framework should behave* leaving many
//! of the detais up to the implementor. Therefore, there are **zero** guarantees that this library
//! will work with any other TUF implementation. Should you want to access a TUF repository that
//! uses `rust-tuf` as its backend from another language, ASN.1 modules and metadata schemas are
//! provided that will allow you to interoperate with this library.
//!
//! # Implementation Considerations
//!
//! ## Key Management
//!
//! Part of TUF is that it acts as its own PKI, and there is no integration that needs to be done
//! for managing keys.
//!
//! Note: No two private keys that are generated should ever exist on the same hardware. When a
//! step says "generate `N` keys," the implication is that these `N` keys are generated on `N`
//! devices.
//!
//! The first set of keys that need to be generated at the root keys that are used to sign the root
//! metadata. The root should be defined with the following properties:
//!
//! - Minimum:
//!   - 3 keys
//!   - threshold of 2
//! - Recommended:
//!   - 5 keys
//!   - threshold of 3
//!
//! If a threshold of root keys are compromised, then the entire system is compromised and TUF
//! clients will need to be manually updated. Similarly, if some `X` keys are lost such that the
//! threshold `N` cannot be reached, then clients will also need to be manually updated. Both of
//! situations are considered critically unsafe. Whatever number of keys are used, it should be
//! assumed that some small number may be lost or compromised.
//!
//! These root keys **MUST** be kept offline on secure media.
//!
//! ## Delegations
//!
//! TUF's most useful feature is the ability to delegate certain roles to sign certain targets.
//! This is discussed in extensive detail in the aforementioned Diplomat paper. There are three
//! problems faced when delegating trust in TUF:
//!
//! 1. What to do for existent accounts that have not yet created and signed TUF metadata
//! 2. What to do when a new account registers
//! 3. What to do when an account uploads a new target and new metadata
//!
//! There are several approaches for dealing with the above scenarios. We are only going to discuss
//! on here as it is the recommended approach. This approach is taken directly from Section 6.1 of
//! the Diplomat paper
//!
//! ### Maximum Security Model
//!
//! The top-level targets role delegates to three other roles and are listed in the following order:
//!
//! 1. `claimed-projects`
//!   - Terminating
//!   - Delegates to project-specific roles that have registered keys with TUF
//! 2. `rarely-updated-projects`
//!   - Terminating
//!   - Signs all packages for all projects that have been "abandoned" or left unupdated for a long
//!   time AND have not yet registered keys with TUF
//! 3. `new-projects`
//!   - Non-terminating
//!   - Signs all packages for all new projects as well as projects that were relegated to
//!   `rarely-updated-projects`
//!
//! The top-level `targets` role as well as `claimed-projects` and `rarely-updated-projects`
//! **MUST** all use offline keys.
//!
//! The critical, manual step is to register new projects with TUF keys and move them into the
//! `claimed-projects` role. Projects that refuse to register keys should have their packages
//! periodically moved into the `rarely-updated-projects` role. Projects in either of these two
//! roles are safe from compromise as their keys are offline. Since the keys used for the above
//! operation are kept offline, this is periodic, manual process.
//!
//! ## Snapshot & Timestamp
//!
//! In a community repository, these two keys need to be kept online and will be used to sign new
//! metadata on every update.

#![deny(missing_docs)]

extern crate chrono;
extern crate data_encoding;
extern crate derp;
extern crate hyper;
extern crate itoa;
#[cfg(test)]
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate log;
#[cfg(test)]
#[macro_use]
extern crate maplit;
extern crate ring;
extern crate serde;
#[macro_use]
extern crate serde_derive;

#[cfg(not(test))]
extern crate serde_json as json;
#[cfg(test)]
#[macro_use]
extern crate serde_json as json;

#[cfg(test)]
extern crate tempdir;
extern crate tempfile;
extern crate untrusted;

pub mod error;

/// Alias for `Result<T, Error>`.
pub type Result<T> = ::std::result::Result<T, Error>;

pub mod client;
pub mod crypto;
pub mod interchange;
pub mod metadata;
pub mod repository;
mod shims;
pub mod tuf;
mod util;

pub use error::*;
pub use tuf::*;
pub use util::*;
<commit_msg>clippy: ignore remaining lints<commit_after>//! This crate provides an API for talking to repositories that implement The Update Framework
//! (TUF).
//!
//! If you are unfamiliar with TUF, you should read up on it via the [official
//! website](http://theupdateframework.github.io/). This crate aims to implement the entirety of
//! the specification as defined at the [head of the `develop`
//! branch](https://github.com/theupdateframework/tuf/blob/develop/docs/tuf-spec.txt) in the
//! official TUF git repository.
//!
//! Additionally, the following two papers are valuable supplements in understanding how to
//! actually implement TUF for a community repository.
//!
//! - [The Diplomat paper
//! (2016)](https://www.usenix.org/conference/nsdi16/technical-sessions/presentation/kuppusamy)
//! - [The Mercury paper
//! (2017)](https://www.usenix.org/conference/atc17/technical-sessions/presentation/kuppusamy)
//!
//! Failure to read the spec and the above papers will likely lead to an implementation that does
//! not take advantage of all the security guarantees that TUF offers.
//!
//! # Interoperability
//!
//! It should be noted that historically the TUF spec defined exactly one metadata format and one
//! way of organizing metadata within a repository. Thus, all TUF implementation could perfectly
//! interoperate. The TUF spec has moved to describing *how a framework should behave* leaving many
//! of the detais up to the implementor. Therefore, there are **zero** guarantees that this library
//! will work with any other TUF implementation. Should you want to access a TUF repository that
//! uses `rust-tuf` as its backend from another language, ASN.1 modules and metadata schemas are
//! provided that will allow you to interoperate with this library.
//!
//! # Implementation Considerations
//!
//! ## Key Management
//!
//! Part of TUF is that it acts as its own PKI, and there is no integration that needs to be done
//! for managing keys.
//!
//! Note: No two private keys that are generated should ever exist on the same hardware. When a
//! step says "generate `N` keys," the implication is that these `N` keys are generated on `N`
//! devices.
//!
//! The first set of keys that need to be generated at the root keys that are used to sign the root
//! metadata. The root should be defined with the following properties:
//!
//! - Minimum:
//!   - 3 keys
//!   - threshold of 2
//! - Recommended:
//!   - 5 keys
//!   - threshold of 3
//!
//! If a threshold of root keys are compromised, then the entire system is compromised and TUF
//! clients will need to be manually updated. Similarly, if some `X` keys are lost such that the
//! threshold `N` cannot be reached, then clients will also need to be manually updated. Both of
//! situations are considered critically unsafe. Whatever number of keys are used, it should be
//! assumed that some small number may be lost or compromised.
//!
//! These root keys **MUST** be kept offline on secure media.
//!
//! ## Delegations
//!
//! TUF's most useful feature is the ability to delegate certain roles to sign certain targets.
//! This is discussed in extensive detail in the aforementioned Diplomat paper. There are three
//! problems faced when delegating trust in TUF:
//!
//! 1. What to do for existent accounts that have not yet created and signed TUF metadata
//! 2. What to do when a new account registers
//! 3. What to do when an account uploads a new target and new metadata
//!
//! There are several approaches for dealing with the above scenarios. We are only going to discuss
//! on here as it is the recommended approach. This approach is taken directly from Section 6.1 of
//! the Diplomat paper
//!
//! ### Maximum Security Model
//!
//! The top-level targets role delegates to three other roles and are listed in the following order:
//!
//! 1. `claimed-projects`
//!   - Terminating
//!   - Delegates to project-specific roles that have registered keys with TUF
//! 2. `rarely-updated-projects`
//!   - Terminating
//!   - Signs all packages for all projects that have been "abandoned" or left unupdated for a long
//!   time AND have not yet registered keys with TUF
//! 3. `new-projects`
//!   - Non-terminating
//!   - Signs all packages for all new projects as well as projects that were relegated to
//!   `rarely-updated-projects`
//!
//! The top-level `targets` role as well as `claimed-projects` and `rarely-updated-projects`
//! **MUST** all use offline keys.
//!
//! The critical, manual step is to register new projects with TUF keys and move them into the
//! `claimed-projects` role. Projects that refuse to register keys should have their packages
//! periodically moved into the `rarely-updated-projects` role. Projects in either of these two
//! roles are safe from compromise as their keys are offline. Since the keys used for the above
//! operation are kept offline, this is periodic, manual process.
//!
//! ## Snapshot & Timestamp
//!
//! In a community repository, these two keys need to be kept online and will be used to sign new
//! metadata on every update.

#![deny(missing_docs)]

#![cfg_attr(
    feature = "cargo-clippy",
    allow(
        collapsible_if,
        implicit_hasher,
        new_ret_no_self,
        op_ref,
        too_many_arguments,
    )
)]

extern crate chrono;
extern crate data_encoding;
extern crate derp;
extern crate hyper;
extern crate itoa;
#[cfg(test)]
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate log;
#[cfg(test)]
#[macro_use]
extern crate maplit;
extern crate ring;
extern crate serde;
#[macro_use]
extern crate serde_derive;

#[cfg(not(test))]
extern crate serde_json as json;
#[cfg(test)]
#[macro_use]
extern crate serde_json as json;

#[cfg(test)]
extern crate tempdir;
extern crate tempfile;
extern crate untrusted;

pub mod error;

/// Alias for `Result<T, Error>`.
pub type Result<T> = ::std::result::Result<T, Error>;

pub mod client;
pub mod crypto;
pub mod interchange;
pub mod metadata;
pub mod repository;
mod shims;
pub mod tuf;
mod util;

pub use error::*;
pub use tuf::*;
pub use util::*;
<|endoftext|>


<commit_before>#![no_std]

#[cfg(test)]
extern crate std;

// Re-export libcore using an alias so that the macros can work in no_std
// crates while remaining compatible with normal crates.
#[allow(private_in_public)]
#[doc(hidden)]
pub use core as __core;

mod lazy_static;

/** This macro can be used to ensure that a function is called only once. It panics if the function
is called a second time.

# Example

Using the macro:

```rust
#[macro_use]
extern crate once;

fn init() {
    assert_has_not_been_called!();

    // code that should only run once
}

fn main() {
    init();
    // init(); // "assertion failed: called == false"
}
```

Custom error message:

```rust
#[macro_use]
extern crate once;

fn init() {
    assert_has_not_been_called!("the init function must only be called {}", "once");
}

fn main() {
    init();
    // init(); // "the init function must only be called once"
}
```
**/
#[macro_export]
macro_rules! assert_has_not_been_called {
    () => {
        assert_has_not_been_called!("assertion failed: has_run == false");
    };
    ($($arg:tt)+) => {{
        fn assert_has_not_been_called() {
            use $crate::__core::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering};
            static CALLED: AtomicBool = ATOMIC_BOOL_INIT;
            let called = CALLED.swap(true, Ordering::Relaxed);
            assert!(called == false, $($arg)+);
        }
        assert_has_not_been_called();
    }};
}

#[test]
fn test_run_once() {
    fn init() {
        assert_has_not_been_called!();
    }
    init();
}

#[test]
fn test_run_once_different_fns() {
    fn init1() {
        assert_has_not_been_called!();
    }
    fn init2() {
        assert_has_not_been_called!();
    }
    init1();
    init2();
}

#[test]
#[should_panic]
fn test_run_twice() {
    fn init() {
        assert_has_not_been_called!();
    }
    init();
    init();
}

#[test]
fn test_hygiene1() {
    fn init() {
        assert_has_not_been_called!();

        #[allow(dead_code)]
        fn assert_has_not_been_called() {}
    }
    init();
}

#[test]
fn test_hygiene2() {
    fn init() {
        assert_has_not_been_called!();

        #[allow(dead_code)]
        static HAS_RUN: bool = false;
    }
    init();
}
<commit_msg>Fix hygiene test<commit_after>#![no_std]

#[cfg(test)]
extern crate std;

// Re-export libcore using an alias so that the macros can work in no_std
// crates while remaining compatible with normal crates.
#[allow(private_in_public)]
#[doc(hidden)]
pub use core as __core;

mod lazy_static;

/** This macro can be used to ensure that a function is called only once. It panics if the function
is called a second time.

# Example

Using the macro:

```rust
#[macro_use]
extern crate once;

fn init() {
    assert_has_not_been_called!();

    // code that should only run once
}

fn main() {
    init();
    // init(); // "assertion failed: called == false"
}
```

Custom error message:

```rust
#[macro_use]
extern crate once;

fn init() {
    assert_has_not_been_called!("the init function must only be called {}", "once");
}

fn main() {
    init();
    // init(); // "the init function must only be called once"
}
```
**/
#[macro_export]
macro_rules! assert_has_not_been_called {
    () => {
        assert_has_not_been_called!("assertion failed: has_run == false");
    };
    ($($arg:tt)+) => {{
        fn assert_has_not_been_called() {
            use $crate::__core::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering};
            static CALLED: AtomicBool = ATOMIC_BOOL_INIT;
            let called = CALLED.swap(true, Ordering::Relaxed);
            assert!(called == false, $($arg)+);
        }
        assert_has_not_been_called();
    }};
}

#[test]
fn test_run_once() {
    fn init() {
        assert_has_not_been_called!();
    }
    init();
}

#[test]
fn test_run_once_different_fns() {
    fn init1() {
        assert_has_not_been_called!();
    }
    fn init2() {
        assert_has_not_been_called!();
    }
    init1();
    init2();
}

#[test]
#[should_panic]
fn test_run_twice() {
    fn init() {
        assert_has_not_been_called!();
    }
    init();
    init();
}

#[test]
fn test_hygiene1() {
    fn init() {
        assert_has_not_been_called!();

        #[allow(dead_code)]
        fn assert_has_not_been_called() {}
    }
    init();
}

#[test]
fn test_hygiene2() {
    fn init() {
        assert_has_not_been_called!();

        #[allow(dead_code)]
        static CALLED: i32 = 42;
    }
    init();
}
<|endoftext|>


<commit_before>// Copyright 2016 The android_logger Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

//! A logger which writes to android output.
//!
//! ## Example
//!
//! ```ignore
//! #[macro_use] extern crate log;
//! extern crate android_logger;
//!
//! use log::LogLevel;
//!
//! fn native_activity_create() {
//!     android_logger::init_once(LogLevel::Trace);
//!
//!     debug!("this is a debug {}", "message");
//!     error!("this is printed by default");
//!
//!     if log_enabled!(LogLevel::Info) {
//!         let x = 3 * 4; // expensive computation
//!         info!("the answer was: {}", x);
//!     }
//! }
//! ```

extern crate android_log_sys as log_ffi;
#[macro_use] extern crate log;

use log_ffi::LogPriority;
use log::{Log,LogLevel,LogMetadata,LogRecord};
use std::ffi::CStr;
use std::mem;
use std::fmt;
use std::ptr;

/// Output log to android system.
fn android_log(prio: log_ffi::LogPriority, tag: &CStr, msg: &CStr) {
    unsafe { log_ffi::__android_log_write(prio as log_ffi::c_int, tag.as_ptr() as *const log_ffi::c_char, msg.as_ptr() as *const log_ffi::c_char) };
}

pub struct PlatformLogger;

const LOGGING_TAG_MAX_LEN: usize = 23;
const LOGGING_MSG_MAX_LEN: usize = 4000;

impl Log for PlatformLogger {
    fn enabled(&self, _: &LogMetadata) -> bool {
        true
    }

    fn log(&self, record: &LogRecord) {
        // tag must not exceed LOGGING_TAG_MAX_LEN
        let mut tag_bytes: [u8; LOGGING_TAG_MAX_LEN + 1] = unsafe { mem::uninitialized() };
        // truncate the tag here to fit into LOGGING_TAG_MAX_LEN
        self.fill_tag_bytes(&mut tag_bytes, record);
        // use stack array as C string
        let tag: &CStr = unsafe { CStr::from_ptr(mem::transmute(tag_bytes.as_ptr())) };

        // message must not exceed LOGGING_MSG_MAX_LEN
        // therefore split log message into multiple log calls
        let mut writer = PlatformLogWriter::new(
            record.level(),
            tag
        );

        // use PlatformLogWriter to output chunks if they exceed max size
        let _ = fmt::write(&mut writer, *record.args());

        // output the remaining message (this would usually be the most common case)
        writer.flush();
    }
}

impl PlatformLogger {
    fn fill_tag_bytes(&self, array: &mut [u8], record: &LogRecord) {
        let tag_bytes_iter = record.location().module_path().bytes();
        if tag_bytes_iter.len() > LOGGING_TAG_MAX_LEN {
            for (input, output) in tag_bytes_iter
                .take(LOGGING_TAG_MAX_LEN - 2)
                .chain(b"..\0".iter().cloned())
                .zip(array.iter_mut())
            {
                *output = input;
            }
        } else {
            for (input, output) in tag_bytes_iter
                .chain(b"\0".iter().cloned())
                .zip(array.iter_mut())
            {
                *output = input;
            }
        }
    }
}

struct PlatformLogWriter<'a> {
    priority: LogPriority,
    len: usize,
    last_newline_index: usize,
    tag: &'a CStr,
    buffer: [u8; LOGGING_MSG_MAX_LEN + 1],
}

impl<'a> PlatformLogWriter<'a> {
    pub fn new<'r>(level: LogLevel, tag: &'r CStr) -> PlatformLogWriter<'r> {
        PlatformLogWriter {
            priority: match level {
                LogLevel::Warn => LogPriority::WARN,
                LogLevel::Info => LogPriority::INFO,
                LogLevel::Debug => LogPriority::DEBUG,
                LogLevel::Error => LogPriority::ERROR,
                LogLevel::Trace => LogPriority::VERBOSE,
            },
            len: 0,
            last_newline_index: 0,
            tag: tag,
            buffer: unsafe { mem::uninitialized() },
        }
    }

    /// Flush some bytes to android logger.
    ///
    /// If there is a newline, flush up to it.
    /// If ther was no newline, flush all.
    ///
    /// Not guaranteed to flush everything.
    fn temporal_flush(&mut self) {
        let total_len = self.len;

        if total_len == 0 {
            return;
        }

        if self.last_newline_index > 0 {
            let copy_from_index = self.last_newline_index;
            let remaining_chunk_len = total_len - copy_from_index;

            self.output_specified_len(copy_from_index);
            self.copy_bytes_to_start(copy_from_index, remaining_chunk_len);
            self.len = remaining_chunk_len;
        } else {
            self.output_specified_len(total_len);
            self.len = 0;
        }
        self.last_newline_index = 0;
    }

    /// Flush everything remaining to android logger.
    fn flush(&mut self) {
        let total_len = self.len;

        if total_len == 0 {
            return;
        }

        self.output_specified_len(total_len);
        self.len = 0;
        self.last_newline_index = 0;
    }

    /// Output buffer up until the \0 which will be placed at `len` position.
    fn output_specified_len(&mut self, len: usize) {
        let mut last_byte: u8 = b'\0';
        mem::swap(&mut last_byte, unsafe { self.buffer.get_unchecked_mut(len) });

        let msg: &CStr = unsafe { CStr::from_ptr(mem::transmute(self.buffer.as_ptr())) };
        android_log(self.priority, self.tag, msg);

        *unsafe { self.buffer.get_unchecked_mut(len) } = last_byte;
    }

    /// Copy `len` bytes from `index` position to starting position.
    fn copy_bytes_to_start(&mut self, index: usize, len: usize) {
        let src = unsafe { self.buffer.as_ptr().offset(index as isize) };
        let dst = self.buffer.as_mut_ptr();
        unsafe { ptr::copy(src, dst, len) };
    }
}

impl<'a> fmt::Write for PlatformLogWriter<'a> {
    fn write_str(&mut self, s: &str) -> fmt::Result {
        let mut incomming_bytes = s.as_bytes();

        while incomming_bytes.len() > 0 {
            let len = self.len;

            // write everything possible to buffer and mark last \n
            let new_len = len + incomming_bytes.len();
            let last_newline = self.buffer[len..LOGGING_MSG_MAX_LEN].iter_mut()
                .zip(incomming_bytes)
                .enumerate()
                .fold(None, |acc, (i, (output, input))| {
                    *output = *input;
                    if *input == b'\n' { Some(i) } else { acc }
                });

            // update last \n index
            if let Some(newline) = last_newline {
                self.last_newline_index = len + newline;
            }

            // calculate how many bytes were written
            let written_len = if new_len <= LOGGING_MSG_MAX_LEN {
                // if the len was not exceeded
                self.len = new_len;
                new_len - len // written len
            } else {
                // if new length was exceeded
                self.len = LOGGING_MSG_MAX_LEN;
                self.temporal_flush();

                LOGGING_MSG_MAX_LEN - len // written len
            };

            incomming_bytes = &incomming_bytes[written_len..];
        }

        Ok(())
    }
}

/// Initializes the global logger with an android logger.
///
/// This can be called many times, but will only initialize logging once,
/// and will not replace any other previously initialized logger.
///
/// It is ok to call this at the activity creation, and it will be
/// repeatedly called on every lifecycle restart (i.e. screen rotation).
pub fn init_once(log_level: LogLevel) {
    match log::set_logger(|max_log_level| {
        max_log_level.set(log_level.to_log_level_filter());
        return Box::new(PlatformLogger);
    }) {
        Err(e) => debug!("{}", e),
        _ => (),
    }
}
<commit_msg>Rename PlatformLogger to AndroidLogger, add some docs.<commit_after>// Copyright 2016 The android_logger Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

//! A logger which writes to android output.
//!
//! ## Example
//!
//! ```ignore
//! #[macro_use] extern crate log;
//! extern crate android_logger;
//!
//! use log::LogLevel;
//!
//! fn native_activity_create() {
//!     android_logger::init_once(LogLevel::Trace);
//!
//!     debug!("this is a debug {}", "message");
//!     error!("this is printed by default");
//!
//!     if log_enabled!(LogLevel::Info) {
//!         let x = 3 * 4; // expensive computation
//!         info!("the answer was: {}", x);
//!     }
//! }
//! ```

extern crate android_log_sys as log_ffi;
#[macro_use] extern crate log;

use log_ffi::LogPriority;
use log::{Log,LogLevel,LogMetadata,LogRecord};
use std::ffi::CStr;
use std::mem;
use std::fmt;
use std::ptr;

/// Output log to android system.
fn android_log(prio: log_ffi::LogPriority, tag: &CStr, msg: &CStr) {
    unsafe { log_ffi::__android_log_write(prio as log_ffi::c_int, tag.as_ptr() as *const log_ffi::c_char, msg.as_ptr() as *const log_ffi::c_char) };
}

/// Underlying android logger, for cases where `init_once` abstraction is not enough.
pub struct AndroidLogger;

const LOGGING_TAG_MAX_LEN: usize = 23;
const LOGGING_MSG_MAX_LEN: usize = 4000;

impl AndroidLogger {
    pub fn new() -> AndroidLogger {
        AndroidLogger
    }
}

impl Log for AndroidLogger {
    fn enabled(&self, _: &LogMetadata) -> bool {
        true
    }

    fn log(&self, record: &LogRecord) {
        // tag must not exceed LOGGING_TAG_MAX_LEN
        let mut tag_bytes: [u8; LOGGING_TAG_MAX_LEN + 1] = unsafe { mem::uninitialized() };
        // truncate the tag here to fit into LOGGING_TAG_MAX_LEN
        self.fill_tag_bytes(&mut tag_bytes, record);
        // use stack array as C string
        let tag: &CStr = unsafe { CStr::from_ptr(mem::transmute(tag_bytes.as_ptr())) };

        // message must not exceed LOGGING_MSG_MAX_LEN
        // therefore split log message into multiple log calls
        let mut writer = PlatformLogWriter::new(
            record.level(),
            tag
        );

        // use PlatformLogWriter to output chunks if they exceed max size
        let _ = fmt::write(&mut writer, *record.args());

        // output the remaining message (this would usually be the most common case)
        writer.flush();
    }
}

impl AndroidLogger {
    fn fill_tag_bytes(&self, array: &mut [u8], record: &LogRecord) {
        let tag_bytes_iter = record.location().module_path().bytes();
        if tag_bytes_iter.len() > LOGGING_TAG_MAX_LEN {
            for (input, output) in tag_bytes_iter
                .take(LOGGING_TAG_MAX_LEN - 2)
                .chain(b"..\0".iter().cloned())
                .zip(array.iter_mut())
            {
                *output = input;
            }
        } else {
            for (input, output) in tag_bytes_iter
                .chain(b"\0".iter().cloned())
                .zip(array.iter_mut())
            {
                *output = input;
            }
        }
    }
}

struct PlatformLogWriter<'a> {
    priority: LogPriority,
    len: usize,
    last_newline_index: usize,
    tag: &'a CStr,
    buffer: [u8; LOGGING_MSG_MAX_LEN + 1],
}

impl<'a> PlatformLogWriter<'a> {
    pub fn new<'r>(level: LogLevel, tag: &'r CStr) -> PlatformLogWriter<'r> {
        PlatformLogWriter {
            priority: match level {
                LogLevel::Warn => LogPriority::WARN,
                LogLevel::Info => LogPriority::INFO,
                LogLevel::Debug => LogPriority::DEBUG,
                LogLevel::Error => LogPriority::ERROR,
                LogLevel::Trace => LogPriority::VERBOSE,
            },
            len: 0,
            last_newline_index: 0,
            tag: tag,
            buffer: unsafe { mem::uninitialized() },
        }
    }

    /// Flush some bytes to android logger.
    ///
    /// If there is a newline, flush up to it.
    /// If ther was no newline, flush all.
    ///
    /// Not guaranteed to flush everything.
    fn temporal_flush(&mut self) {
        let total_len = self.len;

        if total_len == 0 {
            return;
        }

        if self.last_newline_index > 0 {
            let copy_from_index = self.last_newline_index;
            let remaining_chunk_len = total_len - copy_from_index;

            self.output_specified_len(copy_from_index);
            self.copy_bytes_to_start(copy_from_index, remaining_chunk_len);
            self.len = remaining_chunk_len;
        } else {
            self.output_specified_len(total_len);
            self.len = 0;
        }
        self.last_newline_index = 0;
    }

    /// Flush everything remaining to android logger.
    fn flush(&mut self) {
        let total_len = self.len;

        if total_len == 0 {
            return;
        }

        self.output_specified_len(total_len);
        self.len = 0;
        self.last_newline_index = 0;
    }

    /// Output buffer up until the \0 which will be placed at `len` position.
    fn output_specified_len(&mut self, len: usize) {
        let mut last_byte: u8 = b'\0';
        mem::swap(&mut last_byte, unsafe { self.buffer.get_unchecked_mut(len) });

        let msg: &CStr = unsafe { CStr::from_ptr(mem::transmute(self.buffer.as_ptr())) };
        android_log(self.priority, self.tag, msg);

        *unsafe { self.buffer.get_unchecked_mut(len) } = last_byte;
    }

    /// Copy `len` bytes from `index` position to starting position.
    fn copy_bytes_to_start(&mut self, index: usize, len: usize) {
        let src = unsafe { self.buffer.as_ptr().offset(index as isize) };
        let dst = self.buffer.as_mut_ptr();
        unsafe { ptr::copy(src, dst, len) };
    }
}

impl<'a> fmt::Write for PlatformLogWriter<'a> {
    fn write_str(&mut self, s: &str) -> fmt::Result {
        let mut incomming_bytes = s.as_bytes();

        while incomming_bytes.len() > 0 {
            let len = self.len;

            // write everything possible to buffer and mark last \n
            let new_len = len + incomming_bytes.len();
            let last_newline = self.buffer[len..LOGGING_MSG_MAX_LEN].iter_mut()
                .zip(incomming_bytes)
                .enumerate()
                .fold(None, |acc, (i, (output, input))| {
                    *output = *input;
                    if *input == b'\n' { Some(i) } else { acc }
                });

            // update last \n index
            if let Some(newline) = last_newline {
                self.last_newline_index = len + newline;
            }

            // calculate how many bytes were written
            let written_len = if new_len <= LOGGING_MSG_MAX_LEN {
                // if the len was not exceeded
                self.len = new_len;
                new_len - len // written len
            } else {
                // if new length was exceeded
                self.len = LOGGING_MSG_MAX_LEN;
                self.temporal_flush();

                LOGGING_MSG_MAX_LEN - len // written len
            };

            incomming_bytes = &incomming_bytes[written_len..];
        }

        Ok(())
    }
}

/// Initializes the global logger with an android logger.
///
/// This can be called many times, but will only initialize logging once,
/// and will not replace any other previously initialized logger.
///
/// It is ok to call this at the activity creation, and it will be
/// repeatedly called on every lifecycle restart (i.e. screen rotation).
pub fn init_once(log_level: LogLevel) {
    match log::set_logger(|max_log_level| {
        max_log_level.set(log_level.to_log_level_filter());
        return Box::new(AndroidLogger::new());
    }) {
        Err(e) => debug!("{}", e),
        _ => (),
    }
}
<|endoftext|>


<commit_before>//! Simple resampling library in pure Rust.
//!
//! # Examples
//!
//! ```ignore
//! extern crate resize;
//! use resize::Pixel::Gray8;
//! use resize::Type::Triangle;
//! let mut src = vec![0;w1*h1];
//! let mut dst = vec![0;w2*h2];
//! let mut resizer = resize::new(w1, h1, w2, h2, Gray8, Triangle);
//! resizer.resize(&src, &mut dst);
//! ```
// Current implementation is based on:
// * https://github.com/sekrit-twc/zimg/tree/master/src/zimg/resize
// * https://github.com/PistonDevelopers/image/blob/master/src/imageops/sample.rs
#![deny(missing_docs)]

use std::f32;

/// Resizing type to use.
pub enum Type {
    /// Triangle (bilinear) resizing.
    Triangle,
    /// Resize using sinc-windowed filter with radius of 3.
    Lanczos3,
    /// Resize using custom filter.
    Custom(Filter),
}

/// Resampling filter.
pub struct Filter {
    kernel: Box<Fn(f32) -> f32>,
    support: f32,
}

impl Filter {
    /// Create a new filter.
    ///
    /// # Examples
    ///
    /// ```
    /// use resize::Filter;
    /// fn nearest(x: f32) -> f32 { f32::round(x) }
    /// let filter = Filter::new(Box::new(nearest), 0.5);
    /// ```
    pub fn new(kernel: Box<Fn(f32) -> f32>, support: f32) -> Filter {
        Filter {kernel: kernel, support: support}
    }
}

#[inline]
fn triangle_kernel(x: f32) -> f32 {
    f32::max(1.0 - x.abs(), 0.0)
}

#[inline]
fn sinc(x: f32) -> f32 {
    if x == 0.0 {
        1.0
    } else {
        let a = x * f32::consts::PI;
        a.sin() / a
    }
}

#[inline]
fn lanczos3_kernel(x: f32) -> f32 {
    if x.abs() < 3.0 {
        sinc(x) * sinc(x / 3.0)
    } else {
        0.0
    }
}

/// Supported pixel formats.
// TODO(Kagami): >8-bit formats.
#[derive(Debug, Clone, Copy)]
pub enum Pixel {
    /// Grayscale, 8-bit.
    Gray8,
    /// RGB, 8-bit per component.
    RGB24,
    /// RGBA, 8-bit per component.
    RGBA,
}

impl Pixel {
    /// Size of one pixel in that format in bytes.
    #[inline]
    pub fn get_size(&self) -> usize {
        match *self {
            Pixel::Gray8 => 1,
            Pixel::RGB24 => 3,
            Pixel::RGBA => 4,
        }
    }

    /// Return number of components of that format.
    #[inline]
    pub fn get_ncomponents(&self) -> usize {
        match *self {
            Pixel::Gray8 => 1,
            Pixel::RGB24 => 3,
            Pixel::RGBA => 4,
        }
    }
}

/// Resampler with preallocated buffers and coeffecients for the given
/// dimensions and filter type.
#[derive(Debug)]
pub struct Resizer {
    // Source/target dimensions.
    w1: usize,
    h1: usize,
    w2: usize,
    h2: usize,
    pix_fmt: Pixel,
    // Temporary/preallocated stuff.
    tmp: Vec<f32>,
    accum: Vec<f32>,
    coeffs_w: Vec<CoeffsLine>,
    coeffs_h: Vec<CoeffsLine>,
}

#[derive(Debug)]
struct CoeffsLine {
    left: usize,
    data: Vec<f32>,
}

impl Resizer {
    /// Create a new resizer instance.
    pub fn new(w1: usize, h1: usize, w2: usize, h2: usize, p: Pixel, t: Type) -> Resizer {
        let filter = match t {
            Type::Triangle => Filter::new(Box::new(triangle_kernel), 1.0),
            Type::Lanczos3 => Filter::new(Box::new(lanczos3_kernel), 3.0),
            Type::Custom(f) => f,
        };
        Resizer {
            w1: w1,
            h1: h1,
            w2: w2,
            h2: h2,
            pix_fmt: p,
            tmp: vec![0.0;w1*h2*p.get_size()],
            accum: vec![0.0;p.get_ncomponents()],
            // TODO(Kagami): Use same coeffs if w1 = h1 = w2 = h2?
            coeffs_w: Self::calc_coeffs(w1, w2, &filter),
            coeffs_h: Self::calc_coeffs(h1, h2, &filter),
        }
    }

    fn calc_coeffs(s1: usize, s2: usize, f: &Filter) -> Vec<CoeffsLine> {
        let ratio = s1 as f32 / s2 as f32;
        // Scale the filter when downsampling.
        let filter_scale = if ratio > 1.0 { ratio } else { 1.0 };
        let filter_radius = (f.support * filter_scale).ceil();
        let mut coeffs = Vec::with_capacity(s2);
        for x2 in 0..s2 {
            let x1 = (x2 as f32 + 0.5) * ratio - 0.5;
            let left = (x1 - filter_radius).ceil() as isize;
            let left = Self::clamp(left, 0, s1 as isize - 1) as usize;
            let right = (x1 + filter_radius).floor() as isize;
            let right = Self::clamp(right, 0, s1 as isize - 1) as usize;
            let mut data = Vec::with_capacity(right - left + 1);
            let mut sum = 0.0;
            for i in left..right+1 {
                sum += (f.kernel)((i as f32 - x1) / filter_scale);
            }
            for i in left..right+1 {
                let v = (f.kernel)((i as f32 - x1) / filter_scale);
                data.push(v / sum);
            }
            coeffs.push(CoeffsLine {left: left, data: data});
        }
        coeffs
    }

    #[inline]
    fn clamp<N: PartialOrd>(v: N, min: N, max: N) -> N {
        if v <= min {
            min
        } else if v >= max {
            max
        } else {
            v
        }
    }

    #[inline]
    fn pack_u8(v: f32) -> u8 {
        let mut v = v.round();
        v = f32::min(f32::max(v, 0.0), 255.0);
        v as u8
    }

    #[inline]
    fn clear_accum(&mut self) {
        for v in self.accum.iter_mut() {
            *v = 0.0;
        }
    }

    // Resample W1xH1 to W1xH2.
    fn sample_rows(&mut self, src: &[u8]) {
        let ncomp = self.pix_fmt.get_ncomponents();
        let mut offset = 0;
        for x1 in 0..self.w1 {
            for y2 in 0..self.h2 {
                self.clear_accum();
                let ref line = self.coeffs_h[y2];
                for (i, coeff) in line.data.iter().enumerate() {
                    let y0 = line.left + i;
                    let base = (y0 * self.w1 + x1) * ncomp;
                    for n in 0..ncomp {
                        let p = src[base + n] as f32;
                        self.accum[n] += p * coeff;
                    }
                }
                for &v in &self.accum {
                    self.tmp[offset] = v;
                    offset += 1;
                }
            }
        }
    }

    // Resample W1xH2 to W2xH2.
    fn sample_cols(&mut self, dst: &mut [u8]) {
        let ncomp = self.pix_fmt.get_ncomponents();
        let mut offset = 0;
        for y2 in 0..self.h2 {
            for x2 in 0..self.w2 {
                self.clear_accum();
                let ref line = self.coeffs_w[x2];
                for (i, coeff) in line.data.iter().enumerate() {
                    let x0 = line.left + i;
                    let base = (x0 * self.h2 + y2) * ncomp;
                    for n in 0..ncomp {
                        let p = self.tmp[base + n];
                        self.accum[n] += p * coeff;
                    }
                }
                for &v in &self.accum {
                    dst[offset] = Self::pack_u8(v);
                    offset += 1;
                }
            }
        }
    }

    /// Resize `src` image data into `dst`.
    pub fn resize(&mut self, src: &[u8], dst: &mut [u8]) {
        // TODO(Kagami):
        // * Multi-thread
        // * Bound checkings
        // * SIMD
        assert_eq!(src.len(), self.w1 * self.h1 * self.pix_fmt.get_size());
        assert_eq!(dst.len(), self.w2 * self.h2 * self.pix_fmt.get_size());
        self.sample_rows(src);
        self.sample_cols(dst)
    }
}

/// Create a new resizer instance. Alias for `Resizer::new`.
pub fn new(w1: usize, h1: usize, w2: usize, h2: usize, p: Pixel, t: Type) -> Resizer {
    Resizer::new(w1, h1, w2, h2, p, t)
}

/// Resize image data to the new dimension in a single step.
///
/// **NOTE:** If you need to resize to the same dimension multiple times,
/// consider creating an resizer instance since it's faster.
pub fn resize(
    w1: usize, h1: usize, w2: usize, h2: usize,
    p: Pixel, t: Type,
    src: &[u8], dst: &mut [u8],
) {
    Resizer::new(w1, h1, w2, h2, p, t).resize(src, dst)
}
<commit_msg>Add new filters<commit_after>//! Simple resampling library in pure Rust.
//!
//! # Examples
//!
//! ```ignore
//! extern crate resize;
//! use resize::Pixel::Gray8;
//! use resize::Type::Triangle;
//! let mut src = vec![0;w1*h1];
//! let mut dst = vec![0;w2*h2];
//! let mut resizer = resize::new(w1, h1, w2, h2, Gray8, Triangle);
//! resizer.resize(&src, &mut dst);
//! ```
// Current implementation is based on:
// * https://github.com/sekrit-twc/zimg/tree/master/src/zimg/resize
// * https://github.com/PistonDevelopers/image/blob/master/src/imageops/sample.rs
#![deny(missing_docs)]

use std::f32;

/// Resizing type to use.
pub enum Type {
    /// Point resizing.
    Point,
    /// Box (nearest) resizing.
    Nearest,
    /// Triangle (bilinear) resizing.
    Triangle,
    /// Catmull-Rom (bicubic) resizing.
    Catrom,
    /// Resize using sinc-windowed filter with radius of 3.
    Lanczos3,
    /// Resize using custom filter.
    Custom(Filter),
}

/// Resampling filter.
pub struct Filter {
    kernel: Box<Fn(f32) -> f32>,
    support: f32,
}

impl Filter {
    /// Create a new filter.
    ///
    /// # Examples
    ///
    /// ```
    /// use resize::Filter;
    /// fn kernel(x: f32) -> f32 { f32::round(x) }
    /// let filter = Filter::new(Box::new(kernel), 0.5);
    /// ```
    pub fn new(kernel: Box<Fn(f32) -> f32>, support: f32) -> Filter {
        Filter {kernel: kernel, support: support}
    }
}

#[inline]
fn point_kernel(_: f32) -> f32 {
    1.0
}

#[inline]
fn box_kernel(x: f32) -> f32 {
    if x.abs() <= 0.5 { 1.0 } else { 0.0 }
}

#[inline]
fn triangle_kernel(x: f32) -> f32 {
    f32::max(1.0 - x.abs(), 0.0)
}

// Taken from
// https://github.com/PistonDevelopers/image/blob/2921cd7/src/imageops/sample.rs#L68
// Probably may be optimized a bit, see e.g.
// https://github.com/sekrit-twc/zimg/blob/1a606c0/src/zimg/resize/filter.cpp#L149
#[inline]
fn bc_cubic_spline(x: f32, b: f32, c: f32) -> f32 {
    let a = x.abs();
    let k = if a < 1.0 {
        (12.0 - 9.0 * b - 6.0 * c) * a.powi(3) +
        (-18.0 + 12.0 * b + 6.0 * c) * a.powi(2) +
        (6.0 - 2.0 * b)
    } else if a < 2.0 {
        (-b - 6.0 * c) * a.powi(3) +
        (6.0 * b + 30.0 * c) * a.powi(2) +
        (-12.0 * b - 48.0 * c) * a +
        (8.0 * b + 24.0 * c)
    } else {
        0.0
    };
    k / 6.0
}

#[inline]
fn catrom_kernel(x: f32) -> f32 {
    bc_cubic_spline(x, 0.0, 0.5)
}

#[inline]
fn sinc(x: f32) -> f32 {
    if x == 0.0 {
        1.0
    } else {
        let a = x * f32::consts::PI;
        a.sin() / a
    }
}

#[inline]
fn lanczos3_kernel(x: f32) -> f32 {
    if x.abs() < 3.0 {
        sinc(x) * sinc(x / 3.0)
    } else {
        0.0
    }
}

/// Supported pixel formats.
// TODO(Kagami): >8-bit formats.
#[derive(Debug, Clone, Copy)]
pub enum Pixel {
    /// Grayscale, 8-bit.
    Gray8,
    /// RGB, 8-bit per component.
    RGB24,
    /// RGBA, 8-bit per component.
    RGBA,
}

impl Pixel {
    /// Size of one pixel in that format in bytes.
    #[inline]
    pub fn get_size(&self) -> usize {
        match *self {
            Pixel::Gray8 => 1,
            Pixel::RGB24 => 3,
            Pixel::RGBA => 4,
        }
    }

    /// Return number of components of that format.
    #[inline]
    pub fn get_ncomponents(&self) -> usize {
        match *self {
            Pixel::Gray8 => 1,
            Pixel::RGB24 => 3,
            Pixel::RGBA => 4,
        }
    }
}

/// Resampler with preallocated buffers and coeffecients for the given
/// dimensions and filter type.
#[derive(Debug)]
pub struct Resizer {
    // Source/target dimensions.
    w1: usize,
    h1: usize,
    w2: usize,
    h2: usize,
    pix_fmt: Pixel,
    // Temporary/preallocated stuff.
    tmp: Vec<f32>,
    accum: Vec<f32>,
    coeffs_w: Vec<CoeffsLine>,
    coeffs_h: Vec<CoeffsLine>,
}

#[derive(Debug)]
struct CoeffsLine {
    left: usize,
    data: Vec<f32>,
}

impl Resizer {
    /// Create a new resizer instance.
    pub fn new(w1: usize, h1: usize, w2: usize, h2: usize, p: Pixel, t: Type) -> Resizer {
        let filter = match t {
            Type::Point     => Filter::new(Box::new(point_kernel),    0.0),
            Type::Nearest   => Filter::new(Box::new(box_kernel),      0.5),
            Type::Triangle  => Filter::new(Box::new(triangle_kernel), 1.0),
            Type::Catrom    => Filter::new(Box::new(catrom_kernel),   2.0),
            Type::Lanczos3  => Filter::new(Box::new(lanczos3_kernel), 3.0),
            Type::Custom(f) => f,
        };
        Resizer {
            w1: w1,
            h1: h1,
            w2: w2,
            h2: h2,
            pix_fmt: p,
            tmp: vec![0.0;w1*h2*p.get_size()],
            accum: vec![0.0;p.get_ncomponents()],
            // TODO(Kagami): Use same coeffs if w1 = h1 = w2 = h2?
            coeffs_w: Self::calc_coeffs(w1, w2, &filter),
            coeffs_h: Self::calc_coeffs(h1, h2, &filter),
        }
    }

    fn calc_coeffs(s1: usize, s2: usize, f: &Filter) -> Vec<CoeffsLine> {
        let ratio = s1 as f32 / s2 as f32;
        // Scale the filter when downsampling.
        let filter_scale = if ratio > 1.0 { ratio } else { 1.0 };
        let filter_radius = (f.support * filter_scale).ceil();
        let mut coeffs = Vec::with_capacity(s2);
        for x2 in 0..s2 {
            let x1 = (x2 as f32 + 0.5) * ratio - 0.5;
            let left = (x1 - filter_radius).ceil() as isize;
            let left = Self::clamp(left, 0, s1 as isize - 1) as usize;
            let right = (x1 + filter_radius).floor() as isize;
            let right = Self::clamp(right, 0, s1 as isize - 1) as usize;
            let mut data = Vec::with_capacity(right - left + 1);
            let mut sum = 0.0;
            for i in left..right+1 {
                sum += (f.kernel)((i as f32 - x1) / filter_scale);
            }
            for i in left..right+1 {
                let v = (f.kernel)((i as f32 - x1) / filter_scale);
                data.push(v / sum);
            }
            coeffs.push(CoeffsLine {left: left, data: data});
        }
        coeffs
    }

    #[inline]
    fn clamp<N: PartialOrd>(v: N, min: N, max: N) -> N {
        if v <= min {
            min
        } else if v >= max {
            max
        } else {
            v
        }
    }

    #[inline]
    fn pack_u8(v: f32) -> u8 {
        let mut v = v.round();
        v = f32::min(f32::max(v, 0.0), 255.0);
        v as u8
    }

    #[inline]
    fn clear_accum(&mut self) {
        for v in self.accum.iter_mut() {
            *v = 0.0;
        }
    }

    // Resample W1xH1 to W1xH2.
    fn sample_rows(&mut self, src: &[u8]) {
        let ncomp = self.pix_fmt.get_ncomponents();
        let mut offset = 0;
        for x1 in 0..self.w1 {
            for y2 in 0..self.h2 {
                self.clear_accum();
                let ref line = self.coeffs_h[y2];
                for (i, coeff) in line.data.iter().enumerate() {
                    let y0 = line.left + i;
                    let base = (y0 * self.w1 + x1) * ncomp;
                    for n in 0..ncomp {
                        let p = src[base + n] as f32;
                        self.accum[n] += p * coeff;
                    }
                }
                for &v in &self.accum {
                    self.tmp[offset] = v;
                    offset += 1;
                }
            }
        }
    }

    // Resample W1xH2 to W2xH2.
    fn sample_cols(&mut self, dst: &mut [u8]) {
        let ncomp = self.pix_fmt.get_ncomponents();
        let mut offset = 0;
        for y2 in 0..self.h2 {
            for x2 in 0..self.w2 {
                self.clear_accum();
                let ref line = self.coeffs_w[x2];
                for (i, coeff) in line.data.iter().enumerate() {
                    let x0 = line.left + i;
                    let base = (x0 * self.h2 + y2) * ncomp;
                    for n in 0..ncomp {
                        let p = self.tmp[base + n];
                        self.accum[n] += p * coeff;
                    }
                }
                for &v in &self.accum {
                    dst[offset] = Self::pack_u8(v);
                    offset += 1;
                }
            }
        }
    }

    /// Resize `src` image data into `dst`.
    pub fn resize(&mut self, src: &[u8], dst: &mut [u8]) {
        // TODO(Kagami):
        // * Multi-thread
        // * Bound checkings
        // * SIMD
        assert_eq!(src.len(), self.w1 * self.h1 * self.pix_fmt.get_size());
        assert_eq!(dst.len(), self.w2 * self.h2 * self.pix_fmt.get_size());
        self.sample_rows(src);
        self.sample_cols(dst)
    }
}

/// Create a new resizer instance. Alias for `Resizer::new`.
pub fn new(w1: usize, h1: usize, w2: usize, h2: usize, p: Pixel, t: Type) -> Resizer {
    Resizer::new(w1, h1, w2, h2, p, t)
}

/// Resize image data to the new dimension in a single step.
///
/// **NOTE:** If you need to resize to the same dimension multiple times,
/// consider creating an resizer instance since it's faster.
pub fn resize(
    w1: usize, h1: usize, w2: usize, h2: usize,
    p: Pixel, t: Type,
    src: &[u8], dst: &mut [u8],
) {
    Resizer::new(w1, h1, w2, h2, p, t).resize(src, dst)
}
<|endoftext|>


<commit_before>// Copyright 2014-2015 The GeoRust Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//	http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::default::Default;
use std::fmt;

use tokenizer::{PeekableTokens, Token, Tokens};
use types::GeometryCollection;
use types::LineString;
use types::MultiLineString;
use types::MultiPoint;
use types::MultiPolygon;
use types::Point;
use types::Polygon;

mod tokenizer;

#[cfg(feature = "geo-types")]
mod towkt;

pub mod types;

#[cfg(feature = "geo-types")]
pub use towkt::ToWkt;

#[cfg(feature = "geo-types")]
pub mod conversion;

pub enum Geometry {
    Point(Point),
    LineString(LineString),
    Polygon(Polygon),
    MultiPoint(MultiPoint),
    MultiLineString(MultiLineString),
    MultiPolygon(MultiPolygon),
    GeometryCollection(GeometryCollection),
}

impl Geometry {
    fn from_word_and_tokens(word: &str, tokens: &mut PeekableTokens) -> Result<Self, &'static str> {
        match word {
            "POINT" => {
                let x = <Point as FromTokens>::from_tokens_with_parens(tokens);
                x.map(|y| y.as_item())
            }
            "LINESTRING" => {
                let x = <LineString as FromTokens>::from_tokens_with_parens(tokens);
                x.map(|y| y.as_item())
            }
            "POLYGON" => {
                let x = <Polygon as FromTokens>::from_tokens_with_parens(tokens);
                x.map(|y| y.as_item())
            }
            "MULTIPOINT" => {
                let x = <MultiPoint as FromTokens>::from_tokens_with_parens(tokens);
                x.map(|y| y.as_item())
            }
            "MULTILINESTRING" => {
                let x = <MultiLineString as FromTokens>::from_tokens_with_parens(tokens);
                x.map(|y| y.as_item())
            }
            "MULTIPOLYGON" => {
                let x = <MultiPolygon as FromTokens>::from_tokens_with_parens(tokens);
                x.map(|y| y.as_item())
            }
            "GEOMETRYCOLLECTION" => {
                let x = <GeometryCollection as FromTokens>::from_tokens_with_parens(tokens);
                x.map(|y| y.as_item())
            }
            _ => Err("Invalid type encountered"),
        }
    }
}

impl fmt::Display for Geometry {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match self {
            Geometry::Point(point) => point.fmt(f),
            Geometry::LineString(linestring) => linestring.fmt(f),
            Geometry::Polygon(polygon) => polygon.fmt(f),
            Geometry::MultiPoint(multipoint) => multipoint.fmt(f),
            Geometry::MultiLineString(multilinstring) => multilinstring.fmt(f),
            Geometry::MultiPolygon(multipolygon) => multipolygon.fmt(f),
            Geometry::GeometryCollection(geometrycollection) => geometrycollection.fmt(f),
        }
    }
}

pub struct Wkt {
    pub items: Vec<Geometry>,
}

impl Wkt {
    pub fn new() -> Self {
        Wkt { items: vec![] }
    }

    pub fn add_item(&mut self, item: Geometry) {
        self.items.push(item);
    }

    pub fn from_str(wkt_str: &str) -> Result<Self, &'static str> {
        let tokens = Tokens::from_str(wkt_str);
        Wkt::from_tokens(tokens)
    }

    fn from_tokens(tokens: Tokens) -> Result<Self, &'static str> {
        let mut wkt = Wkt::new();
        let mut tokens = tokens.peekable();
        let word = match tokens.next() {
            Some(Token::Word(mut word)) => {
                if !word.is_ascii() {
                    return Err("Encountered non-ascii word");
                }
                word.make_ascii_uppercase();
                word
            }
            None => return Ok(wkt),
            _ => return Err("Invalid WKT format"),
        };
        match Geometry::from_word_and_tokens(&word, &mut tokens) {
            Ok(item) => wkt.add_item(item),
            Err(s) => return Err(s),
        }
        Ok(wkt)
    }
}

trait FromTokens: Sized + Default {
    fn from_tokens(tokens: &mut PeekableTokens) -> Result<Self, &'static str>;

    fn from_tokens_with_parens(tokens: &mut PeekableTokens) -> Result<Self, &'static str> {
        match tokens.next() {
            Some(Token::ParenOpen) => (),
            Some(Token::Word(ref s)) if s.to_ascii_uppercase() == "EMPTY" => {
                return Ok(Default::default())
            }
            _ => return Err("Missing open parenthesis for type"),
        };
        let result = FromTokens::from_tokens(tokens);
        match tokens.next() {
            Some(Token::ParenClose) => (),
            _ => return Err("Missing closing parenthesis for type"),
        };
        result
    }

    fn comma_many<F>(f: F, tokens: &mut PeekableTokens) -> Result<Vec<Self>, &'static str>
    where
        F: Fn(&mut PeekableTokens) -> Result<Self, &'static str>,
    {
        let mut items = Vec::new();

        let item = try!(f(tokens));
        items.push(item);

        while let Some(&Token::Comma) = tokens.peek() {
            tokens.next(); // throw away comma

            let item = try!(f(tokens));
            items.push(item);
        }

        Ok(items)
    }
}

#[cfg(test)]
mod tests {
    use types::{MultiPolygon, Point};
    use {Geometry, Wkt};

    #[test]
    fn empty_string() {
        let wkt = Wkt::from_str("").ok().unwrap();
        assert_eq!(0, wkt.items.len());
    }

    #[test]
    fn empty_items() {
        let mut wkt = Wkt::from_str("POINT EMPTY").ok().unwrap();
        assert_eq!(1, wkt.items.len());
        match wkt.items.pop().unwrap() {
            Geometry::Point(Point(None)) => (),
            _ => unreachable!(),
        };

        let mut wkt = Wkt::from_str("MULTIPOLYGON EMPTY").ok().unwrap();
        assert_eq!(1, wkt.items.len());
        match wkt.items.pop().unwrap() {
            Geometry::MultiPolygon(MultiPolygon(polygons)) => assert_eq!(polygons.len(), 0),
            _ => unreachable!(),
        };
    }
}
<commit_msg>avoid unneeded uppercase allocation<commit_after>// Copyright 2014-2015 The GeoRust Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//	http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::default::Default;
use std::fmt;

use tokenizer::{PeekableTokens, Token, Tokens};
use types::GeometryCollection;
use types::LineString;
use types::MultiLineString;
use types::MultiPoint;
use types::MultiPolygon;
use types::Point;
use types::Polygon;

mod tokenizer;

#[cfg(feature = "geo-types")]
mod towkt;

pub mod types;

#[cfg(feature = "geo-types")]
pub use towkt::ToWkt;

#[cfg(feature = "geo-types")]
pub mod conversion;

pub enum Geometry {
    Point(Point),
    LineString(LineString),
    Polygon(Polygon),
    MultiPoint(MultiPoint),
    MultiLineString(MultiLineString),
    MultiPolygon(MultiPolygon),
    GeometryCollection(GeometryCollection),
}

impl Geometry {
    fn from_word_and_tokens(word: &str, tokens: &mut PeekableTokens) -> Result<Self, &'static str> {
        match word {
            "POINT" => {
                let x = <Point as FromTokens>::from_tokens_with_parens(tokens);
                x.map(|y| y.as_item())
            }
            "LINESTRING" => {
                let x = <LineString as FromTokens>::from_tokens_with_parens(tokens);
                x.map(|y| y.as_item())
            }
            "POLYGON" => {
                let x = <Polygon as FromTokens>::from_tokens_with_parens(tokens);
                x.map(|y| y.as_item())
            }
            "MULTIPOINT" => {
                let x = <MultiPoint as FromTokens>::from_tokens_with_parens(tokens);
                x.map(|y| y.as_item())
            }
            "MULTILINESTRING" => {
                let x = <MultiLineString as FromTokens>::from_tokens_with_parens(tokens);
                x.map(|y| y.as_item())
            }
            "MULTIPOLYGON" => {
                let x = <MultiPolygon as FromTokens>::from_tokens_with_parens(tokens);
                x.map(|y| y.as_item())
            }
            "GEOMETRYCOLLECTION" => {
                let x = <GeometryCollection as FromTokens>::from_tokens_with_parens(tokens);
                x.map(|y| y.as_item())
            }
            _ => Err("Invalid type encountered"),
        }
    }
}

impl fmt::Display for Geometry {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match self {
            Geometry::Point(point) => point.fmt(f),
            Geometry::LineString(linestring) => linestring.fmt(f),
            Geometry::Polygon(polygon) => polygon.fmt(f),
            Geometry::MultiPoint(multipoint) => multipoint.fmt(f),
            Geometry::MultiLineString(multilinstring) => multilinstring.fmt(f),
            Geometry::MultiPolygon(multipolygon) => multipolygon.fmt(f),
            Geometry::GeometryCollection(geometrycollection) => geometrycollection.fmt(f),
        }
    }
}

pub struct Wkt {
    pub items: Vec<Geometry>,
}

impl Wkt {
    pub fn new() -> Self {
        Wkt { items: vec![] }
    }

    pub fn add_item(&mut self, item: Geometry) {
        self.items.push(item);
    }

    pub fn from_str(wkt_str: &str) -> Result<Self, &'static str> {
        let tokens = Tokens::from_str(wkt_str);
        Wkt::from_tokens(tokens)
    }

    fn from_tokens(tokens: Tokens) -> Result<Self, &'static str> {
        let mut wkt = Wkt::new();
        let mut tokens = tokens.peekable();
        let word = match tokens.next() {
            Some(Token::Word(mut word)) => {
                if !word.is_ascii() {
                    return Err("Encountered non-ascii word");
                }
                word.make_ascii_uppercase();
                word
            }
            None => return Ok(wkt),
            _ => return Err("Invalid WKT format"),
        };
        match Geometry::from_word_and_tokens(&word, &mut tokens) {
            Ok(item) => wkt.add_item(item),
            Err(s) => return Err(s),
        }
        Ok(wkt)
    }
}

trait FromTokens: Sized + Default {
    fn from_tokens(tokens: &mut PeekableTokens) -> Result<Self, &'static str>;

    fn from_tokens_with_parens(tokens: &mut PeekableTokens) -> Result<Self, &'static str> {
        match tokens.next() {
            Some(Token::ParenOpen) => (),
            Some(Token::Word(ref s)) if s.eq_ignore_ascii_case("EMPTY") => {
                return Ok(Default::default())
            }
            _ => return Err("Missing open parenthesis for type"),
        };
        let result = FromTokens::from_tokens(tokens);
        match tokens.next() {
            Some(Token::ParenClose) => (),
            _ => return Err("Missing closing parenthesis for type"),
        };
        result
    }

    fn comma_many<F>(f: F, tokens: &mut PeekableTokens) -> Result<Vec<Self>, &'static str>
    where
        F: Fn(&mut PeekableTokens) -> Result<Self, &'static str>,
    {
        let mut items = Vec::new();

        let item = try!(f(tokens));
        items.push(item);

        while let Some(&Token::Comma) = tokens.peek() {
            tokens.next(); // throw away comma

            let item = try!(f(tokens));
            items.push(item);
        }

        Ok(items)
    }
}

#[cfg(test)]
mod tests {
    use types::{MultiPolygon, Point};
    use {Geometry, Wkt};

    #[test]
    fn empty_string() {
        let wkt = Wkt::from_str("").ok().unwrap();
        assert_eq!(0, wkt.items.len());
    }

    #[test]
    fn empty_items() {
        let mut wkt = Wkt::from_str("POINT EMPTY").ok().unwrap();
        assert_eq!(1, wkt.items.len());
        match wkt.items.pop().unwrap() {
            Geometry::Point(Point(None)) => (),
            _ => unreachable!(),
        };

        let mut wkt = Wkt::from_str("MULTIPOLYGON EMPTY").ok().unwrap();
        assert_eq!(1, wkt.items.len());
        match wkt.items.pop().unwrap() {
            Geometry::MultiPolygon(MultiPolygon(polygons)) => assert_eq!(polygons.len(), 0),
            _ => unreachable!(),
        };
    }
}
<|endoftext|>