<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;

#[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.
///
/// 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() {
///     // ...
/// }
/// ```
pub fn open(url: &str) -> Result<Output> {
    open_browser(Browser::Default, url)
}

/// 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() {
///     // ...
/// }
/// ```
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 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"),
                _ => 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, respectively, whichever works
///    first
#[cfg(any(
    target_os = "linux",
    target_os = "freebsd",
    target_os = "netbsd",
    target_os = "openbsd"
))]
#[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("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"
))]
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 = "windows",
    target_os = "macos",
    target_os = "linux",
    target_os = "freebsd",
    target_os = "netbsd",
    target_os = "openbsd"
)))]
compile_error!("Only Windows, Mac OS, Linux and *BSD 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]
#[ignore]
fn test_open_safari() {
    assert!(open_browser(Browser::Safari, "http://github.com").is_ok());
}
<commit_msg>Added initial support for Haiku<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;

#[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() {
///     // ...
/// }
/// ```
pub fn open(url: &str) -> Result<Output> {
    open_browser(Browser::Default, url)
}

/// 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() {
///     // ...
/// }
/// ```
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 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 = "windows",
    target_os = "macos",
    target_os = "linux",
    target_os = "freebsd",
    target_os = "netbsd",
    target_os = "openbsd",
    target_os = "haiku"
)))]
compile_error!("Only Windows, Mac OS, Linux, *BSD and Haiku 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]
#[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>// #[macro_use] extern crate failure_derive;

use dirs;

use std::collections::HashMap;
use std::fs::File;
use std::io::Read;
use std::io::Write;
use std::path::PathBuf;

use chrono::datetime::DateTime;
use chrono::naive::datetime::NaiveDateTime;
use chrono::offset::fixed::FixedOffset;
use chrono::offset::local::Local;
use chrono::Datelike;

use hyper::client::Client;
use rustc_serialize::json;
use rustc_serialize::Decodable;

use failure::Fail;
use regex::Regex;
use std::result;

type Result<T> = result::Result<T, WsfError>;

pub struct Session {
    api_key: String,
    client: Client,
    cache: Cache,
    cacheflushdate: String,
    cache_path: String,
    offline: bool,
}

impl Session {
    pub fn new(api_key: &str) -> Session {
        let mut cache_path: PathBuf = dirs::home_dir().unwrap();
        cache_path.push(".wsf.cache");
        let cache_path = cache_path.display().to_string();

        let mut s = Session {
            api_key: api_key.to_string(),
            client: Client::new(),
            cache: Cache::load(&cache_path).unwrap_or_else(|_| Cache::empty()),
            cacheflushdate: String::new(),
            cache_path,
            offline: false,
        };

        // TODO this is kind of gross, cfd as optional to indicate offline, maybe?
        s.offline = match s.get::<String>("/cacheflushdate".to_owned()) {
            Ok(cfd) => {
                s.cacheflushdate = cfd;
                false
            }
            Err(_) => true,
        };
        s
    }

    pub fn save_cache(&mut self) -> Result<()> {
        self.cache.cache_flush_date = self.cacheflushdate.clone();

        let mut f = File::create(&self.cache_path)?;
        let encoded = json::encode(&self.cache)?;
        f.write_all(encoded.as_bytes())?;
        Ok(())
    }

    fn get<T: Decodable>(&self, path: String) -> Result<T> {
        let url = &format!(
            "http://www.wsdot.wa.gov/ferries/api/schedule/rest{}?apiaccesscode={}",
            path, self.api_key
        );
        let mut res = self.client.get(url).send()?;
        assert_eq!(res.status, hyper::Ok);

        let mut buf = String::new();
        res.read_to_string(&mut buf)?;
        Ok(json::decode::<T>(&buf)?)
    }

    pub fn terminals(&mut self) -> Result<Vec<Terminal>> {
        if self.offline || (self.cache.cache_flush_date == self.cacheflushdate) {
            Ok(self.cache.terminals.clone())
        } else {
            let now = Local::today();
            let path = format!("/terminals/{}-{}-{}", now.year(), now.month(), now.day());
            let routes: Vec<Terminal> = self.get(path)?;
            self.cache.terminals = routes.clone();
            Ok(routes)
        }
    }

    pub fn schedule(&mut self, from: i32, to: i32) -> Result<TerminalCombo> {
        let mut cache_is_stale = true;
        let cache_key = format!("{} {}", from, to);

        if self.offline || (self.cache.cache_flush_date == self.cacheflushdate) {
            if self.cache.sailings.contains_key(&cache_key) {
                // cache is up to date and has route!
                return Ok(self
                    .cache
                    .sailings
                    .get(&cache_key)
                    .expect("checked for key in cache then not found")
                    .clone());
            } else {
                // cache is up to date, but we don't have this route in it
                cache_is_stale = false;
            }
        }

        if cache_is_stale {
            self.cache.sailings.clear();
        }

        let now = Local::now();
        let path = format!(
            "/schedule/{}-{}-{}/{}/{}",
            now.year(),
            now.month(),
            now.day(),
            from,
            to
        );

        let schedule: Schedule = self.get(path)?;

        self.cache
            .sailings
            .insert(cache_key, schedule.TerminalCombos[0].clone());
        Ok(schedule.TerminalCombos[0].clone())
    }
}

#[derive(RustcDecodable, RustcEncodable, Debug)]
struct Cache {
    terminals: Vec<Terminal>,
    sailings: HashMap<String, TerminalCombo>,
    cache_flush_date: String,
}

impl Cache {
    fn empty() -> Cache {
        Cache {
            terminals: vec![],
            sailings: HashMap::new(),
            cache_flush_date: String::new(),
        }
    }

    fn load(path: &str) -> Result<Cache> {
        let mut f = File::open(path)?;
        let mut s = String::new();
        f.read_to_string(&mut s)?;
        let cache: Cache = json::decode(&s)?;
        Ok(cache)
    }
}

#[allow(non_snake_case)]
#[derive(RustcDecodable, RustcEncodable, Debug, Clone)]
pub struct Terminal {
    pub TerminalID: i32,
    pub Description: String,
}

#[allow(non_snake_case)]
#[derive(RustcDecodable, RustcEncodable, Clone, Debug)]
pub struct SailingTime {
    pub DepartingTime: String,
    pub ArrivingTime: Option<String>,
    pub VesselName: String,
}

impl SailingTime {
    // parse date strings of form "/Date(1436318400000-0700)/"
    pub fn depart_time(&self) -> DateTime<Local> {
        let re = Regex::new(r"^/Date\((\d{10})000-(\d{2})(\d{2})\)/$").unwrap();
        let caps = re.captures(&self.DepartingTime).unwrap();

        let epoch: i64 = caps.at(1).unwrap().parse().unwrap();
        let tz_hours: i32 = caps.at(2).unwrap().parse().unwrap();
        let tz_minutes: i32 = caps.at(3).unwrap().parse().unwrap();

        let nd = NaiveDateTime::from_timestamp(epoch, 0);
        let tz = FixedOffset::west((tz_hours * 3600) + (tz_minutes * 60));
        let fotz: DateTime<FixedOffset> = DateTime::from_utc(nd, tz);
        fotz.with_timezone(&Local)
    }
}

#[allow(non_snake_case)]
#[derive(RustcDecodable, RustcEncodable, Clone, Debug)]
pub struct TerminalCombo {
    pub Times: Vec<SailingTime>,
    pub DepartingTerminalName: String,
    pub ArrivingTerminalName: String,
}

#[allow(non_snake_case)]
#[derive(RustcDecodable, RustcEncodable, Debug)]
pub struct Schedule {
    pub TerminalCombos: Vec<TerminalCombo>,
}

#[derive(Debug, Fail)]
pub enum WsfError {
    #[fail(display = "Unable to configure logging: {}", _0)]
    Log(log::SetLoggerError),

    #[fail(display = "Unable to parse WSDOT Data: {}", _0)]
    Parse(rustc_serialize::json::DecoderError),

    #[fail(display = "Unable to save cache: {}", _0)]
    SaveCache(rustc_serialize::json::EncoderError),

    #[fail(display = "Unable to communicate with WSDOT: {}", _0)]
    Http(hyper::error::Error),

    #[fail(display = "Unable to read data: {}", _0)]
    Io(std::io::Error),

    #[fail(display = "Unable to understand input: {}", _0)]
    BadInput(String),
}

impl From<rustc_serialize::json::EncoderError> for WsfError {
    fn from(err: rustc_serialize::json::EncoderError) -> WsfError {
        WsfError::SaveCache(err)
    }
}

impl From<log::SetLoggerError> for WsfError {
    fn from(err: log::SetLoggerError) -> WsfError {
        WsfError::Log(err)
    }
}

impl From<hyper::error::Error> for WsfError {
    fn from(err: hyper::error::Error) -> WsfError {
        WsfError::Http(err)
    }
}

impl From<std::io::Error> for WsfError {
    fn from(err: std::io::Error) -> WsfError {
        WsfError::Io(err)
    }
}

impl From<rustc_serialize::json::DecoderError> for WsfError {
    fn from(err: rustc_serialize::json::DecoderError) -> WsfError {
        WsfError::Parse(err)
    }
}
<commit_msg>get contextual Display b ack<commit_after>// #[macro_use] extern crate failure_derive;

use dirs;

use std::collections::HashMap;
use std::fs::File;
use std::io::Read;
use std::io::Write;
use std::path::PathBuf;

use chrono::datetime::DateTime;
use chrono::naive::datetime::NaiveDateTime;
use chrono::offset::fixed::FixedOffset;
use chrono::offset::local::Local;
use chrono::Datelike;

use hyper::client::Client;
use rustc_serialize::json;
use rustc_serialize::Decodable;

use failure::Fail;
use regex::Regex;
use std::result;

type Result<T> = result::Result<T, WsfError>;

pub struct Session {
    api_key: String,
    client: Client,
    cache: Cache,
    cacheflushdate: String,
    cache_path: String,
    offline: bool,
}

impl Session {
    pub fn new(api_key: &str) -> Session {
        let mut cache_path: PathBuf = dirs::home_dir().unwrap();
        cache_path.push(".wsf.cache");
        let cache_path = cache_path.display().to_string();

        let mut s = Session {
            api_key: api_key.to_string(),
            client: Client::new(),
            cache: Cache::load(&cache_path).unwrap_or_else(|_| Cache::empty()),
            cacheflushdate: String::new(),
            cache_path,
            offline: false,
        };

        // TODO this is kind of gross, cfd as optional to indicate offline, maybe?
        s.offline = match s.get::<String>("/cacheflushdate".to_owned()) {
            Ok(cfd) => {
                s.cacheflushdate = cfd;
                false
            }
            Err(_) => true,
        };
        s
    }

    pub fn save_cache(&mut self) -> Result<()> {
        self.cache.cache_flush_date = self.cacheflushdate.clone();

        let mut f = File::create(&self.cache_path)?;
        let encoded = json::encode(&self.cache)?;
        f.write_all(encoded.as_bytes())?;
        Ok(())
    }

    fn get<T: Decodable>(&self, path: String) -> Result<T> {
        let url = &format!(
            "http://www.wsdot.wa.gov/ferries/api/schedule/rest{}?apiaccesscode={}",
            path, self.api_key
        );
        let mut res = self.client.get(url).send()?;
        assert_eq!(res.status, hyper::Ok);

        let mut buf = String::new();
        res.read_to_string(&mut buf)?;
        Ok(json::decode::<T>(&buf)?)
    }

    pub fn terminals(&mut self) -> Result<Vec<Terminal>> {
        if self.offline || (self.cache.cache_flush_date == self.cacheflushdate) {
            Ok(self.cache.terminals.clone())
        } else {
            let now = Local::today();
            let path = format!("/terminals/{}-{}-{}", now.year(), now.month(), now.day());
            let routes: Vec<Terminal> = self.get(path)?;
            self.cache.terminals = routes.clone();
            Ok(routes)
        }
    }

    pub fn schedule(&mut self, from: i32, to: i32) -> Result<TerminalCombo> {
        let mut cache_is_stale = true;
        let cache_key = format!("{} {}", from, to);

        if self.offline || (self.cache.cache_flush_date == self.cacheflushdate) {
            if self.cache.sailings.contains_key(&cache_key) {
                // cache is up to date and has route!
                return Ok(self
                    .cache
                    .sailings
                    .get(&cache_key)
                    .expect("checked for key in cache then not found")
                    .clone());
            } else {
                // cache is up to date, but we don't have this route in it
                cache_is_stale = false;
            }
        }

        if cache_is_stale {
            self.cache.sailings.clear();
        }

        let now = Local::now();
        let path = format!(
            "/schedule/{}-{}-{}/{}/{}",
            now.year(),
            now.month(),
            now.day(),
            from,
            to
        );

        let schedule: Schedule = self.get(path)?;

        self.cache
            .sailings
            .insert(cache_key, schedule.TerminalCombos[0].clone());
        Ok(schedule.TerminalCombos[0].clone())
    }
}

#[derive(RustcDecodable, RustcEncodable, Debug)]
struct Cache {
    terminals: Vec<Terminal>,
    sailings: HashMap<String, TerminalCombo>,
    cache_flush_date: String,
}

impl Cache {
    fn empty() -> Cache {
        Cache {
            terminals: vec![],
            sailings: HashMap::new(),
            cache_flush_date: String::new(),
        }
    }

    fn load(path: &str) -> Result<Cache> {
        let mut f = File::open(path)?;
        let mut s = String::new();
        f.read_to_string(&mut s)?;
        let cache: Cache = json::decode(&s)?;
        Ok(cache)
    }
}

#[allow(non_snake_case)]
#[derive(RustcDecodable, RustcEncodable, Debug, Clone)]
pub struct Terminal {
    pub TerminalID: i32,
    pub Description: String,
}

#[allow(non_snake_case)]
#[derive(RustcDecodable, RustcEncodable, Clone, Debug)]
pub struct SailingTime {
    pub DepartingTime: String,
    pub ArrivingTime: Option<String>,
    pub VesselName: String,
}

impl SailingTime {
    // parse date strings of form "/Date(1436318400000-0700)/"
    pub fn depart_time(&self) -> DateTime<Local> {
        let re = Regex::new(r"^/Date\((\d{10})000-(\d{2})(\d{2})\)/$").unwrap();
        let caps = re.captures(&self.DepartingTime).unwrap();

        let epoch: i64 = caps.at(1).unwrap().parse().unwrap();
        let tz_hours: i32 = caps.at(2).unwrap().parse().unwrap();
        let tz_minutes: i32 = caps.at(3).unwrap().parse().unwrap();

        let nd = NaiveDateTime::from_timestamp(epoch, 0);
        let tz = FixedOffset::west((tz_hours * 3600) + (tz_minutes * 60));
        let fotz: DateTime<FixedOffset> = DateTime::from_utc(nd, tz);
        fotz.with_timezone(&Local)
    }
}

#[allow(non_snake_case)]
#[derive(RustcDecodable, RustcEncodable, Clone, Debug)]
pub struct TerminalCombo {
    pub Times: Vec<SailingTime>,
    pub DepartingTerminalName: String,
    pub ArrivingTerminalName: String,
}

#[allow(non_snake_case)]
#[derive(RustcDecodable, RustcEncodable, Debug)]
pub struct Schedule {
    pub TerminalCombos: Vec<TerminalCombo>,
}

#[derive(Debug, Fail)]
pub enum WsfError {
    #[fail(display = "Unable to configure logging: {}", _0)]
    Log(#[cause] log::SetLoggerError),

    #[fail(display = "Unable to parse WSDOT Data: {}", _0)]
    Parse(#[cause] rustc_serialize::json::DecoderError),

    #[fail(display = "Unable to save cache: {}", _0)]
    SaveCache(#[cause] rustc_serialize::json::EncoderError),

    #[fail(display = "Unable to communicate with WSDOT: {}", _0)]
    Http(#[cause] hyper::error::Error),

    #[fail(display = "Unable to read data: {}", _0)]
    Io(#[cause] std::io::Error),

    #[fail(display = "Unable to understand input: {}", _0)]
    BadInput(String),
}

impl From<rustc_serialize::json::EncoderError> for WsfError {
    fn from(err: rustc_serialize::json::EncoderError) -> WsfError {
        WsfError::SaveCache(err)
    }
}

impl From<log::SetLoggerError> for WsfError {
    fn from(err: log::SetLoggerError) -> WsfError {
        WsfError::Log(err)
    }
}

impl From<hyper::error::Error> for WsfError {
    fn from(err: hyper::error::Error) -> WsfError {
        WsfError::Http(err)
    }
}

impl From<std::io::Error> for WsfError {
    fn from(err: std::io::Error) -> WsfError {
        WsfError::Io(err)
    }
}

impl From<rustc_serialize::json::DecoderError> for WsfError {
    fn from(err: rustc_serialize::json::DecoderError) -> WsfError {
        WsfError::Parse(err)
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 - Jeremiah Peschka
//
// This file is provided to you 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.
extern crate time;
extern crate num;
extern crate byteorder;
use self::num::BigUint;
use self::byteorder::{LittleEndian, WriteBytesExt};

#[derive(Debug)]
pub enum FlakeError {
    ClockIsRunningBackwards
}

pub struct Flaker {
    identifier: [u8; 6],
    last_generated_time_ms: u64,
    counter: u16,
}

#[derive(PartialEq)]
pub enum Endianness {
    LittleEndian,
    BigEndian,
}

impl Flaker {
    /// Returns a new Flaker based on the specified identifier
    ///
    /// # Arguments
    ///
    /// * `identifier` - A 6 byte vec that provides some arbitrary identification.
    ///
    /// # Remarks
    ///
    /// This is a convenience function that converts the `identifier` `vec` into
    /// a 6 byte array. Where possible, prefer the array and use `new`.
    ///
    /// *Note*: This also assumes the `flaker` is being created on a little endian
    /// CPU.
    pub fn new_from_identifier(identifier: Vec<u8>) -> Flaker {
        let mut a_identifier: [u8; 6] = [0 as u8; 6];
        a_identifier.clone_from_slice(&identifier);
        Flaker::new(a_identifier, Endianness::LittleEndian)
    }

    /// Returns a new Flaker based on the specified identifier
    ///
    /// # Arguments
    ///
    /// * `identifier` - A 6 byte vec that provides some arbitrary identification.
    /// * `little_endian` - For specifying endianness. This is important for byte order when constructing the flake.
    pub fn new(mut identifier: [u8; 6], endian: Endianness) -> Flaker {
        if identifier.len() < 6 {
            panic!("Identifier must have a length of 6");
        }

        if endian == Endianness::BigEndian {
            identifier.reverse();
        }

        Flaker { identifier: identifier,
                last_generated_time_ms: Flaker::current_time_in_ms(),
                counter: 0
                }
    }

    /// Returns the current UNIX time in milliseconds
    fn current_time_in_ms() -> u64 {
        let now_ts = time::now_utc().to_timespec();

        // Convert current time to milliseconds by multiplying seconds by 1000
        // Convert current fractional seconds from nanoseconds to milliseconds
        // Then, get the current time as milliseconds.
        (now_ts.sec as u64 * 1000) + (now_ts.nsec as u64 / 1000 / 1000)
    }

    /// Creates a new flake ID from the identifier, current time, and an internal counter.
    /// Identifiers are generated as 128-bit numbers:
    /// * 64-bit timestamp as milliseconds since the dawn of time (January 1, 1970)
    /// * 48-bit worker identifier
    /// * 16-bit sequence number that is incremented when more than one identifier is requested in the same millisecond and reset to 0 when the clock moves forward
    fn construct_id(&mut self) -> BigUint {
        // Create a new vec of bytes
        let mut bytes = [0 as u8; 16];

        // push the counter into bytes
        bytes[0] = self.counter as u8;
        bytes[1] = (self.counter >> 8) as u8;

        // next 6 bytes are the worker id
        for (pos, byte) in self.identifier.iter().enumerate() {
            bytes[pos + 2] = *byte;
        }

        let mut wtr = vec![];

        wtr.write_u64::<LittleEndian>(self.last_generated_time_ms).unwrap();

        for (pos, w) in wtr.into_iter().enumerate() {
            bytes[pos + 8] = w;
        }

        BigUint::from_bytes_le(&bytes)
    }

    /// Update internal data structures.
    fn update(&mut self) -> Result<(), FlakeError> {
        let current_time_in_ms = Flaker::current_time_in_ms();

        if self.last_generated_time_ms > current_time_in_ms {
            println!("\t{} > {}", self.last_generated_time_ms, current_time_in_ms);
            return Result::Err(FlakeError::ClockIsRunningBackwards);
        }

        if self.last_generated_time_ms < current_time_in_ms {
            self.counter = 0;
        }
        else {
            self.counter += 1;
        }

        self.last_generated_time_ms = current_time_in_ms;

        Ok(())
    }

    /// Generate a new ID
    pub fn get_id(&mut self) -> Result<BigUint, FlakeError> {
        let update_result = self.update();

        match update_result {
            Ok(_) => Ok(self.construct_id()),
            Err(err) => Err(err),
        }
    }
}

#[test]
fn ids_change_over_time() {
    use std::time::Duration;
    use std::thread;

    let mut f1 = Flaker::new_from_identifier(vec![0, 1, 2, 3, 4, 5]);
    let id1 = f1.get_id().unwrap();
    thread::sleep(Duration::from_millis(50));
    let id2 = f1.get_id().unwrap();

    println!("{} < {}", id1, id2);

    assert!(id1 < id2);
}

#[test]
fn ids_change_quickly() {
    let mut f1 = Flaker::new([0, 1, 2, 3, 4, 5], Endianness::LittleEndian);

    let id3 = f1.get_id().unwrap();
    let id4 = f1.get_id().unwrap();

    println!("{} < {}", id3, id4);

    assert!(id3 < id4);
}<commit_msg>refactored update to use higher order functions<commit_after>// Copyright (c) 2016 - Jeremiah Peschka
//
// This file is provided to you 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.
extern crate time;
extern crate num;
extern crate byteorder;
use self::num::BigUint;
use self::byteorder::{LittleEndian, WriteBytesExt};

#[derive(Debug)]
pub enum FlakeError {
    ClockIsRunningBackwards
}

pub struct Flaker {
    identifier: [u8; 6],
    last_generated_time_ms: u64,
    counter: u16,
}

#[derive(PartialEq)]
pub enum Endianness {
    LittleEndian,
    BigEndian,
}

impl Flaker {
    /// Returns a new Flaker based on the specified identifier
    ///
    /// # Arguments
    ///
    /// * `identifier` - A 6 byte vec that provides some arbitrary identification.
    ///
    /// # Remarks
    ///
    /// This is a convenience function that converts the `identifier` `vec` into
    /// a 6 byte array. Where possible, prefer the array and use `new`.
    ///
    /// *Note*: This also assumes the `flaker` is being created on a little endian
    /// CPU.
    pub fn new_from_identifier(identifier: Vec<u8>) -> Flaker {
        let mut a_identifier: [u8; 6] = [0 as u8; 6];
        a_identifier.clone_from_slice(&identifier);
        Flaker::new(a_identifier, Endianness::LittleEndian)
    }

    /// Returns a new Flaker based on the specified identifier
    ///
    /// # Arguments
    ///
    /// * `identifier` - A 6 byte vec that provides some arbitrary identification.
    /// * `little_endian` - For specifying endianness. This is important for byte order when constructing the flake.
    pub fn new(mut identifier: [u8; 6], endian: Endianness) -> Flaker {
        if identifier.len() < 6 {
            panic!("Identifier must have a length of 6");
        }

        if endian == Endianness::BigEndian {
            identifier.reverse();
        }

        Flaker { identifier: identifier,
                last_generated_time_ms: Flaker::current_time_in_ms(),
                counter: 0
                }
    }

    /// Returns the current UNIX time in milliseconds
    fn current_time_in_ms() -> u64 {
        let now_ts = time::now_utc().to_timespec();

        // Convert current time to milliseconds by multiplying seconds by 1000
        // Convert current fractional seconds from nanoseconds to milliseconds
        // Then, get the current time as milliseconds.
        (now_ts.sec as u64 * 1000) + (now_ts.nsec as u64 / 1000 / 1000)
    }

    /// Creates a new flake ID from the identifier, current time, and an internal counter.
    /// Identifiers are generated as 128-bit numbers:
    /// * 64-bit timestamp as milliseconds since the dawn of time (January 1, 1970)
    /// * 48-bit worker identifier
    /// * 16-bit sequence number that is incremented when more than one identifier is requested in the same millisecond and reset to 0 when the clock moves forward
    fn construct_id(&mut self) -> BigUint {
        // Create a new vec of bytes
        let mut bytes = [0 as u8; 16];

        // push the counter into bytes
        bytes[0] = self.counter as u8;
        bytes[1] = (self.counter >> 8) as u8;

        // next 6 bytes are the worker id
        for (pos, byte) in self.identifier.iter().enumerate() {
            bytes[pos + 2] = *byte;
        }

        let mut wtr = vec![];

        wtr.write_u64::<LittleEndian>(self.last_generated_time_ms).unwrap();

        for (pos, w) in wtr.into_iter().enumerate() {
            bytes[pos + 8] = w;
        }

        BigUint::from_bytes_le(&bytes)
    }

    /// Update internal data structures.
    fn update(&mut self) -> Result<(), FlakeError> {
        let current_time_in_ms = Flaker::current_time_in_ms();

        if self.last_generated_time_ms > current_time_in_ms {
            return Result::Err(FlakeError::ClockIsRunningBackwards);
        }

        if self.last_generated_time_ms < current_time_in_ms {
            self.counter = 0;
        }
        else {
            self.counter += 1;
        }

        self.last_generated_time_ms = current_time_in_ms;

        Ok(())
    }

    /// Generate a new ID
    pub fn get_id(&mut self) -> Result<BigUint, FlakeError> {
        self.update().map(|_| self.construct_id())
    }
}

#[test]
fn ids_change_over_time() {
    use std::time::Duration;
    use std::thread;

    let mut f1 = Flaker::new_from_identifier(vec![0, 1, 2, 3, 4, 5]);
    let id1 = f1.get_id().unwrap();
    thread::sleep(Duration::from_millis(50));
    let id2 = f1.get_id().unwrap();

    println!("{} < {}", id1, id2);

    assert!(id1 < id2);
}

#[test]
fn ids_change_quickly() {
    let mut f1 = Flaker::new([0, 1, 2, 3, 4, 5], Endianness::LittleEndian);

    let id3 = f1.get_id().unwrap();
    let id4 = f1.get_id().unwrap();

    println!("{} < {}", id3, id4);

    assert!(id3 < id4);
}<|endoftext|>


<commit_before>#![crate_name = "neovim"]
#![crate_type = "lib"]
#![crate_type = "rlib"]
#![allow(raw_pointer_derive)]

extern crate libc;

use std::ffi::CString;
use std::fmt;

#[cfg(target_os="macos")]
mod platform {
    #[link(name = "nvim")]
    #[link(name = "uv")]
    #[link(name = "msgpack")]
    #[link(name = "termkey", kind = "static")]
    #[link(name = "unibilium", kind = "static")]
    #[link(name = "util")]
    #[link(name = "intl")]
    #[link(name = "iconv")]
    extern{}
}

#[cfg(target_os="linux")]
mod platform {
    #[link(name = "nvim")]
    #[link(name = "uv")]
    #[link(name = "msgpack")]
    #[link(name = "termkey", kind = "static")]
    #[link(name = "unibilium", kind = "static")]
    #[link(name = "util")]
    extern{}
}

mod ffi;

pub enum Object {
    Buffer(ffi::C_Buffer),
    Window(ffi::C_Window),
    Tabpage(ffi::C_Tabpage),
    Boolean(ffi::C_Boolean),
    Integer(ffi::C_Integer),
    Float(ffi::C_Float),
    String(String),
    Array(Array),
    Dictionary(ffi::C_Dictionary)
}

impl fmt::Debug for Object {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Object::Buffer(ref obj) => write!(f, "Buffer({:?})", obj),
            Object::Window(ref obj) => write!(f, "Window({:?})", obj),
            Object::Tabpage(ref obj) => write!(f, "Tabpage({:?})", obj),
            Object::Boolean(ref obj) => write!(f, "Boolean({:?})", obj),
            Object::Integer(ref obj) => write!(f, "Integer({:?})", obj),
            Object::Float(ref obj) => write!(f, "Float({:?})", obj),
            Object::String(ref obj) => write!(f, "String({:?})", obj),
            Object::Array(ref arr) => {
                write!(f, "Array(").ok();
                let obj = arr.unwrap_value();
                for i in range(0, obj.size) {
                    let inner_obj_opt = unsafe { c_object_to_object(obj.items.offset(i as isize)) };
                    if let Some(inner_obj) = inner_obj_opt {
                        write!(f, "{:?}", inner_obj).ok();
                        if i + 1 < obj.size {
                            write!(f, ", ").ok();
                        }
                    } else {
                        write!(f, "Nil ").ok();
                    }
                }
                write!(f, ")")
            },
            Object::Dictionary(ref obj) => write!(f, "Dictionary(Length: {:?})", obj.size)
        }
    }
}

unsafe fn c_object_to_object(obj: *mut ffi::C_Object) -> Option<Object> {
    match (*obj).object_type {
        ffi::ObjectType::BufferType =>
            Some(Object::Buffer((*(obj as *mut ffi::C_Object_Buffer)).data)),
        ffi::ObjectType::WindowType =>
            Some(Object::Window((*(obj as *mut ffi::C_Object_Window)).data)),
        ffi::ObjectType::TabpageType =>
            Some(Object::Tabpage((*(obj as *mut ffi::C_Object_Tabpage)).data)),
        ffi::ObjectType::NilType =>
            None,
        ffi::ObjectType::BooleanType =>
            Some(Object::Boolean((*(obj as *mut ffi::C_Object_Boolean)).data)),
        ffi::ObjectType::IntegerType =>
            Some(Object::Integer((*(obj as *mut ffi::C_Object_Integer)).data)),
        ffi::ObjectType::FloatType =>
            Some(Object::Float((*(obj as *mut ffi::C_Object_Float)).data)),
        ffi::ObjectType::StringType => {
            let vim_str: ffi::C_String = (*(obj as *mut ffi::C_Object_String)).data;
            let v = Vec::from_raw_buf(vim_str.data as *const u8, vim_str.size as usize);
            Some(Object::String(String::from_utf8_unchecked(v)))
        },
        ffi::ObjectType::ArrayType =>
            Some(Object::Array(Array::wrap_value((*(obj as *mut ffi::C_Object_Array)).data))),
        ffi::ObjectType::DictionaryType =>
            Some(Object::Dictionary((*(obj as *mut ffi::C_Object_Dictionary)).data))
    }
}

pub fn main_setup(args: &[&str]) -> i32 {
    let args_vec_cstr: Vec<CString> = args.iter().map(|s| CString::new(*s).ok().unwrap()).collect();
    let args_vec_ptr: Vec<*const ffi::c_char> = args_vec_cstr.iter().map(|s| s.as_ptr()).collect();
    unsafe { ffi::nvim_main_setup(args_vec_ptr.len() as i32, args_vec_ptr.as_ptr()) }
}

pub fn main_loop() -> i32 {
    unsafe { ffi::nvim_main_loop() }
}

pub fn channel_from_fds(read_fd: i32, write_fd: i32) -> u64 {
    unsafe { ffi::channel_from_fds(read_fd, write_fd) }
}

pub fn serialize_message(id: u64, method: &'static str, args: &Array) -> String {
    unsafe {
        let buf = ffi::vim_msgpack_new();
        let vim_str = ffi::C_String {data: method.as_ptr() as *const i8, size: method.len() as u64};
        ffi::vim_serialize_request(id, vim_str, args.unwrap_value(), buf);
        let v = Vec::from_raw_buf((*buf).data as *const u8, (*buf).size as usize);
        ffi::vim_msgpack_free(buf);
        String::from_utf8_unchecked(v)
    }
}

pub fn deserialize_message(message: &String) -> Array {
    let s = ffi::C_String {
        data: message.as_slice().as_ptr() as *const i8,
        size: message.len() as u64
    };
    let mut arr_raw = ffi::C_Array {
        items: ::std::ptr::null_mut(),
        size: 0,
        capacity: 0
    };
    unsafe { ffi::vim_msgpack_parse(s, &mut arr_raw) };
    Array::wrap_value(arr_raw)
}

pub struct Array {
    value: ffi::C_Array,
    is_owned: bool,
}

impl Array {
    pub fn new() -> Array {
        Array {
            value: ffi::C_Array {
                items: ::std::ptr::null_mut(),
                size: 0,
                capacity: 0
            },
            is_owned: true
        }
    }

    pub fn add_buffer(&mut self, val: ffi::C_Buffer) {
        unsafe { ffi::vim_array_add_buffer(val, &mut self.value) }
    }

    pub fn add_window(&mut self, val: ffi::C_Window) {
        unsafe { ffi::vim_array_add_window(val, &mut self.value) }
    }

    pub fn add_tabpage(&mut self, val: ffi::C_Tabpage) {
        unsafe { ffi::vim_array_add_tabpage(val, &mut self.value) }
    }

    pub fn add_nil(&mut self) {
        unsafe { ffi::vim_array_add_nil(&mut self.value) }
    }

    pub fn add_boolean(&mut self, val: ffi::C_Boolean) {
        unsafe { ffi::vim_array_add_boolean(val, &mut self.value) }
    }

    pub fn add_integer(&mut self, val: ffi::C_Integer) {
        unsafe { ffi::vim_array_add_integer(val, &mut self.value) }
    }

    pub fn add_float(&mut self, val: ffi::C_Float) {
        unsafe { ffi::vim_array_add_float(val, &mut self.value) }
    }

    pub fn add_string(&mut self, val: &str) {
        unsafe {
            // we need to copy the string into memory not managed by Rust,
            // so api_free_array can clear it
            let ptr = ffi::malloc(val.len() as u64);
            ::std::ptr::copy_memory(ptr, val.as_ptr() as *const ffi::c_void, val.len());
            let vim_str = ffi::C_String {data: ptr as *const i8, size: val.len() as u64};
            ffi::vim_array_add_string(vim_str, &mut self.value)
        }
    }

    pub fn add_array(&mut self, val: &Array) {
        unsafe { ffi::vim_array_add_array(val.unwrap_value(), &mut self.value) }
    }

    pub fn add_dictionary(&mut self, val: ffi::C_Dictionary) {
        unsafe { ffi::vim_array_add_dictionary(val, &mut self.value) }
    }

    pub fn get(&self, index: u64) -> Option<Object> {
        if index >= self.len() {
            return None;
        }
        unsafe { c_object_to_object(self.value.items.offset(index as isize)) }
    }

    pub fn len(&self) -> u64 {
        self.value.size
    }

    #[doc(hidden)]
    pub fn unwrap_value(&self) -> ffi::C_Array {
        self.value
    }

    #[doc(hidden)]
    pub fn wrap_value(c_array: ffi::C_Array) -> Array {
        Array {
            value: c_array,
            is_owned: false
        }
    }
}

impl Drop for Array {
    fn drop(&mut self) {
        if self.is_owned {
            unsafe { ffi::api_free_array(self.value) };
        }
    }
}

impl fmt::Debug for Array {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Array(").ok();
        for i in range(0, self.len()) {
            if let Some(obj) = self.get(i) {
                write!(f, "{:?}", obj).ok();
            } else {
                write!(f, "Nil").ok();
            }
            if i + 1 < self.len() {
                write!(f, ", ").ok();
            }
        }
        write!(f, ")")
    }
}

#[test]
fn test_request() {
    let mut args = Array::new();
    args.add_integer(80);
    args.add_integer(24);
    args.add_string("hello");

    let msg = serialize_message(1, "test", &args);
    let arr = deserialize_message(&msg);
    println!("LENGTH: {}", arr.len());
    for i in range(0, arr.len()) {
        println!("{:?}", arr.get(i));
    }
}
<commit_msg>Remove unnecessary call<commit_after>#![crate_name = "neovim"]
#![crate_type = "lib"]
#![crate_type = "rlib"]
#![allow(raw_pointer_derive)]

extern crate libc;

use std::ffi::CString;
use std::fmt;

#[cfg(target_os="macos")]
mod platform {
    #[link(name = "nvim")]
    #[link(name = "uv")]
    #[link(name = "msgpack")]
    #[link(name = "termkey", kind = "static")]
    #[link(name = "unibilium", kind = "static")]
    #[link(name = "util")]
    #[link(name = "intl")]
    #[link(name = "iconv")]
    extern{}
}

#[cfg(target_os="linux")]
mod platform {
    #[link(name = "nvim")]
    #[link(name = "uv")]
    #[link(name = "msgpack")]
    #[link(name = "termkey", kind = "static")]
    #[link(name = "unibilium", kind = "static")]
    #[link(name = "util")]
    extern{}
}

mod ffi;

pub enum Object {
    Buffer(ffi::C_Buffer),
    Window(ffi::C_Window),
    Tabpage(ffi::C_Tabpage),
    Boolean(ffi::C_Boolean),
    Integer(ffi::C_Integer),
    Float(ffi::C_Float),
    String(String),
    Array(Array),
    Dictionary(ffi::C_Dictionary)
}

impl fmt::Debug for Object {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Object::Buffer(ref obj) => write!(f, "Buffer({:?})", obj),
            Object::Window(ref obj) => write!(f, "Window({:?})", obj),
            Object::Tabpage(ref obj) => write!(f, "Tabpage({:?})", obj),
            Object::Boolean(ref obj) => write!(f, "Boolean({:?})", obj),
            Object::Integer(ref obj) => write!(f, "Integer({:?})", obj),
            Object::Float(ref obj) => write!(f, "Float({:?})", obj),
            Object::String(ref obj) => write!(f, "String({:?})", obj),
            Object::Array(ref arr) => {
                write!(f, "Array(").ok();
                let obj = arr.unwrap_value();
                for i in range(0, obj.size) {
                    let inner_obj_opt = unsafe { c_object_to_object(obj.items.offset(i as isize)) };
                    if let Some(inner_obj) = inner_obj_opt {
                        write!(f, "{:?}", inner_obj).ok();
                        if i + 1 < obj.size {
                            write!(f, ", ").ok();
                        }
                    } else {
                        write!(f, "Nil ").ok();
                    }
                }
                write!(f, ")")
            },
            Object::Dictionary(ref obj) => write!(f, "Dictionary(Length: {:?})", obj.size)
        }
    }
}

unsafe fn c_object_to_object(obj: *mut ffi::C_Object) -> Option<Object> {
    match (*obj).object_type {
        ffi::ObjectType::BufferType =>
            Some(Object::Buffer((*(obj as *mut ffi::C_Object_Buffer)).data)),
        ffi::ObjectType::WindowType =>
            Some(Object::Window((*(obj as *mut ffi::C_Object_Window)).data)),
        ffi::ObjectType::TabpageType =>
            Some(Object::Tabpage((*(obj as *mut ffi::C_Object_Tabpage)).data)),
        ffi::ObjectType::NilType =>
            None,
        ffi::ObjectType::BooleanType =>
            Some(Object::Boolean((*(obj as *mut ffi::C_Object_Boolean)).data)),
        ffi::ObjectType::IntegerType =>
            Some(Object::Integer((*(obj as *mut ffi::C_Object_Integer)).data)),
        ffi::ObjectType::FloatType =>
            Some(Object::Float((*(obj as *mut ffi::C_Object_Float)).data)),
        ffi::ObjectType::StringType => {
            let vim_str: ffi::C_String = (*(obj as *mut ffi::C_Object_String)).data;
            let v = Vec::from_raw_buf(vim_str.data as *const u8, vim_str.size as usize);
            Some(Object::String(String::from_utf8_unchecked(v)))
        },
        ffi::ObjectType::ArrayType =>
            Some(Object::Array(Array::wrap_value((*(obj as *mut ffi::C_Object_Array)).data))),
        ffi::ObjectType::DictionaryType =>
            Some(Object::Dictionary((*(obj as *mut ffi::C_Object_Dictionary)).data))
    }
}

pub fn main_setup(args: &[&str]) -> i32 {
    let args_vec_cstr: Vec<CString> = args.iter().map(|s| CString::new(*s).unwrap()).collect();
    let args_vec_ptr: Vec<*const ffi::c_char> = args_vec_cstr.iter().map(|s| s.as_ptr()).collect();
    unsafe { ffi::nvim_main_setup(args_vec_ptr.len() as i32, args_vec_ptr.as_ptr()) }
}

pub fn main_loop() -> i32 {
    unsafe { ffi::nvim_main_loop() }
}

pub fn channel_from_fds(read_fd: i32, write_fd: i32) -> u64 {
    unsafe { ffi::channel_from_fds(read_fd, write_fd) }
}

pub fn serialize_message(id: u64, method: &'static str, args: &Array) -> String {
    unsafe {
        let buf = ffi::vim_msgpack_new();
        let vim_str = ffi::C_String {data: method.as_ptr() as *const i8, size: method.len() as u64};
        ffi::vim_serialize_request(id, vim_str, args.unwrap_value(), buf);
        let v = Vec::from_raw_buf((*buf).data as *const u8, (*buf).size as usize);
        ffi::vim_msgpack_free(buf);
        String::from_utf8_unchecked(v)
    }
}

pub fn deserialize_message(message: &String) -> Array {
    let s = ffi::C_String {
        data: message.as_slice().as_ptr() as *const i8,
        size: message.len() as u64
    };
    let mut arr_raw = ffi::C_Array {
        items: ::std::ptr::null_mut(),
        size: 0,
        capacity: 0
    };
    unsafe { ffi::vim_msgpack_parse(s, &mut arr_raw) };
    Array::wrap_value(arr_raw)
}

pub struct Array {
    value: ffi::C_Array,
    is_owned: bool,
}

impl Array {
    pub fn new() -> Array {
        Array {
            value: ffi::C_Array {
                items: ::std::ptr::null_mut(),
                size: 0,
                capacity: 0
            },
            is_owned: true
        }
    }

    pub fn add_buffer(&mut self, val: ffi::C_Buffer) {
        unsafe { ffi::vim_array_add_buffer(val, &mut self.value) }
    }

    pub fn add_window(&mut self, val: ffi::C_Window) {
        unsafe { ffi::vim_array_add_window(val, &mut self.value) }
    }

    pub fn add_tabpage(&mut self, val: ffi::C_Tabpage) {
        unsafe { ffi::vim_array_add_tabpage(val, &mut self.value) }
    }

    pub fn add_nil(&mut self) {
        unsafe { ffi::vim_array_add_nil(&mut self.value) }
    }

    pub fn add_boolean(&mut self, val: ffi::C_Boolean) {
        unsafe { ffi::vim_array_add_boolean(val, &mut self.value) }
    }

    pub fn add_integer(&mut self, val: ffi::C_Integer) {
        unsafe { ffi::vim_array_add_integer(val, &mut self.value) }
    }

    pub fn add_float(&mut self, val: ffi::C_Float) {
        unsafe { ffi::vim_array_add_float(val, &mut self.value) }
    }

    pub fn add_string(&mut self, val: &str) {
        unsafe {
            // we need to copy the string into memory not managed by Rust,
            // so api_free_array can clear it
            let ptr = ffi::malloc(val.len() as u64);
            ::std::ptr::copy_memory(ptr, val.as_ptr() as *const ffi::c_void, val.len());
            let vim_str = ffi::C_String {data: ptr as *const i8, size: val.len() as u64};
            ffi::vim_array_add_string(vim_str, &mut self.value)
        }
    }

    pub fn add_array(&mut self, val: &Array) {
        unsafe { ffi::vim_array_add_array(val.unwrap_value(), &mut self.value) }
    }

    pub fn add_dictionary(&mut self, val: ffi::C_Dictionary) {
        unsafe { ffi::vim_array_add_dictionary(val, &mut self.value) }
    }

    pub fn get(&self, index: u64) -> Option<Object> {
        if index >= self.len() {
            return None;
        }
        unsafe { c_object_to_object(self.value.items.offset(index as isize)) }
    }

    pub fn len(&self) -> u64 {
        self.value.size
    }

    #[doc(hidden)]
    pub fn unwrap_value(&self) -> ffi::C_Array {
        self.value
    }

    #[doc(hidden)]
    pub fn wrap_value(c_array: ffi::C_Array) -> Array {
        Array {
            value: c_array,
            is_owned: false
        }
    }
}

impl Drop for Array {
    fn drop(&mut self) {
        if self.is_owned {
            unsafe { ffi::api_free_array(self.value) };
        }
    }
}

impl fmt::Debug for Array {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Array(").ok();
        for i in range(0, self.len()) {
            if let Some(obj) = self.get(i) {
                write!(f, "{:?}", obj).ok();
            } else {
                write!(f, "Nil").ok();
            }
            if i + 1 < self.len() {
                write!(f, ", ").ok();
            }
        }
        write!(f, ")")
    }
}

#[test]
fn test_request() {
    let mut args = Array::new();
    args.add_integer(80);
    args.add_integer(24);
    args.add_string("hello");

    let msg = serialize_message(1, "test", &args);
    let arr = deserialize_message(&msg);
    println!("LENGTH: {}", arr.len());
    for i in range(0, arr.len()) {
        println!("{:?}", arr.get(i));
    }
}
<|endoftext|>


<commit_before>extern crate bincode;
extern crate rustc_serialize;

use std::collections::HashMap;
use std::collections::hash_map::Keys;
use std::option::Option;
use std::io::*;
use std::marker::PhantomData;
use std::hash::Hash;
use std::cell::RefCell;
use rustc_serialize::{Encodable, Decodable};
use bincode::SizeLimit;
use bincode::rustc_serialize::{encode_into, encoded_size, decode};

#[derive(RustcEncodable, RustcDecodable)]
struct Entry<K, V> {
  key: K,
  value: V
}

pub struct Location {
  start: u64,
  size: usize
}

pub struct Store<K, V, F> {
  file: RefCell<F>,
  keys: HashMap<K, Location>,
  data: PhantomData<V>,
  buffer: RefCell<Option<Vec<u8>>>,
  offset: u64
}

impl<K, V, F> Store<K, V, F>
where K: Eq + Hash + Encodable + Decodable,
      V: Encodable + Decodable,
      F: Read + Write + Seek
{
  pub fn new(target: F) -> Store<K, V, F> {
    Store {
      file: RefCell::new(target),
      keys: HashMap::new(),
      data: PhantomData,
      buffer: RefCell::new(Some(Vec::new())),
      offset: 0
    }
  }

  pub fn insert(&mut self, key: K, value: V) -> Result<Option<V>> {
    let previous = try!(self.get(&key));
    let entry = Entry {
      key: key,
      value: value
    };
    let mut file = self.file.borrow_mut();
    let buf = self.buffer.borrow_mut().take().unwrap();
    let mut encoded = Cursor::new(buf);
    encode_into(&entry, &mut encoded, SizeLimit::Infinite).unwrap();
    let buf = encoded.into_inner();
    let start = self.offset;
    let size = encoded_size(&entry) as usize;
    self.offset = self.offset + size as u64;
    // TODO put buffer back on fail
    try!(file.write_all(&buf[..size]));
    self.keys.insert(entry.key, Location { start: start, size: size });
    *self.buffer.borrow_mut() = Some(buf);
    match previous {
      Some(prev) => Ok(Some(prev)),
      None => Ok(None)
    }
  }

  pub fn get(&self, key: &K) -> Result<Option<V>> {
    let location = self.keys.get(key);
    match location {
      Some(loc) => {
        let mut buffer = self.buffer.borrow_mut();
        let buf = &mut buffer.as_mut().unwrap()[..loc.size];
        let mut file = self.file.borrow_mut();
        try!(file.seek(SeekFrom::Start(loc.start)));
        try!(file.read(buf));
        try!(file.seek(SeekFrom::End(0)));
        let entry: Entry<K, V> = decode(buf).unwrap();
        Ok(Some(entry.value))
      }
      None => Ok(None)
    }
  }

  pub fn remove(&mut self, key: &K) -> Result<Option<V>> {
    let previous = try!(self.get(key));
    match previous {
      Some(prev) => {
        self.keys.remove(key);
        Ok(Some(prev))
      }
      None => Ok(None)
    }
  }

  pub fn keys<'a>(&'a self) -> Keys<'a, K, Location> {
    self.keys.keys()
  }
}

#[test]
fn insert_get() {
  #[derive(RustcEncodable, RustcDecodable, PartialEq, Debug, Clone)]
  struct Test {
    num: u64,
    string: String
  }
  let test = Test {
    num: 42,
    string: String::from("testing")
  };
  let buffer: Cursor<Vec<u8>> = Cursor::new(Vec::new());
  let mut store: Store<String, Test, Cursor<Vec<u8>>> = Store::new(buffer);
  store.insert(String::from("foo"), test.clone()).unwrap();
  let val = store.get(&String::from("foo")).unwrap().unwrap();
  assert_eq!(test, val);
}

#[test]
fn insert_remove() {
  let buffer: Cursor<Vec<u8>> = Cursor::new(Vec::new());
  let mut store: Store<String, u64, Cursor<Vec<u8>>> = Store::new(buffer);
  store.insert(String::from("foo"), 100).unwrap();
  assert_eq!(store.get(&String::from("foo")).unwrap().unwrap(), 100);
  store.remove(&String::from("foo")).unwrap();
  match store.get(&String::from("foo")).unwrap() {
    None => assert!(true),
    Some(_) => assert!(false)
  }
}

#[test]
fn keys() {
  let buffer: Cursor<Vec<u8>> = Cursor::new(Vec::new());
  let mut store: Store<String, u64, Cursor<Vec<u8>>> = Store::new(buffer);
  store.insert(String::from("foo"), 50).unwrap();
  for key in store.keys() {
    assert_eq!(*key, String::from("foo"));
  }
}<commit_msg>make test use composite key<commit_after>extern crate bincode;
extern crate rustc_serialize;

use std::collections::HashMap;
use std::collections::hash_map::Keys;
use std::option::Option;
use std::io::*;
use std::marker::PhantomData;
use std::hash::Hash;
use std::cell::RefCell;
use rustc_serialize::{Encodable, Decodable};
use bincode::SizeLimit;
use bincode::rustc_serialize::{encode_into, encoded_size, decode};

#[derive(RustcEncodable, RustcDecodable)]
struct Entry<K, V> {
  key: K,
  value: V
}

pub struct Location {
  start: u64,
  size: usize
}

pub struct Store<K, V, F> {
  file: RefCell<F>,
  keys: HashMap<K, Location>,
  data: PhantomData<V>,
  buffer: RefCell<Option<Vec<u8>>>,
  offset: u64
}

impl<K, V, F> Store<K, V, F>
where K: Eq + Hash + Encodable + Decodable,
      V: Encodable + Decodable,
      F: Read + Write + Seek
{
  pub fn new(target: F) -> Store<K, V, F> {
    Store {
      file: RefCell::new(target),
      keys: HashMap::new(),
      data: PhantomData,
      buffer: RefCell::new(Some(Vec::new())),
      offset: 0
    }
  }

  pub fn insert(&mut self, key: K, value: V) -> Result<Option<V>> {
    let previous = try!(self.get(&key));
    let entry = Entry {
      key: key,
      value: value
    };
    let mut file = self.file.borrow_mut();
    let buf = self.buffer.borrow_mut().take().unwrap();
    let mut encoded = Cursor::new(buf);
    encode_into(&entry, &mut encoded, SizeLimit::Infinite).unwrap();
    let buf = encoded.into_inner();
    let start = self.offset;
    let size = encoded_size(&entry) as usize;
    self.offset = self.offset + size as u64;
    // TODO put buffer back on fail
    try!(file.write_all(&buf[..size]));
    self.keys.insert(entry.key, Location { start: start, size: size });
    *self.buffer.borrow_mut() = Some(buf);
    match previous {
      Some(prev) => Ok(Some(prev)),
      None => Ok(None)
    }
  }

  pub fn get(&self, key: &K) -> Result<Option<V>> {
    let location = self.keys.get(key);
    match location {
      Some(loc) => {
        let mut buffer = self.buffer.borrow_mut();
        let buf = &mut buffer.as_mut().unwrap()[..loc.size];
        let mut file = self.file.borrow_mut();
        try!(file.seek(SeekFrom::Start(loc.start)));
        try!(file.read(buf));
        try!(file.seek(SeekFrom::End(0)));
        let entry: Entry<K, V> = decode(buf).unwrap();
        Ok(Some(entry.value))
      }
      None => Ok(None)
    }
  }

  pub fn remove(&mut self, key: &K) -> Result<Option<V>> {
    let previous = try!(self.get(key));
    match previous {
      Some(prev) => {
        self.keys.remove(key);
        Ok(Some(prev))
      }
      None => Ok(None)
    }
  }

  pub fn keys<'a>(&'a self) -> Keys<'a, K, Location> {
    self.keys.keys()
  }
}

#[test]
fn insert_get() {
  #[derive(RustcEncodable, RustcDecodable, PartialEq, Debug, Clone)]
  struct Test {
    num: u64,
    string: String
  }
  let test = Test {
    num: 42,
    string: String::from("testing")
  };
  let buffer: Cursor<Vec<u8>> = Cursor::new(Vec::new());
  let mut store: Store<Vec<String>, Test, Cursor<Vec<u8>>> = Store::new(buffer);
  store.insert(vec![String::from("foo"), String::from("bar")], test.clone()).unwrap();
  let val = store.get(&vec![String::from("foo"), String::from("bar")]).unwrap().unwrap();
  assert_eq!(test, val);
}

#[test]
fn insert_remove() {
  let buffer: Cursor<Vec<u8>> = Cursor::new(Vec::new());
  let mut store: Store<String, u64, Cursor<Vec<u8>>> = Store::new(buffer);
  store.insert(String::from("foo"), 100).unwrap();
  assert_eq!(store.get(&String::from("foo")).unwrap().unwrap(), 100);
  store.remove(&String::from("foo")).unwrap();
  match store.get(&String::from("foo")).unwrap() {
    None => assert!(true),
    Some(_) => assert!(false)
  }
}

#[test]
fn keys() {
  let buffer: Cursor<Vec<u8>> = Cursor::new(Vec::new());
  let mut store: Store<String, u64, Cursor<Vec<u8>>> = Store::new(buffer);
  store.insert(String::from("foo"), 50).unwrap();
  for key in store.keys() {
    assert_eq!(*key, String::from("foo"));
  }
}<|endoftext|>


<commit_before>#![crate_type = "lib"]
#![crate_name = "leveldb"]
#![feature(globs,phase)]
#![phase(syntax, link)] extern crate log;

extern crate serialize;
extern crate libc;

pub use database::options as options;
pub use database::error as error;
pub use database::iterator as iterator;

mod cbits;
pub mod database;
<commit_msg>Make the cbits public for those who want<commit_after>#![crate_type = "lib"]
#![crate_name = "leveldb"]
#![feature(globs)]

extern crate serialize;
extern crate libc;

pub use database::options as options;
pub use database::error as error;
pub use database::iterator as iterator;

pub mod cbits;
pub mod database;
<|endoftext|>


<commit_before>//! A Rust implementation of the MetroHash algorithm.
//!
//! MetroHash is a high quality, high performance hash algorithm
//!
//! # Example
//!
//! ```rust
//! #![feature(hashmap_hasher)]
//!
//! use std::collections::HashMap;
//! use std::collections::hash_state::DefaultState;
//! use metrohash::MetroHash;
//!
//! let mut hash: HashMap<_, _, DefaultState<MetroHash>> = Default::default();
//! hash.insert(1000, "1000");
//! assert_eq!(hash.get(&1000), Some(&"1000"));
//! ```

#![cfg_attr(test, feature(test))]
mod metrohash64;
mod metrohash128;
mod utils;

pub use metrohash64::*;
pub use metrohash128::*;

pub type MetroHash = MetroHash64;

#[cfg(test)]
mod tests;
<commit_msg>no need for feature flags after 1.7<commit_after>//! A Rust implementation of the MetroHash algorithm.
//!
//! MetroHash is a high quality, high performance hash algorithm
//!
//! # Example
//!
//! ```rust
//! use std::collections::HashMap;
//! use std::hash::BuildHasherDefault;
//! use metrohash::MetroHash;
//!
//! let mut hash: HashMap<_, _, BuildHasherDefault<MetroHash>> = Default::default();
//! hash.insert(1000, "1000");
//! assert_eq!(hash.get(&1000), Some(&"1000"));
//! ```

#![cfg_attr(test, feature(test))]
mod metrohash64;
mod metrohash128;
mod utils;

pub use metrohash64::*;
pub use metrohash128::*;

pub type MetroHash = MetroHash64;

#[cfg(test)]
mod tests;
<|endoftext|>


<commit_before>//! Stream abstraction for rotor
//!
//! The crate provides:
//!
//! * Buffering for network sockets
//! * Simple abstractions like read N bytes, read until '\n'
//! * Persistent (auto-reconnecting) client connections
//! * Abstraction for accepting connection on server-side
//!
//! Assumptions for streams:
//!
//! 1. You read data by length-prefixed or fixed-string-delimited chunks rather
//!    than byte-by-byte
//! 2. Each chunk fits memory
//! 3. Your data stream is not entirely full-duplex: while you can read and
//!    write simultaneously, when you apply pushback (i.e. waiting for bytes to
//!    be flushed), you can't do reads [*]
//!
//! [*] This matches HTTP perfectly, and most bidirectional interactive
//!     workflows (including based on websockets). But for some cases may be
//!     hard to implement. One such case is when you need to generate some
//!     output stream (you can't buffer it), and have to parse input stream at
//!     the same time.

extern crate netbuf;
extern crate memchr;
extern crate rotor;
#[macro_use] extern crate log;
#[macro_use] extern crate quick_error;
#[cfg(feature="replaceable")] extern crate rotor_tools;

mod substr;
mod transport;
mod protocol;
mod stream;
mod accept;
mod persistent;
mod trait_impls;
mod intention;
mod extensions;
mod errors;

pub use protocol::{Protocol, Expectation, Exception};
pub use accept::{Accepted};
pub use persistent::{Persistent};
pub use errors::ProtocolStop;

use std::any::Any;
use std::io;
use std::io::{Read, Write};
use std::error::Error;

use rotor::{Evented, Time};
use rotor::mio::{TryAccept};

pub use netbuf::{Buf, MAX_BUF_SIZE};

// Any is needed to use Stream as a Seed for Machine
pub trait StreamSocket: Read + Write + Evented + SocketError + Sized + Any {}

/// Transport is thing that provides buffered I/O for stream sockets
///
/// This is usually passed in all the `Protocol` handler methods. But in
/// case you manipulate the transport by some external methods (like the
/// one stored in `Arc<Mutex<Stream>>` you may wish to use `Stream::transport`
/// or `Persistent::transport` methods to manipulate tranpsport. Just remember
/// to **wake up** the state machine after manipulating buffers of transport.
pub struct Transport<'a, S: StreamSocket> {
    sock: &'a mut S,
    inbuf: &'a mut Buf,
    outbuf: &'a mut Buf,
}

/// Socket acceptor State Machine
///
/// TODO(tailhook) Currently this panics when there is no slab space when
/// accepting a connection. This may be fixed by sleeping and retrying
pub enum Accept<M, A: TryAccept+Sized>
    where A::Output: StreamSocket,
          M: Accepted<Socket=A::Output>,
{
    Server(A, <M as Accepted>::Seed),
    Connection(M),
}

/// A main stream state machine abstaction
///
/// You may use the `Stream` directly. But it's recommented to either use
/// `Persistent` for client connections or `Accept` for server-side
/// connection processing.
#[derive(Debug)]
pub struct Stream<P: Protocol> {
    socket: P::Socket,
    fsm: P,
    expectation: Expectation,
    connected: bool,
    deadline: Option<Time>,
    inbuf: Buf,
    outbuf: Buf,
}

struct StreamImpl<S: StreamSocket> {
    socket: S,
    connected: bool,
    inbuf: Buf,
    outbuf: Buf,
}

pub trait ActiveStream: StreamSocket {
    type Address;
    fn connect(addr: &Self::Address) -> io::Result<Self>;
}

pub trait SocketError {
    fn take_socket_error(&self) -> io::Result<()>;
}

/// A structure that encapsulates a state machine and an expectation
///
/// It's usually built with a builder that starts with `Intent::of(machine)`.
///
/// Then you add an expectation:
///
/// ```ignore
/// Intent::of(machine).expect_delimiter("\n", 1024)
/// ```
///
/// And then you may add a timeout (in form of "deadline" or "absolute time"):
///
/// ```ignore
/// Intent::of(machine).expect_bytes(100)
///     .deadline(scope.now() + Duration::new(10, 0))
/// ```
///
#[derive(Debug)]
pub struct Intent<M>(Result<M, Option<Box<Error>>>, Expectation, Option<Time>);

/// A helper class returned from `Intent::of()`
///
/// See the documentation of `Intent` for guide
#[derive(Debug)]
pub struct IntentBuilder<M>(M);
<commit_msg>Re-export `rotor_tools::sync` when `replaceable` feature is enabled<commit_after>//! Stream abstraction for rotor
//!
//! The crate provides:
//!
//! * Buffering for network sockets
//! * Simple abstractions like read N bytes, read until '\n'
//! * Persistent (auto-reconnecting) client connections
//! * Abstraction for accepting connection on server-side
//!
//! Assumptions for streams:
//!
//! 1. You read data by length-prefixed or fixed-string-delimited chunks rather
//!    than byte-by-byte
//! 2. Each chunk fits memory
//! 3. Your data stream is not entirely full-duplex: while you can read and
//!    write simultaneously, when you apply pushback (i.e. waiting for bytes to
//!    be flushed), you can't do reads [*]
//!
//! [*] This matches HTTP perfectly, and most bidirectional interactive
//!     workflows (including based on websockets). But for some cases may be
//!     hard to implement. One such case is when you need to generate some
//!     output stream (you can't buffer it), and have to parse input stream at
//!     the same time.

extern crate netbuf;
extern crate memchr;
extern crate rotor;
#[macro_use] extern crate log;
#[macro_use] extern crate quick_error;
#[cfg(feature="replaceable")] extern crate rotor_tools;

mod substr;
mod transport;
mod protocol;
mod stream;
mod accept;
mod persistent;
mod trait_impls;
mod intention;
mod extensions;
mod errors;

pub use protocol::{Protocol, Expectation, Exception};
pub use accept::{Accepted};
pub use persistent::{Persistent};
pub use errors::ProtocolStop;
#[cfg(feature="replaceable")] pub use rotor_tools::sync;

use std::any::Any;
use std::io;
use std::io::{Read, Write};
use std::error::Error;

use rotor::{Evented, Time};
use rotor::mio::{TryAccept};

pub use netbuf::{Buf, MAX_BUF_SIZE};

// Any is needed to use Stream as a Seed for Machine
pub trait StreamSocket: Read + Write + Evented + SocketError + Sized + Any {}

/// Transport is thing that provides buffered I/O for stream sockets
///
/// This is usually passed in all the `Protocol` handler methods. But in
/// case you manipulate the transport by some external methods (like the
/// one stored in `Arc<Mutex<Stream>>` you may wish to use `Stream::transport`
/// or `Persistent::transport` methods to manipulate tranpsport. Just remember
/// to **wake up** the state machine after manipulating buffers of transport.
pub struct Transport<'a, S: StreamSocket> {
    sock: &'a mut S,
    inbuf: &'a mut Buf,
    outbuf: &'a mut Buf,
}

/// Socket acceptor State Machine
///
/// TODO(tailhook) Currently this panics when there is no slab space when
/// accepting a connection. This may be fixed by sleeping and retrying
pub enum Accept<M, A: TryAccept+Sized>
    where A::Output: StreamSocket,
          M: Accepted<Socket=A::Output>,
{
    Server(A, <M as Accepted>::Seed),
    Connection(M),
}

/// A main stream state machine abstaction
///
/// You may use the `Stream` directly. But it's recommented to either use
/// `Persistent` for client connections or `Accept` for server-side
/// connection processing.
#[derive(Debug)]
pub struct Stream<P: Protocol> {
    socket: P::Socket,
    fsm: P,
    expectation: Expectation,
    connected: bool,
    deadline: Option<Time>,
    inbuf: Buf,
    outbuf: Buf,
}

struct StreamImpl<S: StreamSocket> {
    socket: S,
    connected: bool,
    inbuf: Buf,
    outbuf: Buf,
}

pub trait ActiveStream: StreamSocket {
    type Address;
    fn connect(addr: &Self::Address) -> io::Result<Self>;
}

pub trait SocketError {
    fn take_socket_error(&self) -> io::Result<()>;
}

/// A structure that encapsulates a state machine and an expectation
///
/// It's usually built with a builder that starts with `Intent::of(machine)`.
///
/// Then you add an expectation:
///
/// ```ignore
/// Intent::of(machine).expect_delimiter("\n", 1024)
/// ```
///
/// And then you may add a timeout (in form of "deadline" or "absolute time"):
///
/// ```ignore
/// Intent::of(machine).expect_bytes(100)
///     .deadline(scope.now() + Duration::new(10, 0))
/// ```
///
#[derive(Debug)]
pub struct Intent<M>(Result<M, Option<Box<Error>>>, Expectation, Option<Time>);

/// A helper class returned from `Intent::of()`
///
/// See the documentation of `Intent` for guide
#[derive(Debug)]
pub struct IntentBuilder<M>(M);
<|endoftext|>