<commit_before>#![cfg_attr(feature = "unstable", feature(fs_ext, libc, convert))]

//! xdg-basedir is a utility library to make conforming to the
//! [XDG basedir specification](http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html) easier.
//!
//! #Example
//! ```ignore
//! #![cfg(unix)]
//! extern crate xdg_basedir;
//!
//! #![cfg(unix)]
//! use xdg_basedir::*;
//! use std::path::PathBuf;
//! ...
//! let data_home: PathBuf = try!(get_data_home());
//! ...
//! ```
//!
//! Alternate implementation and some initial source borrowed from [rust-xdg](https://github.com/o11c/rust-xdg).
//! The APIs provided by ```rust-xdg``` and ```xdg-basedir``` are different.

#[cfg(feature = "unstable")]
extern crate libc;

pub mod error;

pub use error::*;

use std::convert::From;
use std::env::{self, home_dir, split_paths};
use std::ffi::OsString;
use std::path::PathBuf;

#[cfg(feature = "unstable")]
use std::fs;
#[cfg(feature = "unstable")]
use std::os::unix::fs::PermissionsExt;
#[cfg(feature = "unstable")]
use std::path::Path;

/// Get the data home directory given a closure that returns the the value of an environment variable.
/// This method allows having a custom environment.
///
/// If ```$XDG_DATA_HOME``` is not set, it returns ```$HOME/.local/share```.
pub fn get_data_home_from_env<'a, F>(get_env_var: &'a F) -> Result<PathBuf>
    where F: Fn(&'a str) -> Option<OsString>
{
    get_env_path_or_default(get_env_var, "XDG_DATA_HOME", ".local/share")
}

/// Get the data home directory.
///
/// If ```$XDG_DATA_HOME``` is not set, it returns ```$HOME/.local/share```.
pub fn get_data_home() -> Result<PathBuf> {
    get_data_home_from_env(&env::var_os)
}

/// Get the default data directories given a closure that returns the the value of an environment variable.
/// This method allows having a custom environment.
///
/// If ```$XDG_DATA_DIRS``` is not set, it returns ```[/usr/local/share, /usr/share]```.
pub fn get_data_dirs_from_env<'a, F>(get_env_var: &'a F) -> Vec<PathBuf>
    where F: Fn(&'a str) -> Option<OsString>
{
    get_env_paths_or_default(get_env_var, "XDG_DATA_DIRS", "/usr/local/share:/usr/share")
}

/// Get the data directories.
///
/// If ```$XDG_DATA_DIRS``` is not set, it returns ```[/usr/local/share, /usr/share]```.
pub fn get_data_dirs() -> Vec<PathBuf> {
    get_data_dirs_from_env(&env::var_os)
}

/// Get the config home directory given a closure that returns the the value of an environment variable.
/// This method allows having a custom environment.
///
/// If ```$XDG_CONFIG_HOME``` is not set, it returns ```$HOME/.config```.
pub fn get_config_home_from_env<'a, F>(get_env_var: &'a F) -> Result<PathBuf>
    where F: Fn(&'a str) -> Option<OsString>
{
    get_env_path_or_default(get_env_var, "XDG_CONFIG_HOME", ".config")
}
/// Get the config home directory.
///
/// If ```$XDG_CONFIG_HOME``` is not set, it returns ```$HOME/.config```.
pub fn get_config_home() -> Result<PathBuf> {
    get_config_home_from_env(&env::var_os)
}

/// Get the default config directories given a closure that returns the the value of an environment variable.
/// This method allows having a custom environment.
///
/// If ```$XDG_CONFIG_DIRS``` is not set, it returns ```[/etc/xdg]```.
pub fn get_config_dirs_from_env<'a, F>(get_env_var: &'a F) -> Vec<PathBuf>
    where F: Fn(&'a str) -> Option<OsString>
{
    get_env_paths_or_default(get_env_var, "XDG_CONFIG_DIRS", "/etc/xdg")
}

/// Get the config directories.
///
/// If ```$XDG_CONFIG_DIRS``` is not set, it returns ```[/etc/xdg]```.
pub fn get_config_dirs() -> Vec<PathBuf> {
    get_config_dirs_from_env(&env::var_os)
}

/// Get the cache home directory given a closure that returns the the value of an environment variable.
/// This method allows having a custom environment.
///
/// If ```$XDG_CACHE_HOME``` is not set, it returns ```$HOME/.cache```.
pub fn get_cache_home_from_env<'a, F>(get_env_var: &'a F) -> Result<PathBuf>
    where F: Fn(&'a str) -> Option<OsString>
{
    get_env_path_or_default(get_env_var, "XDG_CACHE_HOME", ".cache")
}

/// Get the cache home directory.
///
/// If ```$XDG_CACHE_HOME``` is not set, it returns ```$HOME/.cache```.
pub fn get_cache_home() -> Result<PathBuf> {
    get_cache_home_from_env(&env::var_os)
}

/// Get ```$XDG_RUNTIME_DIR``` if found in the environment.
/// This method allows having a custom environment.
///
/// Returns None if ```$XDG_RUNTIME_DIR``` is not set, in which case it is up to the application
/// to fallback to a location that conforms to the specification.
pub fn get_runtime_dir_from_env<'a, F>(get_env_var: &'a F) -> Option<PathBuf>
    where F: Fn(&'a str) -> Option<OsString>
{
    get_env_path(get_env_var, "XDG_RUNTIME_DIR")
}

/// Get ```$XDG_RUNTIME_DIR``` if found in the environment.
///
/// Returns None if ```$XDG_RUNTIME_DIR``` is not set, in which case it is up to the application
/// to fallback to a location that conforms to the specification.
pub fn get_runtime_dir() -> Option<PathBuf> {
    get_runtime_dir_from_env(&env::var_os)
}

/// Check that the value set for ```$XDG_RUNTIME_DIR``` is a valid path, has the correct owner and
/// permissions.
///
/// Returns Ok(()) if path is valid, owner is current uid and has permissions 0o700, or propogates any errors
/// that occurred.
///
/// >$XDG_RUNTIME_DIR defines the base directory relative to which user-specific non-essential runtime files and
/// other file objects (such as sockets, named pipes, ...) should be stored. The directory MUST be owned by the
/// user, and he MUST be the only one having read and write access to it. Its Unix access mode MUST be 0700.
/// >
/// >The lifetime of the directory MUST be bound to the user being logged in. It MUST be created when the user
/// first logs in and if the user fully logs out the directory MUST be removed. If the user logs in more than
/// once he should get pointed to the same directory, and it is mandatory that the directory continues to exist
/// from his first login to his last logout on the system, and not removed in between. Files in the directory
/// MUST not survive reboot or a full logout/login cycle.
/// >
/// >The directory MUST be on a local file system and not shared with any other system. The directory MUST by
/// fully-featured by the standards of the operating system. More specifically, on Unix-like operating systems
/// AF_UNIX sockets, symbolic links, hard links, proper permissions, file locking, sparse files, memory mapping,
/// file change notifications, a reliable hard link count must be supported, and no restrictions on the file name
/// character set should be imposed. Files in this directory MAY be subjected to periodic clean-up. To ensure that
/// your files are not removed, they should have their access time timestamp modified at least once every 6 hours
/// of monotonic time or the 'sticky' bit should be set on the file.
#[cfg(feature = "unstable")]
pub fn test_runtime_dir<P: AsRef<Path>>(path: P) -> Result<()> {
    fs::metadata(&path)
        .or_else(|e| Err(Error::from(e)))
        .map(|attr| (attr.permissions().mode()))
        .and_then(inner::check_permissions)
        .and(inner::test_dir_uid_is_current_user(path.as_ref()))
}

/// Get path from environment variable's value or a default path relative to home_dir
fn get_env_path_or_default<'a, F>(get_env_var: &'a F, env_var: &'a str, default: &'a str) -> Result<PathBuf>
    where F: Fn(&'a str) -> Option<OsString>
{
    get_env_path(get_env_var, env_var)
        .or(home_dir().map(|p| p.join(default)))
        .ok_or(Error::from(XdgError::NoHomeDir))
}

/// Get an environment variable's value as a PathBuf.
fn get_env_path<'a, F>(get_env_var: &'a F, env_var: &'a str) -> Option<PathBuf>
    where F: Fn(&'a str) -> Option<OsString>
{
    get_env_var(env_var)
        .map(PathBuf::from)
        .into_iter()
        .filter(|x| x.is_absolute())
        .next()
}

fn get_env_paths_or_default<'a, F>(get_env_var: &'a F, env_var: &'a str, default: &'a str) -> Vec<PathBuf>
    where F: Fn(&'a str) -> Option<OsString>
{
    let path_string = (*get_env_var)(env_var)
        .into_iter()
        .filter(|x| x != "")
        .next()
        .unwrap_or(OsString::from(default));

    split_paths(&path_string).collect()
}

#[cfg(feature = "unstable")]
mod inner {
    use super::*;

    use libc::funcs::posix88::stat_::stat;
    use libc::funcs::posix88::unistd::getuid;
    use libc::types::os::arch::posix01::stat as Stat;
    use std::ffi::CString;
    use std::mem;
    use std::path::Path;

    pub fn test_dir_uid_is_current_user(path: &Path) -> Result<()> {
        let p = try!(cstr(path));
        unsafe {
            let mut s: Stat = mem::zeroed();
            stat(p.as_ptr(), &mut s);

            let uid = getuid();

            match uid == s.st_uid {
                true => Ok(()),
                false => Result::from(XdgError::IncorrectOwner)
            }
        }
    }

    pub fn check_permissions(permissions: u32) -> Result<()> {
        match permissions == 0o700 {
            true => Ok(()),
            false => Result::from(XdgError::IncorrectPermissions)
        }
    }

    fn cstr(path: &Path) -> Result<CString> {
        path.as_os_str().to_cstring()
            .ok_or(Error::from(XdgError::InvalidPath))
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use std::collections::HashMap;
    use std::env::{self, home_dir, join_paths, split_paths};
    use std::ffi::OsString;
    use std::path::PathBuf;

    #[test]
    fn test_env_with_no_xdg_vars() {
        let mut custom_env = HashMap::new();
        custom_env.insert("dummy", "");

        let f = &|var: &str| { custom_env.get(var).map(OsString::from) };

        assert!(get_data_home_from_env(f).unwrap()   == home_dir().unwrap().join(".local/share"));
        assert!(get_data_dirs_from_env(f)            == vec![PathBuf::from("/usr/local/share"), PathBuf::from("/usr/share")]);
        assert!(get_config_home_from_env(f).unwrap() == home_dir().unwrap().join(".config"));
        assert!(get_config_dirs_from_env(f)          == vec![PathBuf::from("/etc/xdg")]);
        assert!(get_cache_home_from_env(f).unwrap()  == home_dir().unwrap().join(".cache"));
        assert!(get_runtime_dir_from_env(f)          == None);
    }

    #[test]
    fn test_env_with_empty_xdg_vars() {
        let mut custom_env = HashMap::new();
        custom_env.insert("XDG_DATA_HOME", "");
        custom_env.insert("XDG_DATA_DIRS", "");
        custom_env.insert("XDG_CONFIG_HOME", "");
        custom_env.insert("XDG_CONFIG_DIRS", "");
        custom_env.insert("XDG_CACHE_HOME", "");

        let f = &|var: &str| { custom_env.get(var).map(OsString::from) };

        assert!(get_data_home_from_env(f).unwrap()   == home_dir().unwrap().join(".local/share"));
        assert!(get_data_dirs_from_env(f)            == vec![PathBuf::from("/usr/local/share"), PathBuf::from("/usr/share")]);
        assert!(get_config_home_from_env(f).unwrap() == home_dir().unwrap().join(".config"));
        assert!(get_config_dirs_from_env(f)          == vec![PathBuf::from("/etc/xdg")]);
        assert!(get_cache_home_from_env(f).unwrap()  == home_dir().unwrap().join(".cache"));
        assert!(get_runtime_dir_from_env(f)          == None);
    }

    #[test]
    fn test_env_with_xdg_vars() {
        let cwd = PathBuf::from(&env::current_dir().unwrap());
        let mut custom_env = HashMap::new();

        custom_env.insert("XDG_DATA_HOME", cwd.join("user/data").into_os_string());
        custom_env.insert("XDG_DATA_DIRS", join_paths(vec![cwd.join("share/data"), cwd.join("local/data")]).unwrap());
        custom_env.insert("XDG_CONFIG_HOME", cwd.join("user/config").into_os_string());
        custom_env.insert("XDG_CONFIG_DIRS", join_paths(vec![cwd.join("config"), cwd.join("local/config")]).unwrap());
        custom_env.insert("XDG_CACHE_HOME", cwd.join("user/cache").into_os_string());

        let f = &|var: &str| { custom_env.get(var).map(OsString::from) };

        assert!(get_data_home_from_env(f).unwrap()   == custom_env.get("XDG_DATA_HOME").map(PathBuf::from).unwrap());
        assert!(get_data_dirs_from_env(f)            == split_paths(&custom_env["XDG_DATA_DIRS"]).collect::<Vec<PathBuf>>());
        assert!(get_config_home_from_env(f).unwrap() == custom_env.get("XDG_CONFIG_HOME").map(PathBuf::from).unwrap());
        assert!(get_config_dirs_from_env(f)          == split_paths(&custom_env["XDG_CONFIG_DIRS"]).collect::<Vec<PathBuf>>());
        assert!(get_cache_home_from_env(f).unwrap()  == custom_env.get("XDG_CACHE_HOME").map(PathBuf::from).unwrap());
    }
}
<commit_msg>remove unneeded fs_ext feature tag. fs_ext was stabilized<commit_after>#![cfg_attr(feature = "unstable", feature(libc, convert))]

//! xdg-basedir is a utility library to make conforming to the
//! [XDG basedir specification](http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html) easier.
//!
//! #Example
//! ```ignore
//! #![cfg(unix)]
//! extern crate xdg_basedir;
//!
//! #![cfg(unix)]
//! use xdg_basedir::*;
//! use std::path::PathBuf;
//! ...
//! let data_home: PathBuf = try!(get_data_home());
//! ...
//! ```
//!
//! Alternate implementation and some initial source borrowed from [rust-xdg](https://github.com/o11c/rust-xdg).
//! The APIs provided by ```rust-xdg``` and ```xdg-basedir``` are different.

#[cfg(feature = "unstable")]
extern crate libc;

pub mod error;

pub use error::*;

use std::convert::From;
use std::env::{self, home_dir, split_paths};
use std::ffi::OsString;
use std::path::PathBuf;

#[cfg(feature = "unstable")]
use std::fs;
#[cfg(feature = "unstable")]
use std::os::unix::fs::PermissionsExt;
#[cfg(feature = "unstable")]
use std::path::Path;

/// Get the data home directory given a closure that returns the the value of an environment variable.
/// This method allows having a custom environment.
///
/// If ```$XDG_DATA_HOME``` is not set, it returns ```$HOME/.local/share```.
pub fn get_data_home_from_env<'a, F>(get_env_var: &'a F) -> Result<PathBuf>
    where F: Fn(&'a str) -> Option<OsString>
{
    get_env_path_or_default(get_env_var, "XDG_DATA_HOME", ".local/share")
}

/// Get the data home directory.
///
/// If ```$XDG_DATA_HOME``` is not set, it returns ```$HOME/.local/share```.
pub fn get_data_home() -> Result<PathBuf> {
    get_data_home_from_env(&env::var_os)
}

/// Get the default data directories given a closure that returns the the value of an environment variable.
/// This method allows having a custom environment.
///
/// If ```$XDG_DATA_DIRS``` is not set, it returns ```[/usr/local/share, /usr/share]```.
pub fn get_data_dirs_from_env<'a, F>(get_env_var: &'a F) -> Vec<PathBuf>
    where F: Fn(&'a str) -> Option<OsString>
{
    get_env_paths_or_default(get_env_var, "XDG_DATA_DIRS", "/usr/local/share:/usr/share")
}

/// Get the data directories.
///
/// If ```$XDG_DATA_DIRS``` is not set, it returns ```[/usr/local/share, /usr/share]```.
pub fn get_data_dirs() -> Vec<PathBuf> {
    get_data_dirs_from_env(&env::var_os)
}

/// Get the config home directory given a closure that returns the the value of an environment variable.
/// This method allows having a custom environment.
///
/// If ```$XDG_CONFIG_HOME``` is not set, it returns ```$HOME/.config```.
pub fn get_config_home_from_env<'a, F>(get_env_var: &'a F) -> Result<PathBuf>
    where F: Fn(&'a str) -> Option<OsString>
{
    get_env_path_or_default(get_env_var, "XDG_CONFIG_HOME", ".config")
}
/// Get the config home directory.
///
/// If ```$XDG_CONFIG_HOME``` is not set, it returns ```$HOME/.config```.
pub fn get_config_home() -> Result<PathBuf> {
    get_config_home_from_env(&env::var_os)
}

/// Get the default config directories given a closure that returns the the value of an environment variable.
/// This method allows having a custom environment.
///
/// If ```$XDG_CONFIG_DIRS``` is not set, it returns ```[/etc/xdg]```.
pub fn get_config_dirs_from_env<'a, F>(get_env_var: &'a F) -> Vec<PathBuf>
    where F: Fn(&'a str) -> Option<OsString>
{
    get_env_paths_or_default(get_env_var, "XDG_CONFIG_DIRS", "/etc/xdg")
}

/// Get the config directories.
///
/// If ```$XDG_CONFIG_DIRS``` is not set, it returns ```[/etc/xdg]```.
pub fn get_config_dirs() -> Vec<PathBuf> {
    get_config_dirs_from_env(&env::var_os)
}

/// Get the cache home directory given a closure that returns the the value of an environment variable.
/// This method allows having a custom environment.
///
/// If ```$XDG_CACHE_HOME``` is not set, it returns ```$HOME/.cache```.
pub fn get_cache_home_from_env<'a, F>(get_env_var: &'a F) -> Result<PathBuf>
    where F: Fn(&'a str) -> Option<OsString>
{
    get_env_path_or_default(get_env_var, "XDG_CACHE_HOME", ".cache")
}

/// Get the cache home directory.
///
/// If ```$XDG_CACHE_HOME``` is not set, it returns ```$HOME/.cache```.
pub fn get_cache_home() -> Result<PathBuf> {
    get_cache_home_from_env(&env::var_os)
}

/// Get ```$XDG_RUNTIME_DIR``` if found in the environment.
/// This method allows having a custom environment.
///
/// Returns None if ```$XDG_RUNTIME_DIR``` is not set, in which case it is up to the application
/// to fallback to a location that conforms to the specification.
pub fn get_runtime_dir_from_env<'a, F>(get_env_var: &'a F) -> Option<PathBuf>
    where F: Fn(&'a str) -> Option<OsString>
{
    get_env_path(get_env_var, "XDG_RUNTIME_DIR")
}

/// Get ```$XDG_RUNTIME_DIR``` if found in the environment.
///
/// Returns None if ```$XDG_RUNTIME_DIR``` is not set, in which case it is up to the application
/// to fallback to a location that conforms to the specification.
pub fn get_runtime_dir() -> Option<PathBuf> {
    get_runtime_dir_from_env(&env::var_os)
}

/// Check that the value set for ```$XDG_RUNTIME_DIR``` is a valid path, has the correct owner and
/// permissions.
///
/// Returns Ok(()) if path is valid, owner is current uid and has permissions 0o700, or propogates any errors
/// that occurred.
///
/// >$XDG_RUNTIME_DIR defines the base directory relative to which user-specific non-essential runtime files and
/// other file objects (such as sockets, named pipes, ...) should be stored. The directory MUST be owned by the
/// user, and he MUST be the only one having read and write access to it. Its Unix access mode MUST be 0700.
/// >
/// >The lifetime of the directory MUST be bound to the user being logged in. It MUST be created when the user
/// first logs in and if the user fully logs out the directory MUST be removed. If the user logs in more than
/// once he should get pointed to the same directory, and it is mandatory that the directory continues to exist
/// from his first login to his last logout on the system, and not removed in between. Files in the directory
/// MUST not survive reboot or a full logout/login cycle.
/// >
/// >The directory MUST be on a local file system and not shared with any other system. The directory MUST by
/// fully-featured by the standards of the operating system. More specifically, on Unix-like operating systems
/// AF_UNIX sockets, symbolic links, hard links, proper permissions, file locking, sparse files, memory mapping,
/// file change notifications, a reliable hard link count must be supported, and no restrictions on the file name
/// character set should be imposed. Files in this directory MAY be subjected to periodic clean-up. To ensure that
/// your files are not removed, they should have their access time timestamp modified at least once every 6 hours
/// of monotonic time or the 'sticky' bit should be set on the file.
#[cfg(feature = "unstable")]
pub fn test_runtime_dir<P: AsRef<Path>>(path: P) -> Result<()> {
    fs::metadata(&path)
        .or_else(|e| Err(Error::from(e)))
        .map(|attr| (attr.permissions().mode()))
        .and_then(inner::check_permissions)
        .and(inner::test_dir_uid_is_current_user(path.as_ref()))
}

/// Get path from environment variable's value or a default path relative to home_dir
fn get_env_path_or_default<'a, F>(get_env_var: &'a F, env_var: &'a str, default: &'a str) -> Result<PathBuf>
    where F: Fn(&'a str) -> Option<OsString>
{
    get_env_path(get_env_var, env_var)
        .or(home_dir().map(|p| p.join(default)))
        .ok_or(Error::from(XdgError::NoHomeDir))
}

/// Get an environment variable's value as a PathBuf.
fn get_env_path<'a, F>(get_env_var: &'a F, env_var: &'a str) -> Option<PathBuf>
    where F: Fn(&'a str) -> Option<OsString>
{
    get_env_var(env_var)
        .map(PathBuf::from)
        .into_iter()
        .filter(|x| x.is_absolute())
        .next()
}

fn get_env_paths_or_default<'a, F>(get_env_var: &'a F, env_var: &'a str, default: &'a str) -> Vec<PathBuf>
    where F: Fn(&'a str) -> Option<OsString>
{
    let path_string = (*get_env_var)(env_var)
        .into_iter()
        .filter(|x| x != "")
        .next()
        .unwrap_or(OsString::from(default));

    split_paths(&path_string).collect()
}

#[cfg(feature = "unstable")]
mod inner {
    use super::*;

    use libc::funcs::posix88::stat_::stat;
    use libc::funcs::posix88::unistd::getuid;
    use libc::types::os::arch::posix01::stat as Stat;
    use std::ffi::CString;
    use std::mem;
    use std::path::Path;

    pub fn test_dir_uid_is_current_user(path: &Path) -> Result<()> {
        let p = try!(cstr(path));
        unsafe {
            let mut s: Stat = mem::zeroed();
            stat(p.as_ptr(), &mut s);

            let uid = getuid();

            match uid == s.st_uid {
                true => Ok(()),
                false => Result::from(XdgError::IncorrectOwner)
            }
        }
    }

    pub fn check_permissions(permissions: u32) -> Result<()> {
        match permissions == 0o700 {
            true => Ok(()),
            false => Result::from(XdgError::IncorrectPermissions)
        }
    }

    fn cstr(path: &Path) -> Result<CString> {
        path.as_os_str().to_cstring()
            .ok_or(Error::from(XdgError::InvalidPath))
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use std::collections::HashMap;
    use std::env::{self, home_dir, join_paths, split_paths};
    use std::ffi::OsString;
    use std::path::PathBuf;

    #[test]
    fn test_env_with_no_xdg_vars() {
        let mut custom_env = HashMap::new();
        custom_env.insert("dummy", "");

        let f = &|var: &str| { custom_env.get(var).map(OsString::from) };

        assert!(get_data_home_from_env(f).unwrap()   == home_dir().unwrap().join(".local/share"));
        assert!(get_data_dirs_from_env(f)            == vec![PathBuf::from("/usr/local/share"), PathBuf::from("/usr/share")]);
        assert!(get_config_home_from_env(f).unwrap() == home_dir().unwrap().join(".config"));
        assert!(get_config_dirs_from_env(f)          == vec![PathBuf::from("/etc/xdg")]);
        assert!(get_cache_home_from_env(f).unwrap()  == home_dir().unwrap().join(".cache"));
        assert!(get_runtime_dir_from_env(f)          == None);
    }

    #[test]
    fn test_env_with_empty_xdg_vars() {
        let mut custom_env = HashMap::new();
        custom_env.insert("XDG_DATA_HOME", "");
        custom_env.insert("XDG_DATA_DIRS", "");
        custom_env.insert("XDG_CONFIG_HOME", "");
        custom_env.insert("XDG_CONFIG_DIRS", "");
        custom_env.insert("XDG_CACHE_HOME", "");

        let f = &|var: &str| { custom_env.get(var).map(OsString::from) };

        assert!(get_data_home_from_env(f).unwrap()   == home_dir().unwrap().join(".local/share"));
        assert!(get_data_dirs_from_env(f)            == vec![PathBuf::from("/usr/local/share"), PathBuf::from("/usr/share")]);
        assert!(get_config_home_from_env(f).unwrap() == home_dir().unwrap().join(".config"));
        assert!(get_config_dirs_from_env(f)          == vec![PathBuf::from("/etc/xdg")]);
        assert!(get_cache_home_from_env(f).unwrap()  == home_dir().unwrap().join(".cache"));
        assert!(get_runtime_dir_from_env(f)          == None);
    }

    #[test]
    fn test_env_with_xdg_vars() {
        let cwd = PathBuf::from(&env::current_dir().unwrap());
        let mut custom_env = HashMap::new();

        custom_env.insert("XDG_DATA_HOME", cwd.join("user/data").into_os_string());
        custom_env.insert("XDG_DATA_DIRS", join_paths(vec![cwd.join("share/data"), cwd.join("local/data")]).unwrap());
        custom_env.insert("XDG_CONFIG_HOME", cwd.join("user/config").into_os_string());
        custom_env.insert("XDG_CONFIG_DIRS", join_paths(vec![cwd.join("config"), cwd.join("local/config")]).unwrap());
        custom_env.insert("XDG_CACHE_HOME", cwd.join("user/cache").into_os_string());

        let f = &|var: &str| { custom_env.get(var).map(OsString::from) };

        assert!(get_data_home_from_env(f).unwrap()   == custom_env.get("XDG_DATA_HOME").map(PathBuf::from).unwrap());
        assert!(get_data_dirs_from_env(f)            == split_paths(&custom_env["XDG_DATA_DIRS"]).collect::<Vec<PathBuf>>());
        assert!(get_config_home_from_env(f).unwrap() == custom_env.get("XDG_CONFIG_HOME").map(PathBuf::from).unwrap());
        assert!(get_config_dirs_from_env(f)          == split_paths(&custom_env["XDG_CONFIG_DIRS"]).collect::<Vec<PathBuf>>());
        assert!(get_cache_home_from_env(f).unwrap()  == custom_env.get("XDG_CACHE_HOME").map(PathBuf::from).unwrap());
    }
}
<|endoftext|>


<commit_before>/// A graph similarity score using neighbor matching according to [this paper][1].
///
/// [1]: http://arxiv.org/abs/1009.5290 "2010, Mladen Nikolic, Measuring Similarity
///      of Graph Nodes by Neighbor Matching"

extern crate nalgebra;
extern crate munkres;

use nalgebra::{DMat, Shape, ApproxEq};
use munkres::{WeightMatrix, solve_assignment};
use std::cmp;
use std::mem;

pub type Idx = u32;

trait Edges {
    /// The number of edges
    fn len(&self) -> usize;

    /// Returns the target node of the nth-edge
    fn nth_edge(&self, n: usize) -> usize;

    /// Returns the nth edge weight
    fn nth_edge_weight(&self, _n: usize) -> f32 {
        panic!();
    }
}

impl<'a> Edges for &'a [Idx] {
    #[inline]
    fn len(&self) -> usize {
        let x: &[Idx] = self;
        x.len()
    }
    #[inline]
    fn nth_edge(&self, n: usize) -> usize {
        self[n] as usize
    }
}

#[inline]
/// Calculates the similarity of two nodes `i` and `j`.
///
/// `n_i` contains the neighborhood of i (either in or out neighbors, not both)
/// `n_j` contains the neighborhood of j (either in or out neighbors, not both)
/// `x`   the similarity matrix.
fn s_next<T: Edges>(n_i: T, n_j: T, x: &DMat<f32>) -> f32 {
    let max_deg = cmp::max(n_i.len(), n_j.len());
    let min_deg = cmp::min(n_i.len(), n_j.len());

    if min_deg == 0 {
        // in the paper, 0/0 is defined as 1.0
        return 1.0;
    }

    assert!(min_deg > 0 && max_deg > 0);

    // map indicies from 0..min(degree) to the node indices
    let mapidx = |(a, b)| (n_i.nth_edge(a), n_j.nth_edge(b));

    let mut w = WeightMatrix::from_fn(min_deg, |ab| x[mapidx(ab)]);

    let assignment = solve_assignment(&mut w);
    assert!(assignment.len() == min_deg);

    let sum: f32 = assignment.iter().fold(0.0, |acc, &ab| acc + x[mapidx(ab)]);

    return sum / max_deg as f32;
}

#[inline]
/// Calculates x[k+1]
///
/// `node_color_scale((i,j))`: If two nodes `i` (of graph A) and `j` (of graph B)
/// are of different color, this can be set to return 0.0. Alternatively a
/// node-color distance (within 0...1) could be used to penalize.
fn next_x<F>(x: &DMat<f32>,
             new_x: &mut DMat<f32>,
             in_a: &[Vec<Idx>],
             in_b: &[Vec<Idx>],
             out_a: &[Vec<Idx>],
             out_b: &[Vec<Idx>],
             node_color_scale: F)
    where F: Fn((usize, usize)) -> f32
{
    let shape = x.shape();
    assert!(shape == new_x.shape());

    for i in 0..shape.0 {
        for j in 0..shape.1 {
            let in_i: &[Idx] = &in_a[i];
            let in_j: &[Idx] = &in_b[j];
            let out_i: &[Idx] = &out_a[i];
            let out_j: &[Idx] = &out_b[j];
            new_x[(i, j)] = node_color_scale((i, j)) *
                            (s_next(in_i, in_j, x) + s_next(out_i, out_j, x)) /
                            2.0;
        }
    }
}

#[derive(Debug)]
pub struct NodeSimilarityMatrix {
    // number of nodes in graph A
    na: usize,
    // number of nodes in graph B
    nb: usize,
    // current version of similarity matrix
    current: DMat<f32>,
    // previous version of similarity matrix
    previous: DMat<f32>,
    // current number of iterations
    num_iters: usize,
}

#[derive(Copy, Clone)]
pub struct Graph<'a> {
    in_edges: &'a [Vec<Idx>],
    out_edges: &'a [Vec<Idx>],
}

impl<'a> Graph<'a> {
    pub fn new<'b>(in_edges: &'b [Vec<Idx>], out_edges: &'b [Vec<Idx>]) -> Graph<'b> {
        assert!(in_edges.len() == out_edges.len());
        Graph {
            in_edges: in_edges,
            out_edges: out_edges,
        }
    }

    fn num_nodes(&self) -> usize {
        let n = self.in_edges.len();
        assert!(n == self.out_edges.len());
        n
    }

    fn node_degree(&self, node_idx: usize) -> usize {
        self.in_edges[node_idx].len() + self.out_edges[node_idx].len()
    }
}

impl NodeSimilarityMatrix {
    pub fn new<F>(graph_a: Graph, graph_b: Graph, node_color_scale: &F) -> NodeSimilarityMatrix
        where F: Fn((usize, usize)) -> f32
    {
        // `x` is the node-similarity matrix.
        // we initialize `x`, so that x[i,j]=1 for all i in A.edges() and j in
        // B.edges().
        let x: DMat<f32> = DMat::from_fn(graph_a.num_nodes(), graph_b.num_nodes(), |i, j| {
            node_color_scale((i, j)) *
            if graph_a.node_degree(i) > 0 && graph_b.node_degree(j) > 0 {
                1.0
            } else {
                0.0
            }
        });

        let new_x: DMat<f32> = DMat::new_zeros(graph_a.num_nodes(), graph_b.num_nodes());

        NodeSimilarityMatrix {
            na: graph_a.num_nodes(),
            nb: graph_b.num_nodes(),
            current: x,
            previous: new_x,
            num_iters: 0,
        }
    }

    fn in_eps(&self, eps: f32) -> bool {
        self.previous.approx_eq_eps(&self.current, &eps)
    }

    fn next<F>(&mut self, graph_a: Graph, graph_b: Graph, node_color_scale: &F)
        where F: Fn((usize, usize)) -> f32
    {
        next_x(&self.current,
               &mut self.previous,
               graph_a.in_edges,
               graph_b.in_edges,
               graph_a.out_edges,
               graph_b.out_edges,
               node_color_scale);
        mem::swap(&mut self.previous, &mut self.current);
        self.num_iters += 1;
    }

    #[inline]
    /// Calculates the similarity matrix for two graphs A and B.
    ///
    /// `in_a`:  Incoming edge list for each node of graph A
    /// `in_b`:  Incoming edge list for each node of graph B
    /// `out_a`: Outgoing edge list for each node of graph A
    /// `out_b`: Outgoing edge list for each node of graph B
    /// `eps`:   When to stop the iteration
    /// `stop_after_iter`: Stop after iteration (Calculate x(stop_after_iter))
    pub fn calc<F>(graph_a: Graph,
                   graph_b: Graph,
                   eps: f32,
                   stop_after_iter: usize,
                   node_color_scale: &F)
                   -> NodeSimilarityMatrix
        where F: Fn((usize, usize)) -> f32
    {
        let mut x = NodeSimilarityMatrix::new(graph_a, graph_b, node_color_scale);

        for _ in 0..stop_after_iter {
            if x.in_eps(eps) {
                break;
            }
            x.next(graph_a, graph_b, node_color_scale);
        }

        return x;
    }

    pub fn matrix(&self) -> &DMat<f32> {
        &self.current
    }

    pub fn num_iterations(&self) -> usize {
        self.num_iters
    }

    fn optimal_node_assignment(&self, n: usize) -> Vec<(usize, usize)> {
        let x = &self.current;
        assert!(n > 0);
        let mut w = WeightMatrix::from_fn(n, |ij| x[ij]);
        let assignment = solve_assignment(&mut w);
        assert!(assignment.len() == n);
        assignment
    }

    fn score_optimal_sum(&self, n: usize) -> f32 {
        self.optimal_node_assignment(n).iter().fold(0.0, |acc, &ab| acc + self.current[ab])
    }

    /// Calculate a measure how good the edge weights match up.
    ///
    /// We start by calculating the optimal node assignment between nodes of graph A and
    /// graph B, then compare all outgoing edges of similar nodes by again using an
    /// assignment.
    pub fn score_outgoing_edge_weights(&self) -> f32 {
        // XXX
        0.0
    }

    /// Sums the optimal assignment of the node similarities and normalizes (divides)
    /// by the min degree of both graphs.
    /// Used as default in the paper.
    pub fn score_sum_norm_min_degree(&self) -> f32 {
        let x = &self.current;
        let n = cmp::min(x.nrows(), x.ncols());
        if n > 0 {
            self.score_optimal_sum(n) / n as f32
        } else {
            0.0
        }
    }

    /// Sums the optimal assignment of the node similarities and normalizes (divides)
    /// by the min degree of both graphs.
    /// Penalizes the difference in size of graphs.
    pub fn score_sum_norm_max_degree(&self) -> f32 {
        let x = &self.current;
        let n = cmp::min(x.nrows(), x.ncols());
        let m = cmp::max(x.nrows(), x.ncols());

        if n > 0 {
            assert!(m > 0);
            self.score_optimal_sum(n) / m as f32
        } else {
            0.0
        }
    }

    /// Calculates the average over the whole node similarity matrix. This is faster,
    /// as no assignment has to be found. "Graphs with greater number of automorphisms
    /// would be considered to be more self-similar than graphs without automorphisms."
    pub fn score_average(&self) -> f32 {
        let x = &self.current;
        let n = cmp::min(x.nrows(), x.ncols());
        if n > 0 {
            let sum: f32 = x.as_vec().iter().fold(0.0, |acc, &v| acc + v);
            let len = x.as_vec().len();
            assert!(len > 0);
            sum / len as f32
        } else {
            0.0
        }
    }
}

#[test]
fn test_matrix() {
    // A: 0 --> 1
    let in_a = vec![vec![], vec![0]];
    let out_a = vec![vec![1], vec![]];

    // B: 0 <-- 1
    let in_b = vec![vec![1], vec![]];
    let out_b = vec![vec![], vec![0]];

    let s = NodeSimilarityMatrix::calc(Graph::new(&in_a, &out_a),
                                       Graph::new(&in_b, &out_b),
                                       0.1,
                                       100,
                                       &|_| 1.0);
    println!("{:?}", s);
    assert_eq!(1, s.num_iterations());
    let mat = s.matrix();
    assert_eq!(2, mat.nrows());
    assert_eq!(2, mat.ncols());

    // A and B are isomorphic
    assert_eq!(1.0, mat[(0, 0)]);
    assert_eq!(1.0, mat[(0, 1)]);
    assert_eq!(1.0, mat[(1, 0)]);
    assert_eq!(1.0, mat[(1, 1)]);
}

#[test]
fn test_matrix_iter1() {
    let in_a = vec![vec![0, 0, 0]];
    let out_a = vec![vec![0, 0, 0]];

    let in_b = vec![vec![0, 0, 0, 0, 0]];
    let out_b = vec![vec![0, 0, 0, 0, 0]];

    let s = NodeSimilarityMatrix::calc(Graph::new(&in_a, &out_a),
                                       Graph::new(&in_b, &out_b),
                                       0.1,
                                       1,
                                       &|_| 1.0);
    assert_eq!(1, s.num_iterations());
    let mat = s.matrix();
    assert_eq!(3.0 / 5.0, mat[(0, 0)]);
}

#[test]
fn test_score() {
    // A: 0 --> 1
    let in_a = vec![vec![], vec![0]];
    let out_a = vec![vec![1], vec![]];

    // B: 0 <-- 1
    let in_b = vec![vec![1], vec![]];
    let out_b = vec![vec![], vec![0]];

    let s = NodeSimilarityMatrix::calc(Graph::new(&in_a, &out_a),
                                       Graph::new(&in_b, &out_b),
                                       0.1,
                                       100,
                                       &|_| 1.0);

    assert_eq!(1, s.num_iterations());

    // The score is 1.0 <=> A and B are isomorphic
    assert_eq!(1.0, s.score_sum_norm_min_degree());

    // The score is 1.0 <=> A and B are isomorphic
    assert_eq!(1.0, s.score_sum_norm_max_degree());
}
<commit_msg>Store the graphs inside the NodeSimilarityMatrix<commit_after>/// A graph similarity score using neighbor matching according to [this paper][1].
///
/// [1]: http://arxiv.org/abs/1009.5290 "2010, Mladen Nikolic, Measuring Similarity
///      of Graph Nodes by Neighbor Matching"

extern crate nalgebra;
extern crate munkres;

use nalgebra::{DMat, Shape, ApproxEq};
use munkres::{WeightMatrix, solve_assignment};
use std::cmp;
use std::mem;

pub type Idx = u32;

trait Edges {
    /// The number of edges
    fn len(&self) -> usize;

    /// Returns the target node of the nth-edge
    fn nth_edge(&self, n: usize) -> usize;

    /// Returns the nth edge weight
    fn nth_edge_weight(&self, _n: usize) -> f32 {
        panic!();
    }
}

impl<'a> Edges for &'a [Idx] {
    #[inline]
    fn len(&self) -> usize {
        let x: &[Idx] = self;
        x.len()
    }
    #[inline]
    fn nth_edge(&self, n: usize) -> usize {
        self[n] as usize
    }
}

#[inline]
/// Calculates the similarity of two nodes `i` and `j`.
///
/// `n_i` contains the neighborhood of i (either in or out neighbors, not both)
/// `n_j` contains the neighborhood of j (either in or out neighbors, not both)
/// `x`   the similarity matrix.
fn s_next<T: Edges>(n_i: T, n_j: T, x: &DMat<f32>) -> f32 {
    let max_deg = cmp::max(n_i.len(), n_j.len());
    let min_deg = cmp::min(n_i.len(), n_j.len());

    if min_deg == 0 {
        // in the paper, 0/0 is defined as 1.0
        return 1.0;
    }

    assert!(min_deg > 0 && max_deg > 0);

    // map indicies from 0..min(degree) to the node indices
    let mapidx = |(a, b)| (n_i.nth_edge(a), n_j.nth_edge(b));

    let mut w = WeightMatrix::from_fn(min_deg, |ab| x[mapidx(ab)]);

    let assignment = solve_assignment(&mut w);
    assert!(assignment.len() == min_deg);

    let sum: f32 = assignment.iter().fold(0.0, |acc, &ab| acc + x[mapidx(ab)]);

    return sum / max_deg as f32;
}

#[inline]
/// Calculates x[k+1]
///
/// `node_color_scale((i,j))`: If two nodes `i` (of graph A) and `j` (of graph B)
/// are of different color, this can be set to return 0.0. Alternatively a
/// node-color distance (within 0...1) could be used to penalize.
fn next_x<F>(x: &DMat<f32>,
             new_x: &mut DMat<f32>,
             in_a: &[Vec<Idx>],
             in_b: &[Vec<Idx>],
             out_a: &[Vec<Idx>],
             out_b: &[Vec<Idx>],
             node_color_scale: F)
    where F: Fn((usize, usize)) -> f32
{
    let shape = x.shape();
    assert!(shape == new_x.shape());

    for i in 0..shape.0 {
        for j in 0..shape.1 {
            let in_i: &[Idx] = &in_a[i];
            let in_j: &[Idx] = &in_b[j];
            let out_i: &[Idx] = &out_a[i];
            let out_j: &[Idx] = &out_b[j];
            new_x[(i, j)] = node_color_scale((i, j)) *
                            (s_next(in_i, in_j, x) + s_next(out_i, out_j, x)) /
                            2.0;
        }
    }
}

#[derive(Debug)]
pub struct NodeSimilarityMatrix<'a> {
    graph_a: Graph<'a>,
    graph_b: Graph<'a>,
    // current version of similarity matrix
    current: DMat<f32>,
    // previous version of similarity matrix
    previous: DMat<f32>,
    // current number of iterations
    num_iters: usize,
}

#[derive(Copy, Clone, Debug)]
pub struct Graph<'a> {
    in_edges: &'a [Vec<Idx>],
    out_edges: &'a [Vec<Idx>],
}

impl<'a> Graph<'a> {
    pub fn new<'b>(in_edges: &'b [Vec<Idx>], out_edges: &'b [Vec<Idx>]) -> Graph<'b> {
        assert!(in_edges.len() == out_edges.len());
        Graph {
            in_edges: in_edges,
            out_edges: out_edges,
        }
    }

    fn num_nodes(&self) -> usize {
        let n = self.in_edges.len();
        assert!(n == self.out_edges.len());
        n
    }

    fn node_degree(&self, node_idx: usize) -> usize {
        self.in_edges[node_idx].len() + self.out_edges[node_idx].len()
    }
}

impl<'a> NodeSimilarityMatrix<'a> {
    pub fn new<'b, F>(graph_a: Graph<'b>,
                      graph_b: Graph<'b>,
                      node_color_scale: &F)
                      -> NodeSimilarityMatrix<'b>
        where F: Fn((usize, usize)) -> f32
    {
        // `x` is the node-similarity matrix.
        // we initialize `x`, so that x[i,j]=1 for all i in A.edges() and j in
        // B.edges().
        let x: DMat<f32> = DMat::from_fn(graph_a.num_nodes(), graph_b.num_nodes(), |i, j| {
            node_color_scale((i, j)) *
            if graph_a.node_degree(i) > 0 && graph_b.node_degree(j) > 0 {
                1.0
            } else {
                0.0
            }
        });

        let new_x: DMat<f32> = DMat::new_zeros(graph_a.num_nodes(), graph_b.num_nodes());

        NodeSimilarityMatrix {
            graph_a: graph_a,
            graph_b: graph_b,
            current: x,
            previous: new_x,
            num_iters: 0,
        }
    }

    fn in_eps(&self, eps: f32) -> bool {
        self.previous.approx_eq_eps(&self.current, &eps)
    }

    /// Calculates the next iteration of the similarity matrix.
    pub fn next<F>(&mut self, node_color_scale: &F)
        where F: Fn((usize, usize)) -> f32
    {
        next_x(&self.current,
               &mut self.previous,
               self.graph_a.in_edges,
               self.graph_b.in_edges,
               self.graph_a.out_edges,
               self.graph_b.out_edges,
               node_color_scale);
        mem::swap(&mut self.previous, &mut self.current);
        self.num_iters += 1;
    }

    #[inline]
    /// Iteratively calculate the similarity matrix.
    ///
    /// `eps`:   When to stop the iteration
    /// `stop_after_iter`: Stop after iteration (Calculate x(stop_after_iter))
    pub fn iterate<F>(&mut self, eps: f32, stop_after_iter: usize, node_color_scale: &F)
        where F: Fn((usize, usize)) -> f32
    {
        for _ in 0..stop_after_iter {
            if self.in_eps(eps) {
                break;
            }
            self.next(node_color_scale);
        }
    }

    pub fn matrix(&self) -> &DMat<f32> {
        &self.current
    }

    pub fn num_iterations(&self) -> usize {
        self.num_iters
    }

    fn optimal_node_assignment(&self, n: usize) -> Vec<(usize, usize)> {
        let x = &self.current;
        assert!(n > 0);
        let mut w = WeightMatrix::from_fn(n, |ij| x[ij]);
        let assignment = solve_assignment(&mut w);
        assert!(assignment.len() == n);
        assignment
    }

    fn score_optimal_sum(&self, n: usize) -> f32 {
        self.optimal_node_assignment(n).iter().fold(0.0, |acc, &ab| acc + self.current[ab])
    }

    /// Calculate a measure how good the edge weights match up.
    ///
    /// We start by calculating the optimal node assignment between nodes of graph A and
    /// graph B, then compare all outgoing edges of similar nodes by again using an
    /// assignment.
    pub fn score_outgoing_edge_weights(&self) -> f32 {
        // XXX
        0.0
    }

    /// Sums the optimal assignment of the node similarities and normalizes (divides)
    /// by the min degree of both graphs.
    /// Used as default in the paper.
    pub fn score_sum_norm_min_degree(&self) -> f32 {
        let x = &self.current;
        let n = cmp::min(x.nrows(), x.ncols());
        if n > 0 {
            self.score_optimal_sum(n) / n as f32
        } else {
            0.0
        }
    }

    /// Sums the optimal assignment of the node similarities and normalizes (divides)
    /// by the min degree of both graphs.
    /// Penalizes the difference in size of graphs.
    pub fn score_sum_norm_max_degree(&self) -> f32 {
        let x = &self.current;
        let n = cmp::min(x.nrows(), x.ncols());
        let m = cmp::max(x.nrows(), x.ncols());

        if n > 0 {
            assert!(m > 0);
            self.score_optimal_sum(n) / m as f32
        } else {
            0.0
        }
    }

    /// Calculates the average over the whole node similarity matrix. This is faster,
    /// as no assignment has to be found. "Graphs with greater number of automorphisms
    /// would be considered to be more self-similar than graphs without automorphisms."
    pub fn score_average(&self) -> f32 {
        let x = &self.current;
        let n = cmp::min(x.nrows(), x.ncols());
        if n > 0 {
            let sum: f32 = x.as_vec().iter().fold(0.0, |acc, &v| acc + v);
            let len = x.as_vec().len();
            assert!(len > 0);
            sum / len as f32
        } else {
            0.0
        }
    }
}

#[test]
fn test_matrix() {
    // A: 0 --> 1
    let in_a = vec![vec![], vec![0]];
    let out_a = vec![vec![1], vec![]];

    // B: 0 <-- 1
    let in_b = vec![vec![1], vec![]];
    let out_b = vec![vec![], vec![0]];

    let node_color = |_| 1.0;
    let mut s = NodeSimilarityMatrix::new(Graph::new(&in_a, &out_a),
                                          Graph::new(&in_b, &out_b),
                                          &node_color);
    s.iterate(0.1, 100, &node_color);

    println!("{:?}", s);
    assert_eq!(1, s.num_iterations());
    let mat = s.matrix();
    assert_eq!(2, mat.nrows());
    assert_eq!(2, mat.ncols());

    // A and B are isomorphic
    assert_eq!(1.0, mat[(0, 0)]);
    assert_eq!(1.0, mat[(0, 1)]);
    assert_eq!(1.0, mat[(1, 0)]);
    assert_eq!(1.0, mat[(1, 1)]);
}

#[test]
fn test_matrix_iter1() {
    let in_a = vec![vec![0, 0, 0]];
    let out_a = vec![vec![0, 0, 0]];

    let in_b = vec![vec![0, 0, 0, 0, 0]];
    let out_b = vec![vec![0, 0, 0, 0, 0]];

    let node_color = |_| 1.0;
    let mut s = NodeSimilarityMatrix::new(Graph::new(&in_a, &out_a),
                                          Graph::new(&in_b, &out_b),
                                          &node_color);
    s.iterate(0.1, 1, &node_color);

    assert_eq!(1, s.num_iterations());
    let mat = s.matrix();
    assert_eq!(3.0 / 5.0, mat[(0, 0)]);
}

#[test]
fn test_score() {
    // A: 0 --> 1
    let in_a = vec![vec![], vec![0]];
    let out_a = vec![vec![1], vec![]];

    // B: 0 <-- 1
    let in_b = vec![vec![1], vec![]];
    let out_b = vec![vec![], vec![0]];

    let node_color = |_| 1.0;
    let mut s = NodeSimilarityMatrix::new(Graph::new(&in_a, &out_a),
                                          Graph::new(&in_b, &out_b),
                                          &node_color);
    s.iterate(0.1, 100, &node_color);

    assert_eq!(1, s.num_iterations());

    // The score is 1.0 <=> A and B are isomorphic
    assert_eq!(1.0, s.score_sum_norm_min_degree());

    // The score is 1.0 <=> A and B are isomorphic
    assert_eq!(1.0, s.score_sum_norm_max_degree());
}
<|endoftext|>


<commit_before>//! [![github]](https://github.com/dtolnay/serde-yaml) [![crates-io]](https://crates.io/crates/serde-yaml) [![docs-rs]](https://docs.rs/serde-yaml)
//!
//! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
//! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust
//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logoColor=white&logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K
//!
//!

//!
//! This crate is a Rust library for using the [Serde] serialization framework
//! with data in [YAML] file format.
//!
//! This library does not reimplement a YAML parser; it uses [yaml-rust] which
//! is a pure Rust YAML 1.2 implementation.
//!
//! [Serde]: https://github.com/serde-rs/serde
//! [YAML]: https://yaml.org/
//! [yaml-rust]: https://github.com/chyh1990/yaml-rust
//!
//! # Examples
//!
//! ```
//! use std::collections::BTreeMap;
//!
//! fn main() -> Result<(), serde_yaml::Error> {
//!     // You have some type.
//!     let mut map = BTreeMap::new();
//!     map.insert("x".to_string(), 1.0);
//!     map.insert("y".to_string(), 2.0);
//!
//!     // Serialize it to a YAML string.
//!     let s = serde_yaml::to_string(&map)?;
//!     assert_eq!(s, "---\nx: 1.0\ny: 2.0\n");
//!
//!     // Deserialize it back to a Rust type.
//!     let deserialized_map: BTreeMap<String, f64> = serde_yaml::from_str(&s)?;
//!     assert_eq!(map, deserialized_map);
//!     Ok(())
//! }
//! ```
//!
//! ## Using Serde derive
//!
//! It can also be used with Serde's serialization code generator `serde_derive` to
//! handle structs and enums defined in your own program.
//!
//! ```
//! # use serde_derive::{Serialize, Deserialize};
//! use serde::{Serialize, Deserialize};
//!
//! #[derive(Debug, PartialEq, Serialize, Deserialize)]
//! struct Point {
//!     x: f64,
//!     y: f64,
//! }
//!
//! fn main() -> Result<(), serde_yaml::Error> {
//!     let point = Point { x: 1.0, y: 2.0 };
//!
//!     let s = serde_yaml::to_string(&point)?;
//!     assert_eq!(s, "---\nx: 1.0\ny: 2.0\n");
//!
//!     let deserialized_point: Point = serde_yaml::from_str(&s)?;
//!     assert_eq!(point, deserialized_point);
//!     Ok(())
//! }
//! ```

#![doc(html_root_url = "https://docs.rs/serde_yaml/0.8.23")]
#![deny(missing_docs)]
// Suppressed clippy_pedantic lints
#![allow(
    // buggy
    clippy::question_mark, // https://github.com/rust-lang/rust-clippy/issues/7859
    // private Deserializer::next
    clippy::should_implement_trait,
    // things are often more readable this way
    clippy::cast_lossless,
    clippy::if_not_else,
    clippy::manual_assert,
    clippy::match_same_arms,
    clippy::module_name_repetitions,
    clippy::needless_pass_by_value,
    clippy::option_if_let_else,
    clippy::redundant_else,
    clippy::single_match_else,
    // code is acceptable
    clippy::blocks_in_if_conditions,
    clippy::cast_possible_wrap,
    clippy::cast_precision_loss,
    clippy::doc_markdown,
    clippy::items_after_statements,
    // noisy
    clippy::missing_errors_doc,
    clippy::must_use_candidate,
)]

pub use crate::de::{from_reader, from_slice, from_str, Deserializer};
pub use crate::error::{Error, Location, Result};
pub use crate::ser::{to_string, to_vec, to_writer, Serializer};
pub use crate::value::{from_value, to_value, Index, Number, Sequence, Value};

#[doc(inline)]
pub use crate::mapping::Mapping;

/// Entry points for deserializing with pre-existing state.
///
/// These functions are only exposed this way because we don't yet expose a
/// Deserializer type. Data formats that have a public Deserializer should not
/// copy these signatures.
pub mod seed {
    pub use super::de::{from_reader_seed, from_slice_seed, from_str_seed};
}

mod de;
mod error;
pub mod mapping;
mod number;
mod path;
mod ser;
mod value;
<commit_msg>Ignore return_self_not_must_use clippy lint<commit_after>//! [![github]](https://github.com/dtolnay/serde-yaml) [![crates-io]](https://crates.io/crates/serde-yaml) [![docs-rs]](https://docs.rs/serde-yaml)
//!
//! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
//! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust
//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logoColor=white&logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K
//!
//!

//!
//! This crate is a Rust library for using the [Serde] serialization framework
//! with data in [YAML] file format.
//!
//! This library does not reimplement a YAML parser; it uses [yaml-rust] which
//! is a pure Rust YAML 1.2 implementation.
//!
//! [Serde]: https://github.com/serde-rs/serde
//! [YAML]: https://yaml.org/
//! [yaml-rust]: https://github.com/chyh1990/yaml-rust
//!
//! # Examples
//!
//! ```
//! use std::collections::BTreeMap;
//!
//! fn main() -> Result<(), serde_yaml::Error> {
//!     // You have some type.
//!     let mut map = BTreeMap::new();
//!     map.insert("x".to_string(), 1.0);
//!     map.insert("y".to_string(), 2.0);
//!
//!     // Serialize it to a YAML string.
//!     let s = serde_yaml::to_string(&map)?;
//!     assert_eq!(s, "---\nx: 1.0\ny: 2.0\n");
//!
//!     // Deserialize it back to a Rust type.
//!     let deserialized_map: BTreeMap<String, f64> = serde_yaml::from_str(&s)?;
//!     assert_eq!(map, deserialized_map);
//!     Ok(())
//! }
//! ```
//!
//! ## Using Serde derive
//!
//! It can also be used with Serde's serialization code generator `serde_derive` to
//! handle structs and enums defined in your own program.
//!
//! ```
//! # use serde_derive::{Serialize, Deserialize};
//! use serde::{Serialize, Deserialize};
//!
//! #[derive(Debug, PartialEq, Serialize, Deserialize)]
//! struct Point {
//!     x: f64,
//!     y: f64,
//! }
//!
//! fn main() -> Result<(), serde_yaml::Error> {
//!     let point = Point { x: 1.0, y: 2.0 };
//!
//!     let s = serde_yaml::to_string(&point)?;
//!     assert_eq!(s, "---\nx: 1.0\ny: 2.0\n");
//!
//!     let deserialized_point: Point = serde_yaml::from_str(&s)?;
//!     assert_eq!(point, deserialized_point);
//!     Ok(())
//! }
//! ```

#![doc(html_root_url = "https://docs.rs/serde_yaml/0.8.23")]
#![deny(missing_docs)]
// Suppressed clippy_pedantic lints
#![allow(
    // buggy
    clippy::question_mark, // https://github.com/rust-lang/rust-clippy/issues/7859
    // private Deserializer::next
    clippy::should_implement_trait,
    // things are often more readable this way
    clippy::cast_lossless,
    clippy::if_not_else,
    clippy::manual_assert,
    clippy::match_same_arms,
    clippy::module_name_repetitions,
    clippy::needless_pass_by_value,
    clippy::option_if_let_else,
    clippy::redundant_else,
    clippy::single_match_else,
    // code is acceptable
    clippy::blocks_in_if_conditions,
    clippy::cast_possible_wrap,
    clippy::cast_precision_loss,
    clippy::doc_markdown,
    clippy::items_after_statements,
    clippy::return_self_not_must_use,
    // noisy
    clippy::missing_errors_doc,
    clippy::must_use_candidate,
)]

pub use crate::de::{from_reader, from_slice, from_str, Deserializer};
pub use crate::error::{Error, Location, Result};
pub use crate::ser::{to_string, to_vec, to_writer, Serializer};
pub use crate::value::{from_value, to_value, Index, Number, Sequence, Value};

#[doc(inline)]
pub use crate::mapping::Mapping;

/// Entry points for deserializing with pre-existing state.
///
/// These functions are only exposed this way because we don't yet expose a
/// Deserializer type. Data formats that have a public Deserializer should not
/// copy these signatures.
pub mod seed {
    pub use super::de::{from_reader_seed, from_slice_seed, from_str_seed};
}

mod de;
mod error;
pub mod mapping;
mod number;
mod path;
mod ser;
mod value;
<|endoftext|>


<commit_before>extern crate hyper;
extern crate serde_json;

use std::io::Read;

use hyper::Client;
use hyper::header::Connection;

pub fn test_api() {
    println!("Hello, Stockfighter!");

    // Create a client.
    let client = Client::new();

    // Creating an outgoing request.
    let mut res = client.get("http://api.stockfighter.io/ob/api/heartbeat")
        // set a header
        .header(Connection::close())
        // let 'er go!
        .send().unwrap();

    // Read the Response.
    let mut body = String::new();
    res.read_to_string(&mut body).unwrap();

    let value: serde_json::Value = serde_json::from_str(&body).unwrap();
    println!("Response: {}", value.as_object().unwrap().get("ok").unwrap().as_boolean().unwrap());
}

pub fn test_venue(venue: &str) {
    // Create a client.
    let client = Client::new();

    let url = format!("https://api.stockfighter.io/ob/api/venues/{}/heartbeat", venue);

    // Creating an outgoing request.
    let mut res = client.get(&url)
        // set a header
        .header(Connection::close())
        // let 'er go!
        .send().unwrap();

    // Read the Response.
    let mut body = String::new();
    res.read_to_string(&mut body).unwrap();

    let value: serde_json::Value = serde_json::from_str(&body).unwrap();
    println!("Response: {}", value.as_object().unwrap().get("ok").unwrap().as_boolean().unwrap());
}
<commit_msg>Refactor<commit_after>extern crate hyper;
extern crate serde_json;

use std::io::Read;

use hyper::Client;
use hyper::header::Connection;

fn get<U: hyper::client::IntoUrl>(url: U) -> serde_json::Value {
    // Create a client.
    let client = Client::new();

    // Creating an outgoing request.
    let mut res = client.get(url)
        // set a header
        .header(Connection::close())
        // let 'er go!
        .send().unwrap();

    // Read the Response.
    let mut body = String::new();
    res.read_to_string(&mut body).unwrap();

    let value: serde_json::Value = serde_json::from_str(&body).unwrap();
    value
}

pub fn test_api() {
    let value = get("http://api.stockfighter.io/ob/api/heartbeat");
    println!("Response: {}", value.as_object().unwrap().get("ok").unwrap().as_boolean().unwrap());
}

pub fn test_venue(venue: &str) {
    let url = format!("https://api.stockfighter.io/ob/api/venues/{}/heartbeat", venue);
    let value = get(&url);
    println!("Response: {}", value.as_object().unwrap().get("ok").unwrap().as_boolean().unwrap());
}
<|endoftext|>


<commit_before>//!
//! Rust wrapper around freetype 2 library
//!
//! # Initialization
//!
//! To create a new freetype context, instantiate the Library struct as below.
//! The Library (along with other objects) obeys RAII and is dropped when the struct goes out of
//! scope.
//!
//! # Example
//!
//! ```
//! extern crate freetype;
//!
//! fn main() {
//!     use freetype::Library;
//!
//!     // Init the library
//!     let lib = Library::init().unwrap();
//!     // Load a font face
//!     let face = lib.new_face("/path/to/a/font/file.ttf", 0).unwrap();
//!     // Set the font size
//!     face.set_char_size(40 * 64, 0, 50, 0).unwrap();
//!     // Load a character
//!     face.load_char('A').unwrap();
//!     // Get the glyph instance
//!     let glyph = face.glyph();
//!     do_something_with_bitmap(glyph.bitmap().buffer());
//! }
//! ```
//!
//! See in the `examples/` folder for more examples.
//!
//! # External links
//! - See [freetype docs](http://www.freetype.org/freetype2/docs/reference/ft2-index.html)
//!   for more information

#![deny(missing_copy_implementations)]

#[macro_use]
extern crate bitflags;
extern crate libc;
pub extern crate freetype_sys;

pub use bitmap::Bitmap;
pub use bitmap_glyph::BitmapGlyph;
pub use error::{ FtResult, Error };
pub use face::Face;
pub use glyph::Glyph;
pub use glyph_slot::GlyphSlot;
pub use library::Library;
pub use outline::Outline;
pub use render_mode::RenderMode;
pub use freetype_sys as ffi;

pub mod bitmap;
pub mod bitmap_glyph;
pub mod error;
pub mod face;
pub mod glyph;
pub mod glyph_slot;
pub mod library;
pub mod outline;
pub mod render_mode;
pub mod tt_os2;

pub type BBox = ffi::FT_BBox;
pub type GlyphMetrics = ffi::FT_Glyph_Metrics;
pub type Matrix = ffi::FT_Matrix;
pub type Vector = ffi::FT_Vector;
<commit_msg>Don't doctest example as it contains a made up function<commit_after>//!
//! Rust wrapper around freetype 2 library
//!
//! # Initialization
//!
//! To create a new freetype context, instantiate the Library struct as below.
//! The Library (along with other objects) obeys RAII and is dropped when the struct goes out of
//! scope.
//!
//! # Example
//!
//! ```no_run
//! extern crate freetype;
//!
//! fn main() {
//!     use freetype::Library;
//!
//!     // Init the library
//!     let lib = Library::init().unwrap();
//!     // Load a font face
//!     let face = lib.new_face("/path/to/a/font/file.ttf", 0).unwrap();
//!     // Set the font size
//!     face.set_char_size(40 * 64, 0, 50, 0).unwrap();
//!     // Load a character
//!     face.load_char('A').unwrap();
//!     // Get the glyph instance
//!     let glyph = face.glyph();
//!     do_something_with_bitmap(glyph.bitmap().buffer());
//! }
//! ```
//!
//! See in the `examples/` folder for more examples.
//!
//! # External links
//! - See [freetype docs](http://www.freetype.org/freetype2/docs/reference/ft2-index.html)
//!   for more information

#![deny(missing_copy_implementations)]

#[macro_use]
extern crate bitflags;
extern crate libc;
pub extern crate freetype_sys;

pub use bitmap::Bitmap;
pub use bitmap_glyph::BitmapGlyph;
pub use error::{ FtResult, Error };
pub use face::Face;
pub use glyph::Glyph;
pub use glyph_slot::GlyphSlot;
pub use library::Library;
pub use outline::Outline;
pub use render_mode::RenderMode;
pub use freetype_sys as ffi;

pub mod bitmap;
pub mod bitmap_glyph;
pub mod error;
pub mod face;
pub mod glyph;
pub mod glyph_slot;
pub mod library;
pub mod outline;
pub mod render_mode;
pub mod tt_os2;

pub type BBox = ffi::FT_BBox;
pub type GlyphMetrics = ffi::FT_Glyph_Metrics;
pub type Matrix = ffi::FT_Matrix;
pub type Vector = ffi::FT_Vector;
<|endoftext|>


<commit_before>//! A parser for the DWARF debugging information format.
//!
//! This library targets the fourth edition of the standard (the most recent, at
//! time of writing).
//!
//! TODO FITZGEN: example usage here!

#![deny(missing_docs)]

extern crate byteorder;
extern crate leb128;

mod constants;
pub use constants::*;

mod endianity;
pub use endianity::{Endianity, LittleEndian, BigEndian};

mod parser;
pub use parser::{Error, ParseResult, Format};
pub use parser::{DebugStrOffset, DebugLineOffset, DebugLocOffset, DebugMacinfoOffset, UnitOffset};
pub use parser::{DebugInfo, DebugInfoOffset, UnitHeadersIter, UnitHeader};
pub use parser::{DebugTypes, DebugTypesOffset, TypeUnitHeadersIter, TypeUnitHeader};
pub use parser::{EntriesCursor, DebuggingInformationEntry, AttrsIter, Attribute, AttributeValue};

mod abbrev;
pub use abbrev::{DebugAbbrev, DebugAbbrevOffset, Abbreviations, Abbreviation,
                 AttributeSpecification};
<commit_msg>Add example usage to the top level crate documentation<commit_after>//! A lazy, zero-copy parser for the DWARF debugging information format.
//!
//! * Zero-copy: everything is just a reference to the original input buffer. No
//!   copies of the input data ever get made.
//!
//! * Lazy: only the compilation units' entries that you iterate over get
//!   parsed, and only as deep as you ask. Skip over a compilation unit and its
//!   entries don't get parsed.
//!
//! * Bring your own object file parser: `gimli` isn't coupled to any platform
//!   or object file format. Use your own ELF parser on Linux or a Mach-O parser
//!   on OSX.
//!
//! This library primarily targets the fourth edition of the standard (the most
//! recent, at time of writing).
//!
//! ## Example Usage
//!
//! Print out all of the functions in a compilation unit.
//!
//! ```rust,no_run
//! use gimli;
//!
//! # let debug_info_buf = [];
//! # let debug_abbrev_buf = [];
//! # let read_debug_info = || &debug_info_buf;
//! # let read_debug_abbrev = || &debug_abbrev_buf;
//! // Read the .debug_info and .debug_abbrev sections with whatever object
//! // loader you're using.
//! let debug_info = gimli::DebugInfo::<gimli::LittleEndian>::new(read_debug_info());
//! let debug_abbrev = gimli::DebugAbbrev::<gimli::LittleEndian>::new(read_debug_abbrev());
//!
//! // Grab the first compilation unit.
//! let unit = debug_info.units().next()
//!     .expect("Should have at least one unit")
//!     .expect("and it should parse OK");
//!
//! // Parse the abbreviations for this compilation unit.
//! let abbrevs = unit.abbreviations(debug_abbrev)
//!     .expect("Should parse the abbreviations OK");
//!
//! // Get a cursor for iterating over this unit's entries.
//! let mut entries = unit.entries(&abbrevs);
//!
//! // Keep iterating entries while the cursor is not exhausted.
//! while let Some(entry) = entries.current() {
//!     let entry = entry.expect("Should parse the entry OK");
//!
//!     // If we find an entry for a function, print it.
//!     if entry.tag() == gimli::DW_TAG_subprogram {
//!         println!("Found a function: {:?}", entry);
//!     }
//!
//!     // Advance the cursor to the next entry, in DFS order.
//!     entries.next_dfs();
//! }
//! ```
//!
//! See the
//! [`examples/dwarfdump.rs`](https://github.com/fitzgen/gimli/blob/master/examples/dwarfdump.rs)
//! program for a complete example program.

#![deny(missing_docs)]

extern crate byteorder;
extern crate leb128;

mod constants;
pub use constants::*;

mod endianity;
pub use endianity::{Endianity, LittleEndian, BigEndian};

mod parser;
pub use parser::{Error, ParseResult, Format};
pub use parser::{DebugStrOffset, DebugLineOffset, DebugLocOffset, DebugMacinfoOffset, UnitOffset};
pub use parser::{DebugInfo, DebugInfoOffset, UnitHeadersIter, UnitHeader};
pub use parser::{DebugTypes, DebugTypesOffset, TypeUnitHeadersIter, TypeUnitHeader};
pub use parser::{EntriesCursor, DebuggingInformationEntry, AttrsIter, Attribute, AttributeValue};

mod abbrev;
pub use abbrev::{DebugAbbrev, DebugAbbrevOffset, Abbreviations, Abbreviation,
                 AttributeSpecification};
<|endoftext|>


<commit_before>extern crate hyper;
extern crate url;
extern crate json_flex;

use std::io::Read;

use hyper::Client;
use hyper::header::Connection;
use hyper::header::ContentType;
use url::form_urlencoded;
use json_flex::{JFObject, Unwrap};

pub struct HTTPRequest {
    client: Client,
}

impl HTTPRequest {
    pub fn new() -> HTTPRequest {
        // Create a client.
        let client = Client::new();
        HTTPRequest{client: client}
    }

    pub fn get(&mut self, url: String)
                   -> Result<hyper::client::response::Response, hyper::error::Error> {
        // Creating an outgoing request.
        let res = self.client.get(&*url)
            .header(Connection::close())
            .send();
        res
    }

    pub fn load(&mut self, url_base: String, body: &str)
                   -> Result<hyper::client::response::Response, hyper::error::Error> {
        // Creating an loading data request via POST.
        let url = url_base + "/d/load";
        let res = self.client.post(&*url)
            .header(ContentType::json())
            .body(body)
            .send();
        res
    }

    pub fn receive(&mut self, res: &mut hyper::client::response::Response) -> String {
        // Read the Response.
        let mut body = String::new();
        res.read_to_string(&mut body).unwrap();

        body
    }
}

pub type Query<'a> = Vec<(&'a str, &'a str)>;

#[derive(Clone, Debug)]
pub struct CommandQuery<'a> {
    command: String,
    arguments: Query<'a>
}

impl<'a> CommandQuery<'a> {
    pub fn new(command: &str) -> CommandQuery {
        let arguments: Query = vec![];
        CommandQuery{command: command.to_string(), arguments: arguments}
    }

    pub fn get_command(&mut self) -> String {
        self.command.clone()
    }

    pub fn set_argument(&mut self, arguments: Query<'a>) {
        self.arguments = arguments
    }

    pub fn make_query(&mut self) -> String {
        form_urlencoded::serialize(self.arguments.clone().into_iter())
    }

    pub fn encode(&mut self) -> String {
        format!("/d/{}?{}", self.get_command(), self.make_query())
    }
}

#[derive(Clone, Debug)]
pub struct ResultParser {
    result: Box<JFObject>
}

impl ResultParser {
    pub fn new(json: String) -> ResultParser {
        ResultParser{result: json_flex::decode(json)}
    }

    pub fn get_raw_object(&mut self) -> Box<JFObject> {
        self.result.clone()
    }

    pub fn get_header(&mut self) -> JFObject {
        self.result[0].clone()
    }

    pub fn status(&mut self) -> Option<&i64> {
        self.result[0][0].into_i64().clone()
    }

    pub fn start_time(&mut self) -> Option<&f64> {
        self.result[0][1].into_f64()
    }

    pub fn elapsed_time(&mut self) -> Option<&f64> {
        self.result[0][2].into_f64()
    }

    #[inline]
    fn matched_columns_num(&mut self) -> i64 {
        self.result[1][0][0]
            .unwrap_vec().clone()
            .pop().unwrap()
            .unwrap_i64().clone()
    }

    pub fn matched_columns(&mut self) -> Option<i64> {
        match self.status() {
            Some(&0) => Some(self.matched_columns_num()),
            Some(_) => Some(-1),
            None    => None
        }
    }

    pub fn result(&mut self) -> Option<Vec<JFObject>> {
        match self.status() {
            Some(&0) => Some(vec![self.result[1][0].clone()]),
            Some(_) => Some(vec![self.result[0][3].clone()]),
            None    => None
        }
    }
}
<commit_msg>Add tests<commit_after>extern crate hyper;
extern crate url;
extern crate json_flex;

use std::io::Read;

use hyper::Client;
use hyper::header::Connection;
use hyper::header::ContentType;
use url::form_urlencoded;
use json_flex::{JFObject, Unwrap};

pub struct HTTPRequest {
    client: Client,
}

impl HTTPRequest {
    pub fn new() -> HTTPRequest {
        // Create a client.
        let client = Client::new();
        HTTPRequest{client: client}
    }

    pub fn get(&mut self, url: String)
                   -> Result<hyper::client::response::Response, hyper::error::Error> {
        // Creating an outgoing request.
        let res = self.client.get(&*url)
            .header(Connection::close())
            .send();
        res
    }

    pub fn load(&mut self, url_base: String, body: &str)
                   -> Result<hyper::client::response::Response, hyper::error::Error> {
        // Creating an loading data request via POST.
        let url = url_base + "/d/load";
        let res = self.client.post(&*url)
            .header(ContentType::json())
            .body(body)
            .send();
        res
    }

    pub fn receive(&mut self, res: &mut hyper::client::response::Response) -> String {
        // Read the Response.
        let mut body = String::new();
        res.read_to_string(&mut body).unwrap();

        body
    }
}

pub type Query<'a> = Vec<(&'a str, &'a str)>;

#[derive(Clone, Debug)]
pub struct CommandQuery<'a> {
    command: String,
    arguments: Query<'a>
}

impl<'a> CommandQuery<'a> {
    pub fn new(command: &str) -> CommandQuery {
        let arguments: Query = vec![];
        CommandQuery{command: command.to_string(), arguments: arguments}
    }

    pub fn get_command(&mut self) -> String {
        self.command.clone()
    }

    pub fn set_argument(&mut self, arguments: Query<'a>) {
        self.arguments = arguments
    }

    pub fn make_query(&mut self) -> String {
        form_urlencoded::serialize(self.arguments.clone().into_iter())
    }

    pub fn encode(&mut self) -> String {
        format!("/d/{}?{}", self.get_command(), self.make_query())
    }
}

#[cfg(test)]
mod command_query_test {
    use super::*;

    #[test]
    fn construct_query() {
        let mut command = CommandQuery::new("select");
        command.set_argument(vec![("table", "Site")]);
        let url_encoded = "/d/select?table=Site";
        assert_eq!(url_encoded, command.encode());
    }

    #[test]
    fn construct_complex_query() {
        let mut command = CommandQuery::new("select");
        command.set_argument(vec![("table", "Site"),
                                  ("--query","\'_key:\"http://example.org/\"\'")]);
        let url_encoded = "/d/select?table=Site&--query=%27_key%3A%22http%3A%2F%2Fexample.org%2F%22%27";
        assert_eq!(url_encoded, command.encode());
    }
}

#[derive(Clone, Debug)]
pub struct ResultParser {
    result: Box<JFObject>
}

impl ResultParser {
    pub fn new(json: String) -> ResultParser {
        ResultParser{result: json_flex::decode(json)}
    }

    pub fn get_raw_object(&mut self) -> Box<JFObject> {
        self.result.clone()
    }

    pub fn get_header(&mut self) -> JFObject {
        self.result[0].clone()
    }

    pub fn status(&mut self) -> Option<&i64> {
        self.result[0][0].into_i64().clone()
    }

    pub fn start_time(&mut self) -> Option<&f64> {
        self.result[0][1].into_f64()
    }

    pub fn elapsed_time(&mut self) -> Option<&f64> {
        self.result[0][2].into_f64()
    }

    #[inline]
    fn matched_columns_num(&mut self) -> i64 {
        self.result[1][0][0]
            .unwrap_vec().clone()
            .pop().unwrap()
            .unwrap_i64().clone()
    }

    pub fn matched_columns(&mut self) -> Option<i64> {
        match self.status() {
            Some(&0) => Some(self.matched_columns_num()),
            Some(_) => Some(-1),
            None    => None
        }
    }

    pub fn result(&mut self) -> Option<Vec<JFObject>> {
        match self.status() {
            Some(&0) => Some(vec![self.result[1][0].clone()]),
            Some(_) => Some(vec![self.result[0][3].clone()]),
            None    => None
        }
    }
}

#[cfg(test)]
mod result_parser_test {
    use super::*;

    #[test]
    fn parse_result() {
        let response = "
    [[0,1452348610.39281,0.000101566314697266],
    [[[9],[[\"_id\",\"UInt32\"],[\"_key\",\"ShortText\"],[\"title\",\"ShortText\"]],
    [1,\"http://example.org/\",\"This is test record 1!\"],
    [2,\"http://example.net/\",\"test record 2.\"],
    [3,\"http://example.com/\",\"test test record three.\"],
    [4,\"http://example.net/afr\",\"test record four.\"],
    [5,\"http://example.org/aba\",\"test test test record five.\"],
    [6,\"http://example.com/rab\",\"test test test test record six.\"],
    [7,\"http://example.net/atv\",\"test test test record seven.\"],
    [8,\"http://example.org/gat\",\"test test record eight.\"],
    [9,\"http://example.com/vdw\",\"test test record nine.\"]]]]";
        let mut decode = ResultParser::new(response.to_string());
        assert_eq!(&0, decode.status().unwrap());
        assert_eq!(&1452348610.39281, decode.start_time().unwrap());
        assert_eq!(&0.000101566314697266, decode.elapsed_time().unwrap());
        assert_eq!(9, decode.matched_columns().unwrap());
        // TODO: assert responed data
    }
}
<|endoftext|>


<commit_before>#![feature(plugin)]
#![allow(dead_code)]
#![allow(unused_must_use)]
#[cfg(test)]

extern crate core;
extern crate byteorder;
#[macro_use]
extern crate bitflags;
extern crate rand;

// Private modules
mod frames;
pub mod header;

// Public modules
pub mod error;
pub mod packet;
pub mod client;

#[cfg(test)]
mod tests {
    #[test]
    fn creates_new_client() {
        let _ = super::client::QuicClient::new("localhost", 443);
    }
}
<commit_msg>remove plugin that was breaking stable build<commit_after>#![allow(dead_code)]
#![allow(unused_must_use)]
#[cfg(test)]

extern crate core;
extern crate byteorder;
#[macro_use]
extern crate bitflags;
extern crate rand;

// Private modules
mod frames;
pub mod header;

// Public modules
pub mod error;
pub mod packet;
pub mod client;

#[cfg(test)]
mod tests {
    #[test]
    fn creates_new_client() {
        let _ = super::client::QuicClient::new("localhost", 443);
    }
}
<|endoftext|>


<commit_before>#![allow(unused_variables)]
#![allow(dead_code)]
#![feature(slice_patterns)]

//! # Rules
//! Rules uses regular expressions to do pattern matching using syntax
//! based upon the Perl 6 regex grammar. The Perl 6 grammar has been heavily
//! revised from Perl 5 and should not be equated with it. This may look
//! nothing like any regex you have seen before.
//!
//! # Syntax
//!
//! Currently, this is designed for ASCII and may not behave properly
//! with Unicode.
//!
//! Whitespace is generally ignored so that a regex can be more readable
//! and less dense.
//!
//! ```plain
//! r"fred"    // Normal way
//! r"f r e d" // Completely equivalent
//! // Will match `apples_oranges` or any other deliminator
//! r"apples . oranges"
//! ```
//!
//! # Literals
//!
//! Alphanumerics, underscores (`_`), and everything enclosed within
//! quotes (`"`) and ticks (`'`) are the only literals.
//!
//! ```plain
//! hello_world   // Matches `hello_world`.
//! "carrot cake" // Matches `carrot cake`.
//! 'apple pie'   // Matches `apple pie`.
//! ```
//!
//! Everything else must be escaped with a backslash (`\*`) to literally match.
//!
//! ```plain
//! it\'s\ my\ birthday // Matches `it's my birthday`.
//! ```
//!
//! # Set operators
//!
//! These operators can be applied to groups which will be analyzed later:
//!
//! ```plain
//! +       Union                // [123] + [345] = [12345]
//! |       Union                // Same
//! &       Intersection         // [123] & [345] = [3]
//! -       Difference           // [123] - [345] = [12]
//! ^       Symmetric difference // [123] ^ [345] = [1245]
//! ```
//!
//! # Character classes
//!
//! ## Default character classes
//!
//! Character | Matches               | Inverse
//! ----------|-----------------------|--------
//! `.`       | Any character         | N/A
//! `\d`      | Digit                 | `\D`
//! `\h`      | Horizontal whitespace | `\H`
//! `\n`      | Newline               | `\N`
//! `\s`      | Any whitespace        | `\S`
//! `\t`      | Tab                   | `\T`
//! `\w`      | Alphanumeric or `_`   | `\W`
//!
//! ## Custom character classes
//!
//! Characters inside a set of `<[ ]>` form a custom character
//! class:
//!
//! ```plain
//! // Matches `a` or `b` or `c`
//! <[ a b c ]>
//!
//! // `..` expresses a range so this matches
//! // from `a` to `g` or a digit
//! <[ a .. g \d ]>
//!
//! // The `[]` bind the sets together into (non-capturing)
//! // groups so set operators can be used.
//! <[0-9] - [13579]> // Matches an even number
//! <\d - [13579]>    // Same
//! <[0-6] ^ [5-9]>   // Matches a `5` or `6`
//! <-[0-6]>          // Negation: anything but 0-6
//! ```
//!
//! # Comments
//!
//! Comments are allowed inside a regex.
//!
//! ```plain
//! // This matches `myregex`
//! r"my // This is a comment which goes to the end of the line
//! regex"
//! ```

pub mod collapse;
pub mod parse;
pub mod re;
mod unicode;

<commit_msg>Add range_set to lib<commit_after>#![allow(unused_variables)]
#![allow(dead_code)]
#![feature(slice_patterns)]

//! # Rules
//! Rules uses regular expressions to do pattern matching using syntax
//! based upon the Perl 6 regex grammar. The Perl 6 grammar has been heavily
//! revised from Perl 5 and should not be equated with it. This may look
//! nothing like any regex you have seen before.
//!
//! # Syntax
//!
//! Currently, this is designed for ASCII and may not behave properly
//! with Unicode.
//!
//! Whitespace is generally ignored so that a regex can be more readable
//! and less dense.
//!
//! ```plain
//! r"fred"    // Normal way
//! r"f r e d" // Completely equivalent
//! // Will match `apples_oranges` or any other deliminator
//! r"apples . oranges"
//! ```
//!
//! # Literals
//!
//! Alphanumerics, underscores (`_`), and everything enclosed within
//! quotes (`"`) and ticks (`'`) are the only literals.
//!
//! ```plain
//! hello_world   // Matches `hello_world`.
//! "carrot cake" // Matches `carrot cake`.
//! 'apple pie'   // Matches `apple pie`.
//! ```
//!
//! Everything else must be escaped with a backslash (`\*`) to literally match.
//!
//! ```plain
//! it\'s\ my\ birthday // Matches `it's my birthday`.
//! ```
//!
//! # Set operators
//!
//! These operators can be applied to groups which will be analyzed later:
//!
//! ```plain
//! +       Union                // [123] + [345] = [12345]
//! |       Union                // Same
//! &       Intersection         // [123] & [345] = [3]
//! -       Difference           // [123] - [345] = [12]
//! ^       Symmetric difference // [123] ^ [345] = [1245]
//! ```
//!
//! # Character classes
//!
//! ## Default character classes
//!
//! Character | Matches               | Inverse
//! ----------|-----------------------|--------
//! `.`       | Any character         | N/A
//! `\d`      | Digit                 | `\D`
//! `\h`      | Horizontal whitespace | `\H`
//! `\n`      | Newline               | `\N`
//! `\s`      | Any whitespace        | `\S`
//! `\t`      | Tab                   | `\T`
//! `\w`      | Alphanumeric or `_`   | `\W`
//!
//! ## Custom character classes
//!
//! Characters inside a set of `<[ ]>` form a custom character
//! class:
//!
//! ```plain
//! // Matches `a` or `b` or `c`
//! <[ a b c ]>
//!
//! // `..` expresses a range so this matches
//! // from `a` to `g` or a digit
//! <[ a .. g \d ]>
//!
//! // The `[]` bind the sets together into (non-capturing)
//! // groups so set operators can be used.
//! <[0-9] - [13579]> // Matches an even number
//! <\d - [13579]>    // Same
//! <[0-6] ^ [5-9]>   // Matches a `5` or `6`
//! <-[0-6]>          // Negation: anything but 0-6
//! ```
//!
//! # Comments
//!
//! Comments are allowed inside a regex.
//!
//! ```plain
//! // This matches `myregex`
//! r"my // This is a comment which goes to the end of the line
//! regex"
//! ```

pub mod collapse;
pub mod parse;
pub mod re;
pub mod range_set;
mod unicode;

<|endoftext|>


<commit_before>extern crate time;
extern crate byteorder;

mod ntp;
mod error;

use std::net::UdpSocket;
use time::Timespec;
pub use error::Error;

const NTP_PORT: u16 = 123;
const UDP_LOCAL: &'static str = "0.0.0.0:35000";

/// `retrieve_ntp_timestamp` retrieves the current time from a NTP server.
///
/// # Arguments
///
/// * `host` - The NTP server (i.e. sundial.columbia.edu).
pub fn retrieve_ntp_timestamp(host: &str) -> Result<Timespec, Error> {
    let header = ntp::NTPHeader::new();
    let message = try!(header.encode());

    let socket = try!(UdpSocket::bind(UDP_LOCAL));

    let host = format!("{host}:{port}", host=host, port=NTP_PORT);
    try!(socket.send_to(&message[..], &host[..]));

    let mut buf = [0u8; 1000];

    // TODO: Rust doesn't support timeouts yet
    let (amt, _) = try!(socket.recv_from(buf.as_mut_slice()));

    drop(socket);

    let header = try!(ntp::NTPHeader::decode(amt, &buf));

    Ok(header.transmit_timestamp.as_timespec())
}

#[test]
fn receive_timestamp() {
    const NTP_SERVER: &'static str = "sundial.columbia.edu";

    let t1 = retrieve_ntp_timestamp(NTP_SERVER).unwrap();
    let t2 = retrieve_ntp_timestamp(NTP_SERVER).unwrap();

    assert!(t2 > t1);
}
<commit_msg>NTPHeader made public<commit_after>extern crate time;
extern crate byteorder;

mod ntp;
mod error;

use std::net::UdpSocket;
use time::Timespec;
pub use error::Error;
pub use ntp::NTPHeader;

const NTP_PORT: u16 = 123;
const UDP_LOCAL: &'static str = "0.0.0.0:35000";

/// `retrieve_ntp_timestamp` retrieves the current time from a NTP server.
///
/// # Arguments
///
/// * `host` - The NTP server (i.e. sundial.columbia.edu).
pub fn retrieve_ntp_timestamp(host: &str) -> Result<Timespec, Error> {
    let header = NTPHeader::new();
    let message = try!(header.encode());

    let socket = try!(UdpSocket::bind(UDP_LOCAL));

    let host = format!("{host}:{port}", host=host, port=NTP_PORT);
    try!(socket.send_to(&message[..], &host[..]));

    let mut buf = [0u8; 1000];

    // TODO: Rust doesn't support timeouts yet
    let (amt, _) = try!(socket.recv_from(buf.as_mut_slice()));

    drop(socket);

    let header = try!(ntp::NTPHeader::decode(amt, &buf));

    Ok(header.transmit_timestamp.as_timespec())
}

#[test]
fn receive_timestamp() {
    const NTP_SERVER: &'static str = "sundial.columbia.edu";

    let t1 = retrieve_ntp_timestamp(NTP_SERVER).unwrap();
    let t2 = retrieve_ntp_timestamp(NTP_SERVER).unwrap();

    assert!(t2 > t1);
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/*!
# Autogard
*/

// TODO Using expression template will give better performance.
//      e.g., http://www.met.reading.ac.uk/clouds/adept/

// TODO Multi-threading support.

#![crate_name = "autograd"]
#![crate_type = "rlib"]

#![feature(thread_local)]
#![feature(std_misc)]
#![feature(core)]

mod context;
mod float;

pub use context::Context;
// TODO ideally Private traits shouldn't be exported.
pub use context::ContextCratePrivate;
pub use context::ContextModulePrivate;

pub use float::Float;
<commit_msg>Add Valgrind, ASAN, and TSAN TODO comment.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/*!
# Autogard
*/

// TODO Using expression template will give better performance.
//      e.g., http://www.met.reading.ac.uk/clouds/adept/

// TODO Multi-threading support.

// TODO Add Valgrind, ASAN, and TSAN tests.

#![crate_name = "autograd"]
#![crate_type = "rlib"]

#![feature(thread_local)]
#![feature(std_misc)]
#![feature(core)]

mod context;
mod float;

pub use context::Context;
// TODO ideally Private traits shouldn't be exported.
pub use context::ContextCratePrivate;
pub use context::ContextModulePrivate;

pub use float::Float;
<|endoftext|>


<commit_before>#[macro_use]
extern crate lazy_static;

#[macro_use]
extern crate log;

extern crate uuid;
use self::uuid::Uuid;

#[cfg(unix)]
mod imp {
    mod unix;
    pub use self::unix::get_machine_id;
}

#[cfg(not(any(unix)))]
mod imp {
    fn get_machine_id() -> Option<Uuid> {
        None
    }
}

use std::fmt;

/// A machine-specific ID.
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
pub struct MachineId {
    uuid: Uuid,
}

lazy_static! {
    static ref GENERATED_ID: Uuid = Uuid::new_v4();
    static ref GLOBAL_ID: Option<Uuid> = imp::get_machine_id();
}

impl MachineId {
    /// Retrieves or generates the machine-specific ID.
    pub fn get() -> MachineId {
        MachineId {
            uuid: GLOBAL_ID.unwrap_or(*GENERATED_ID).clone(),
        }
    }
}

impl fmt::Display for MachineId {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.uuid)
    }
}

#[test]
fn test_idempotent() {
    let fst = MachineId::get();
    let snd = MachineId::get();

    assert_eq!(fst, snd);
}

#[test]
fn test_can_print() {
    let mid = MachineId::get();

    println!("{}", mid);
    assert_eq!(false, true);
}
<commit_msg>lib: remove old assert<commit_after>#[macro_use]
extern crate lazy_static;

#[macro_use]
extern crate log;

extern crate uuid;
use self::uuid::Uuid;

#[cfg(unix)]
mod imp {
    mod unix;
    pub use self::unix::get_machine_id;
}

#[cfg(not(any(unix)))]
mod imp {
    fn get_machine_id() -> Option<Uuid> {
        None
    }
}

use std::fmt;

/// A machine-specific ID.
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
pub struct MachineId {
    uuid: Uuid,
}

lazy_static! {
    static ref GENERATED_ID: Uuid = Uuid::new_v4();
    static ref GLOBAL_ID: Option<Uuid> = imp::get_machine_id();
}

impl MachineId {
    /// Retrieves or generates the machine-specific ID.
    pub fn get() -> MachineId {
        MachineId {
            uuid: GLOBAL_ID.unwrap_or(*GENERATED_ID).clone(),
        }
    }
}

impl fmt::Display for MachineId {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.uuid)
    }
}

#[test]
fn test_idempotent() {
    let fst = MachineId::get();
    let snd = MachineId::get();

    assert_eq!(fst, snd);
}

#[test]
fn test_can_print() {
    let mid = MachineId::get();

    println!("{}", mid);
}
<|endoftext|>