<commit_before>#![cfg_attr(unix, feature(fs_ext, libc, convert))]

//! xdg-rs is a utility library to make conforming to the
//! [XDG specification](http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html) easier.
//!
//! Some code borrowed from [rust-xdg](https://github.com/o11c/rust-xdg). ```rust-xdg``` is
//! currently a more complete implementation of the specification. The APIs provided by
//! ```rust-xdg``` and ```xdg-rs``` are different.

#[cfg(unix)]
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::fs;
use std::path::{Path, PathBuf};

#[cfg(unix)]
use std::os::unix::fs::PermissionsExt;

/// 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<F>(get_env_var: &F) -> Result<PathBuf>
    where F: Fn(&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<F>(get_env_var: &F) -> Vec<PathBuf>
    where F: Fn(&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<F>(get_env_var: &F) -> Result<PathBuf>
    where F: Fn(&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<F>(get_env_var: &F) -> Vec<PathBuf>
    where F: Fn(&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<F>(get_env_var: &F) -> Result<PathBuf>
    where F: Fn(&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_PATH``` 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<F>(get_env_var: &F) -> Option<PathBuf>
    where F: Fn(&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_PATH``` 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(true) if permissions are correct, Ok(false) if permissions are incorrect,
/// or propogates any errors that occurred while checking permissions.
///
/// >$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(unix)]
pub fn test_runtime_dir<P: AsRef<Path>>(path: P) -> Result<()> {
    fs::metadata(&path)
        .or_else(|e| Err(From::from(e)))
        .map(|attr| (attr.permissions().mode()))
        .and_then(inner::check_permissions)
        .and(inner::test_dir_uid_is_current_user(path.as_ref()))
}

#[cfg(not(unix))]
pub fn test_runtime_dir<P: AsRef<Path>>(path: P) -> Result<()> {
    Ok(())
}

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

/// Get an environment variable's value as a PathBuf.
fn get_env_path<F>(get_env_var: &F, env_var: &str) -> Option<PathBuf>
    where F: Fn(&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<F>(get_env_var: &F, env_var: &str, default: &str) -> Vec<PathBuf>
    where F: Fn(&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(unix)]
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 => From::from(XdgError::IncorrectOwner)
            }
        }
    }

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

    fn cstr(path: &Path) -> Result<CString> {
        path.as_os_str().to_cstring()
            .ok_or(From::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>fix test_runtime_dir documentation<commit_after>#![cfg_attr(unix, feature(fs_ext, libc, convert))]

//! xdg-rs is a utility library to make conforming to the
//! [XDG specification](http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html) easier.
//!
//! Some code borrowed from [rust-xdg](https://github.com/o11c/rust-xdg). ```rust-xdg``` is
//! currently a more complete implementation of the specification. The APIs provided by
//! ```rust-xdg``` and ```xdg-rs``` are different.

#[cfg(unix)]
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::fs;
use std::path::{Path, PathBuf};

#[cfg(unix)]
use std::os::unix::fs::PermissionsExt;

/// 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<F>(get_env_var: &F) -> Result<PathBuf>
    where F: Fn(&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<F>(get_env_var: &F) -> Vec<PathBuf>
    where F: Fn(&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<F>(get_env_var: &F) -> Result<PathBuf>
    where F: Fn(&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<F>(get_env_var: &F) -> Vec<PathBuf>
    where F: Fn(&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<F>(get_env_var: &F) -> Result<PathBuf>
    where F: Fn(&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_PATH``` 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<F>(get_env_var: &F) -> Option<PathBuf>
    where F: Fn(&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_PATH``` 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(unix)]
pub fn test_runtime_dir<P: AsRef<Path>>(path: P) -> Result<()> {
    fs::metadata(&path)
        .or_else(|e| Err(From::from(e)))
        .map(|attr| (attr.permissions().mode()))
        .and_then(inner::check_permissions)
        .and(inner::test_dir_uid_is_current_user(path.as_ref()))
}

#[cfg(not(unix))]
pub fn test_runtime_dir<P: AsRef<Path>>(path: P) -> Result<()> {
    Ok(())
}

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

/// Get an environment variable's value as a PathBuf.
fn get_env_path<F>(get_env_var: &F, env_var: &str) -> Option<PathBuf>
    where F: Fn(&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<F>(get_env_var: &F, env_var: &str, default: &str) -> Vec<PathBuf>
    where F: Fn(&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(unix)]
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 => From::from(XdgError::IncorrectOwner)
            }
        }
    }

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

    fn cstr(path: &Path) -> Result<CString> {
        path.as_os_str().to_cstring()
            .ok_or(From::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>//! Find discrete returns in full waveform LiDAR data.
//!
//! # Why is this library called `peakbag`?
//!
//! [Peak bagging](https://en.wikipedia.org/wiki/Peak_bagging) is when you try to summit a bunch of
//! mountains, just to say you summited a bunch of mountains. While the practice of peak bagging
//! can correlate with actual appreciation for the out-of-doors and an adventuresome spirit, a peak
//! bagging attitude is neither necessary nor sufficient for a good time outside.
//!
//! Some use "peak bagger" as a derisive term for someone who likes taking a selfie on top of a
//! mountain more than just spending time outside.
//!
//! This library finds peaks in waveforms, so `peakbag` seemed as good of a name as any.

#![deny(box_pointers, fat_ptr_transmutes, missing_copy_implementations, missing_debug_implementations, missing_docs, trivial_casts, unsafe_code, unused_extern_crates, unused_import_braces, unused_qualifications, variant_size_differences)]

#[macro_use]
extern crate log;
extern crate num;

use std::f64;
use std::fmt;

use num::traits::{ToPrimitive, Unsigned};

/// Detects peaks in full waveform data.
///
/// This is a convenience method that wraps calls to `PeakDetector::new` and
/// `PeakDetector::detect_peaks`.
///
/// # Examples
///
/// ```
/// let ref data = [1u32, 2, 3, 4, 3, 2, 1];
/// let peaks = peakbag::detect_peaks(data, 3, 0, 5);
/// assert_eq!(1, peaks.len());
/// assert_eq!(4, peaks[0].amplitude);
/// assert_eq!(3, peaks[0].index);
/// ```
pub fn detect_peaks<T>(data: &[T], width: usize, floor: T, ceiling: T) -> Vec<Peak<T>>
    where T: Copy + fmt::Display + PartialOrd + ToPrimitive + Unsigned
{
    let detector = PeakDetector::new(width, floor, ceiling);
    detector.detect_peaks(data)
}

/// Configurable struct for detecting peaks.
///
/// This structure allow for fine-grained adjustment of the peak detection procedures.
#[derive(Clone, Copy, Debug)]
pub struct PeakDetector<T: Copy> {
    width: usize,
    floor: T,
    ceiling: T,
    saturation: Option<T>,
    max_kurtosis: f64,
    min_height_above_background: f64,
}

impl<T: Copy> PeakDetector<T> {
    /// Creates a new peak detector.
    ///
    /// # Examples
    ///
    /// ```
    /// use peakbag::PeakDetector;
    /// let detector = PeakDetector::new(1, 2, 3);
    /// ```
    pub fn new(width: usize, floor: T, ceiling: T) -> PeakDetector<T> {
        PeakDetector {
            width: width,
            floor: floor,
            ceiling: ceiling,
            saturation: None,
            max_kurtosis: f64::MAX,
            min_height_above_background: f64::MIN,
        }
    }

    /// Sets the saturation level for this peak detector.
    ///
    /// # Examples
    ///
    /// ```
    /// use peakbag::PeakDetector;
    /// let mut detector = PeakDetector::new(1, 2, 3).saturation(3);
    /// ```
    pub fn saturation(mut self, saturation: T) -> PeakDetector<T> {
        self.saturation = Some(saturation);
        self
    }

    /// Sets the minimum allowable height above background for a peak.
    ///
    /// # Examples
    ///
    /// ```
    /// use peakbag::PeakDetector;
    /// let mut detector = PeakDetector::new(1, 2, 3).min_height_above_background(4.0);
    /// ```
    pub fn min_height_above_background(mut self,
                                       min_height_above_background: f64)
                                       -> PeakDetector<T> {
        self.min_height_above_background = min_height_above_background;
        self
    }

    /// Sets the maximum allowable kurtosis for a peak.
    ///
    /// # Examples
    ///
    /// ```
    /// use peakbag::PeakDetector;
    /// let detector = PeakDetector::new(1, 2, 3).max_kurtosis(4.0);
    /// ```
    pub fn max_kurtosis(mut self, max_kurtosis: f64) -> PeakDetector<T> {
        self.max_kurtosis = max_kurtosis;
        self
    }
}

impl<T> PeakDetector<T> where T: Copy + fmt::Display + PartialOrd + ToPrimitive + Unsigned {
    /// Detects peaks in full waveform data.
    ///
    /// # Panics
    ///
    /// Panics if a sample value cannot be converted to an `i64` and `f64`.
    ///
    /// # Examples
    ///
    /// ```
    /// use peakbag::PeakDetector;
    /// let detector = PeakDetector::new(0, 8, 2);
    /// let peaks = detector.detect_peaks(&[1u32, 2, 3, 2, 1]);
    /// ```
    pub fn detect_peaks(&self, data: &[T]) -> Vec<Peak<T>> {
        let mut state = State::Ascending(0);
        let mut peaks = Vec::new();
        for (i, &sample) in data.iter().enumerate() {
            if i == 0 {
                // We assume the first sample has a slope of zero, which means it can start a leading
                // edge.
                state = State::Ascending(1);
                continue;
            }
            let slope = sample.to_i64().unwrap() - data[i - 1].to_i64().unwrap();
            if let Some(s) = self.saturation {
                if sample == s {
                    state = State::Saturated;
                }
            }
            match state {
                State::Ascending(n) => {
                    if slope < 0 {
                        state = State::Ascending(0);
                    } else if n == self.width {
                        state = State::Descending(0, i);
                    } else {
                        state = State::Ascending(n + 1);
                    }
                }
                State::Descending(n, index) => {
                    if slope > 0 {
                        if n == 0 {
                            state = State::Descending(0, i);
                        } else {
                            state = State::Ascending(1);
                        }
                    } else if n + 1 == self.width {
                        let amplitude = data[index];
                        if amplitude > self.floor && amplitude <= self.ceiling {
                            let (mean, rms, kurtosis) = self.peak_stats(&data, index);
                            let height_above_background = self.height_above_background(&data,
                                                                                index);
                            if height_above_background >= self.min_height_above_background &&
                               kurtosis < self.max_kurtosis {
                                peaks.push(Peak {
                                    index: index,
                                    amplitude: amplitude,
                                    mean: mean,
                                    rms: rms,
                                    kurtosis: kurtosis,
                                    height_above_background: height_above_background,
                                });
                            }
                        }
                        state = State::Ascending(0);
                    } else {
                        state = State::Descending(n + 1, index);
                    }
                }
                State::Saturated => {
                    // We know if we're below the floor, we have a negative slope, since we must
                    // have just gone below the floor.
                    if sample <= self.floor {
                        state = State::Ascending(0);
                    }
                }
            };
            debug!("({}) sample={}, slope={}, state={:?}",
                   i,
                   sample,
                   slope,
                   state);
            println!("({}) sample={}, slope={}, state={:?}",
                     i,
                     sample,
                     slope,
                     state);
        }
        peaks
    }

    fn peak_stats(&self, data: &[T], index: usize) -> (f64, f64, f64) {
        let mut values = 0u64;
        let mut values2 = 0u64;
        let mut nvalues = 0usize;
        for &sample in data.iter().skip(index - self.width).take(self.width * 2 + 1) {
            let sample = sample.to_u64().unwrap();
            values += sample;
            values2 += sample * sample;
            nvalues += 1;
        }
        let mean = values as f64 / nvalues as f64;
        let rms = (values2 as f64 / nvalues as f64 - (values as f64 / nvalues as f64).powi(2))
                      .sqrt();
        let mut kurtosis = 0f64;
        for &sample in data.iter().skip(index - self.width).take(self.width * 2 + 1) {
            let sample = sample.to_u64().unwrap();
            let temp = (sample as f64 - mean) / rms;
            kurtosis += temp.powi(4);
        }
        kurtosis = kurtosis / nvalues as f64 - 3.0;
        (mean, rms, kurtosis)
    }

    fn height_above_background(&self, data: &[T], index: usize) -> f64 {
        let slope: f64 = (data[index + self.width] - data[index - self.width]).to_f64().unwrap() /
                         (2.0 * self.width as f64);
        let intercept = data[index + self.width].to_f64().unwrap() -
                        slope * (index - self.width) as f64;
        data[index].to_f64().unwrap() - (slope * index as f64 + intercept)
    }
}

#[derive(Debug)]
enum State {
    Ascending(usize),
    Descending(usize, usize),
    Saturated,
}

/// A peak in the waveform data.
#[derive(Clone, Copy, Debug)]
pub struct Peak<T: Copy> {
    /// The raw intensity of the peak.
    pub amplitude: T,
    /// The index of the peak in the sample data.
    pub index: usize,
    /// The mean intensity value of the peak.
    pub mean: f64,
    /// The rms error of the peak from that mean.
    pub rms: f64,
    /// The kurtosis of the peak.
    pub kurtosis: f64,
    /// The height of the peak above a background level, as defined by the first and last points in
    /// the peak.
    pub height_above_background: f64,
}

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

    #[test]
    fn level_peak() {
        let peaks = detect_peaks(&[1u32, 2, 3, 4, 4, 4, 4], 3, 0, 5);
        assert_eq!(1, peaks.len());
        assert_eq!(3, peaks[0].index);
    }

    #[test]
    fn floor() {
        // Must be above floor, below or equal to ceil
        let peaks = detect_peaks(&[1u32, 2, 3, 4, 3, 2, 1], 3, 4, 5);
        assert_eq!(0, peaks.len());
    }

    #[test]
    fn ceiling() {
        // Must be above floor, below or equal to ceil
        let peaks = detect_peaks(&[1u32, 2, 3, 4, 3, 2, 1], 3, 0, 3);
        assert_eq!(0, peaks.len());
    }

    #[test]
    fn saturation() {
        let detector = PeakDetector::new(3, 1, 8).saturation(8);
        let peaks = detector.detect_peaks(&[5u32, 6, 7, 8, 7, 6, 5]);
        assert_eq!(0, peaks.len());
    }

    #[test]
    fn stats() {
        let peaks = detect_peaks(&[1u32, 2, 3, 4, 3, 2, 1], 3, 0, 5);
        let ref peak = peaks[0];
        assert_eq!(2.2857142857142856, peak.mean);
        assert_eq!(1.0301575072754257, peak.rms);
        assert_eq!(-1.143491124260356, peak.kurtosis);
        assert_eq!(3.0, peak.height_above_background);
    }

    #[test]
    fn min_height_above_background() {
        let detector = PeakDetector::new(3, 1, 8).min_height_above_background(4.0);
        let peaks = detector.detect_peaks(&[1u32, 2, 3, 4, 3, 2, 1]);
        assert_eq!(0, peaks.len());
    }

    #[test]
    fn peak_kurtosis() {
        let detector = PeakDetector::new(3, 1, 8).max_kurtosis(-2.0);
        let peaks = detector.detect_peaks(&[1u32, 2, 3, 4, 3, 2, 1]);
        assert_eq!(0, peaks.len());
    }

    #[test]
    fn should_be_one_peak() {
        let ref data = [2u16, 2, 1, 2, 5, 18, 51, 107, 166, 195, 176, 125, 70, 34, 14, 7, 5, 4, 5,
                        4, 3, 1, 0, 0];
        let peaks = detect_peaks(data, 2, 15, 255);
        assert_eq!(1, peaks.len());
    }
}
<commit_msg>Remove leftover println<commit_after>//! Find discrete returns in full waveform LiDAR data.
//!
//! # Why is this library called `peakbag`?
//!
//! [Peak bagging](https://en.wikipedia.org/wiki/Peak_bagging) is when you try to summit a bunch of
//! mountains, just to say you summited a bunch of mountains. While the practice of peak bagging
//! can correlate with actual appreciation for the out-of-doors and an adventuresome spirit, a peak
//! bagging attitude is neither necessary nor sufficient for a good time outside.
//!
//! Some use "peak bagger" as a derisive term for someone who likes taking a selfie on top of a
//! mountain more than just spending time outside.
//!
//! This library finds peaks in waveforms, so `peakbag` seemed as good of a name as any.

#![deny(box_pointers, fat_ptr_transmutes, missing_copy_implementations, missing_debug_implementations, missing_docs, trivial_casts, unsafe_code, unused_extern_crates, unused_import_braces, unused_qualifications, variant_size_differences)]

#[macro_use]
extern crate log;
extern crate num;

use std::f64;
use std::fmt;

use num::traits::{ToPrimitive, Unsigned};

/// Detects peaks in full waveform data.
///
/// This is a convenience method that wraps calls to `PeakDetector::new` and
/// `PeakDetector::detect_peaks`.
///
/// # Examples
///
/// ```
/// let ref data = [1u32, 2, 3, 4, 3, 2, 1];
/// let peaks = peakbag::detect_peaks(data, 3, 0, 5);
/// assert_eq!(1, peaks.len());
/// assert_eq!(4, peaks[0].amplitude);
/// assert_eq!(3, peaks[0].index);
/// ```
pub fn detect_peaks<T>(data: &[T], width: usize, floor: T, ceiling: T) -> Vec<Peak<T>>
    where T: Copy + fmt::Display + PartialOrd + ToPrimitive + Unsigned
{
    let detector = PeakDetector::new(width, floor, ceiling);
    detector.detect_peaks(data)
}

/// Configurable struct for detecting peaks.
///
/// This structure allow for fine-grained adjustment of the peak detection procedures.
#[derive(Clone, Copy, Debug)]
pub struct PeakDetector<T: Copy> {
    width: usize,
    floor: T,
    ceiling: T,
    saturation: Option<T>,
    max_kurtosis: f64,
    min_height_above_background: f64,
}

impl<T: Copy> PeakDetector<T> {
    /// Creates a new peak detector.
    ///
    /// # Examples
    ///
    /// ```
    /// use peakbag::PeakDetector;
    /// let detector = PeakDetector::new(1, 2, 3);
    /// ```
    pub fn new(width: usize, floor: T, ceiling: T) -> PeakDetector<T> {
        PeakDetector {
            width: width,
            floor: floor,
            ceiling: ceiling,
            saturation: None,
            max_kurtosis: f64::MAX,
            min_height_above_background: f64::MIN,
        }
    }

    /// Sets the saturation level for this peak detector.
    ///
    /// # Examples
    ///
    /// ```
    /// use peakbag::PeakDetector;
    /// let mut detector = PeakDetector::new(1, 2, 3).saturation(3);
    /// ```
    pub fn saturation(mut self, saturation: T) -> PeakDetector<T> {
        self.saturation = Some(saturation);
        self
    }

    /// Sets the minimum allowable height above background for a peak.
    ///
    /// # Examples
    ///
    /// ```
    /// use peakbag::PeakDetector;
    /// let mut detector = PeakDetector::new(1, 2, 3).min_height_above_background(4.0);
    /// ```
    pub fn min_height_above_background(mut self,
                                       min_height_above_background: f64)
                                       -> PeakDetector<T> {
        self.min_height_above_background = min_height_above_background;
        self
    }

    /// Sets the maximum allowable kurtosis for a peak.
    ///
    /// # Examples
    ///
    /// ```
    /// use peakbag::PeakDetector;
    /// let detector = PeakDetector::new(1, 2, 3).max_kurtosis(4.0);
    /// ```
    pub fn max_kurtosis(mut self, max_kurtosis: f64) -> PeakDetector<T> {
        self.max_kurtosis = max_kurtosis;
        self
    }
}

impl<T> PeakDetector<T> where T: Copy + fmt::Display + PartialOrd + ToPrimitive + Unsigned {
    /// Detects peaks in full waveform data.
    ///
    /// # Panics
    ///
    /// Panics if a sample value cannot be converted to an `i64` and `f64`.
    ///
    /// # Examples
    ///
    /// ```
    /// use peakbag::PeakDetector;
    /// let detector = PeakDetector::new(0, 8, 2);
    /// let peaks = detector.detect_peaks(&[1u32, 2, 3, 2, 1]);
    /// ```
    pub fn detect_peaks(&self, data: &[T]) -> Vec<Peak<T>> {
        let mut state = State::Ascending(0);
        let mut peaks = Vec::new();
        for (i, &sample) in data.iter().enumerate() {
            if i == 0 {
                // We assume the first sample has a slope of zero, which means it can start a leading
                // edge.
                state = State::Ascending(1);
                continue;
            }
            let slope = sample.to_i64().unwrap() - data[i - 1].to_i64().unwrap();
            if let Some(s) = self.saturation {
                if sample == s {
                    state = State::Saturated;
                }
            }
            match state {
                State::Ascending(n) => {
                    if slope < 0 {
                        state = State::Ascending(0);
                    } else if n == self.width {
                        state = State::Descending(0, i);
                    } else {
                        state = State::Ascending(n + 1);
                    }
                }
                State::Descending(n, index) => {
                    if slope > 0 {
                        if n == 0 {
                            state = State::Descending(0, i);
                        } else {
                            state = State::Ascending(1);
                        }
                    } else if n + 1 == self.width {
                        let amplitude = data[index];
                        if amplitude > self.floor && amplitude <= self.ceiling {
                            let (mean, rms, kurtosis) = self.peak_stats(&data, index);
                            let height_above_background = self.height_above_background(&data,
                                                                                index);
                            if height_above_background >= self.min_height_above_background &&
                               kurtosis < self.max_kurtosis {
                                peaks.push(Peak {
                                    index: index,
                                    amplitude: amplitude,
                                    mean: mean,
                                    rms: rms,
                                    kurtosis: kurtosis,
                                    height_above_background: height_above_background,
                                });
                            }
                        }
                        state = State::Ascending(0);
                    } else {
                        state = State::Descending(n + 1, index);
                    }
                }
                State::Saturated => {
                    // We know if we're below the floor, we have a negative slope, since we must
                    // have just gone below the floor.
                    if sample <= self.floor {
                        state = State::Ascending(0);
                    }
                }
            };
            debug!("({}) sample={}, slope={}, state={:?}",
                   i,
                   sample,
                   slope,
                   state);
        }
        peaks
    }

    fn peak_stats(&self, data: &[T], index: usize) -> (f64, f64, f64) {
        let mut values = 0u64;
        let mut values2 = 0u64;
        let mut nvalues = 0usize;
        for &sample in data.iter().skip(index - self.width).take(self.width * 2 + 1) {
            let sample = sample.to_u64().unwrap();
            values += sample;
            values2 += sample * sample;
            nvalues += 1;
        }
        let mean = values as f64 / nvalues as f64;
        let rms = (values2 as f64 / nvalues as f64 - (values as f64 / nvalues as f64).powi(2))
                      .sqrt();
        let mut kurtosis = 0f64;
        for &sample in data.iter().skip(index - self.width).take(self.width * 2 + 1) {
            let sample = sample.to_u64().unwrap();
            let temp = (sample as f64 - mean) / rms;
            kurtosis += temp.powi(4);
        }
        kurtosis = kurtosis / nvalues as f64 - 3.0;
        (mean, rms, kurtosis)
    }

    fn height_above_background(&self, data: &[T], index: usize) -> f64 {
        let slope: f64 = (data[index + self.width] - data[index - self.width]).to_f64().unwrap() /
                         (2.0 * self.width as f64);
        let intercept = data[index + self.width].to_f64().unwrap() -
                        slope * (index - self.width) as f64;
        data[index].to_f64().unwrap() - (slope * index as f64 + intercept)
    }
}

#[derive(Debug)]
enum State {
    Ascending(usize),
    Descending(usize, usize),
    Saturated,
}

/// A peak in the waveform data.
#[derive(Clone, Copy, Debug)]
pub struct Peak<T: Copy> {
    /// The raw intensity of the peak.
    pub amplitude: T,
    /// The index of the peak in the sample data.
    pub index: usize,
    /// The mean intensity value of the peak.
    pub mean: f64,
    /// The rms error of the peak from that mean.
    pub rms: f64,
    /// The kurtosis of the peak.
    pub kurtosis: f64,
    /// The height of the peak above a background level, as defined by the first and last points in
    /// the peak.
    pub height_above_background: f64,
}

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

    #[test]
    fn level_peak() {
        let peaks = detect_peaks(&[1u32, 2, 3, 4, 4, 4, 4], 3, 0, 5);
        assert_eq!(1, peaks.len());
        assert_eq!(3, peaks[0].index);
    }

    #[test]
    fn floor() {
        // Must be above floor, below or equal to ceil
        let peaks = detect_peaks(&[1u32, 2, 3, 4, 3, 2, 1], 3, 4, 5);
        assert_eq!(0, peaks.len());
    }

    #[test]
    fn ceiling() {
        // Must be above floor, below or equal to ceil
        let peaks = detect_peaks(&[1u32, 2, 3, 4, 3, 2, 1], 3, 0, 3);
        assert_eq!(0, peaks.len());
    }

    #[test]
    fn saturation() {
        let detector = PeakDetector::new(3, 1, 8).saturation(8);
        let peaks = detector.detect_peaks(&[5u32, 6, 7, 8, 7, 6, 5]);
        assert_eq!(0, peaks.len());
    }

    #[test]
    fn stats() {
        let peaks = detect_peaks(&[1u32, 2, 3, 4, 3, 2, 1], 3, 0, 5);
        let ref peak = peaks[0];
        assert_eq!(2.2857142857142856, peak.mean);
        assert_eq!(1.0301575072754257, peak.rms);
        assert_eq!(-1.143491124260356, peak.kurtosis);
        assert_eq!(3.0, peak.height_above_background);
    }

    #[test]
    fn min_height_above_background() {
        let detector = PeakDetector::new(3, 1, 8).min_height_above_background(4.0);
        let peaks = detector.detect_peaks(&[1u32, 2, 3, 4, 3, 2, 1]);
        assert_eq!(0, peaks.len());
    }

    #[test]
    fn peak_kurtosis() {
        let detector = PeakDetector::new(3, 1, 8).max_kurtosis(-2.0);
        let peaks = detector.detect_peaks(&[1u32, 2, 3, 4, 3, 2, 1]);
        assert_eq!(0, peaks.len());
    }

    #[test]
    fn should_be_one_peak() {
        let ref data = [2u16, 2, 1, 2, 5, 18, 51, 107, 166, 195, 176, 125, 70, 34, 14, 7, 5, 4, 5,
                        4, 3, 1, 0, 0];
        let peaks = detect_peaks(data, 2, 15, 255);
        assert_eq!(1, peaks.len());
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 Nathan Sizemore <nathanrsizemore@gmail.com>
//
// 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/.
//
// This Source Code Form is "Incompatible
// With Secondary Licenses", as defined by
// the Mozilla Public License, v. 2.0.

#[cfg(any(target_os="macos", target_os="linux"))]
extern crate libc;
#[cfg(any(target_os="macos", target_os="linux"))]
extern crate rand;
#[cfg(any(target_os="macos", target_os="linux"))]
extern crate simple_stream;
#[cfg(any(target_os="macos", target_os="linux"))]
extern crate num_cpus;
#[cfg(target_os = "linux")]
extern crate epoll;

#[cfg(any(target_os="macos", target_os="linux"))]
pub mod client;
#[cfg(target_os = "linux")]
pub mod server;
<commit_msg>Debugging<commit_after>// Copyright 2015 Nathan Sizemore <nathanrsizemore@gmail.com>
//
// 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/.
//
// This Source Code Form is "Incompatible
// With Secondary Licenses", as defined by
// the Mozilla Public License, v. 2.0.

#![allow(dead_code)]

#[cfg(any(target_os="macos", target_os="linux"))]
extern crate libc;
#[cfg(any(target_os="macos", target_os="linux"))]
extern crate rand;
#[cfg(any(target_os="macos", target_os="linux"))]
extern crate simple_stream;
#[cfg(any(target_os="macos", target_os="linux"))]
extern crate num_cpus;
#[cfg(target_os = "linux")]
extern crate epoll;

#[cfg(any(target_os="macos", target_os="linux"))]
pub mod client;
#[cfg(target_os = "linux")]
pub mod server;
<|endoftext|>


<commit_before>#[link(name="cudd")]
#[repr(C)]
pub struct Mn { ___x:u32 }

#[repr(C)]
pub struct Node { ___x:u32 }

extern {
    pub fn Cudd_Init(numVars:u64, numVarsZ:u64, numSlots:u64,
                     cacheSize:u64, maxMemory:u64) -> *mut Mn;
    pub fn Cudd_ReadLogicZero(mn:*mut Mn) -> *mut Node;
    pub fn Cudd_bddIthVar(mn:*mut Mn, n:u64) -> *mut Node;
    pub fn Cudd_bddAnd(mn:*mut Mn,
                       a:*mut Node, b:*mut Node) -> *mut Node;
    pub fn Cudd_bddOr(mn:*mut Mn,
                      a:*mut Node, b:*mut Node) -> *mut Node;
    pub fn Cudd_bddXor(mn:*mut Mn,
                       a:*mut Node, b:*mut Node) -> *mut Node;
    pub fn Cudd_bddNand(mn:*mut Mn,
                        a:*mut Node, b:*mut Node) -> *mut Node;
    pub fn Cudd_bddNor(mn:*mut Mn,
                       a:*mut Node, b:*mut Node) -> *mut Node;
    pub fn Cudd_bddXnor(mn:*mut Mn,
                        a:*mut Node, b:*mut Node) -> *mut Node;
    pub fn Cudd_bddIte(mn:*mut Mn, a:*mut Node,
                       b:*mut Node, c:*mut Node) -> *mut Node;
    pub fn Cudd_PrintDebug(mn:*mut Mn, a:*mut Node,
                           n:u64, pr:u64) -> u64;
}

pub fn not(mn:*mut Mn, a:*mut Node) -> *mut Node {
    // TODO: should be "toggle lowest bit in pointer"
    unsafe {
        Cudd_bddXnor(mn, a, Cudd_ReadLogicZero(mn))
    }
}

#[test]
fn it_works() {
    unsafe {
        let mn = Cudd_Init(0, 0, 256, 262144, 0);
        let z = Cudd_ReadLogicZero(mn);
        let a = Cudd_bddIthVar(mn, 0);
        let b = Cudd_bddIthVar(mn, 1);
        let c = Cudd_bddIthVar(mn, 2);
        let d = Cudd_bddIthVar(mn, 3);

        let ab = Cudd_bddOr(mn, a, not(mn, b));
        let bc = Cudd_bddNand(mn, b, c);
        let ad = Cudd_bddOr(mn, not(mn, a), not(mn, d));
        let res = Cudd_bddAnd(mn,
                              Cudd_bddAnd(mn, ab, bc),
                              ad);

        Cudd_PrintDebug(mn, res, 1000, 2);
        // TODO: prints "4.6e300 minterms", what?

        println!("zero: {:?}", z);
        println!("ok");
        panic!();
    }
}

<commit_msg>panic<commit_after>#[link(name="cudd")]
#[repr(C)]
pub struct Mn { ___x:u32 }

#[repr(C)]
pub struct Node { ___x:u32 }

extern {
    pub fn Cudd_Init(numVars:u64, numVarsZ:u64, numSlots:u64,
                     cacheSize:u64, maxMemory:u64) -> *mut Mn;
    pub fn Cudd_ReadLogicZero(mn:*mut Mn) -> *mut Node;
    pub fn Cudd_bddIthVar(mn:*mut Mn, n:u64) -> *mut Node;
    pub fn Cudd_bddAnd(mn:*mut Mn,
                       a:*mut Node, b:*mut Node) -> *mut Node;
    pub fn Cudd_bddOr(mn:*mut Mn,
                      a:*mut Node, b:*mut Node) -> *mut Node;
    pub fn Cudd_bddXor(mn:*mut Mn,
                       a:*mut Node, b:*mut Node) -> *mut Node;
    pub fn Cudd_bddNand(mn:*mut Mn,
                        a:*mut Node, b:*mut Node) -> *mut Node;
    pub fn Cudd_bddNor(mn:*mut Mn,
                       a:*mut Node, b:*mut Node) -> *mut Node;
    pub fn Cudd_bddXnor(mn:*mut Mn,
                        a:*mut Node, b:*mut Node) -> *mut Node;
    pub fn Cudd_bddIte(mn:*mut Mn, a:*mut Node,
                       b:*mut Node, c:*mut Node) -> *mut Node;
    pub fn Cudd_PrintDebug(mn:*mut Mn, a:*mut Node,
                           n:u64, pr:u64) -> u64;
}

pub fn not(mn:*mut Mn, a:*mut Node) -> *mut Node {
    // TODO: should be "toggle lowest bit in pointer"
    unsafe {
        Cudd_bddXnor(mn, a, Cudd_ReadLogicZero(mn))
    }
}

#[test]
fn it_works() {
    unsafe {
        let mn = Cudd_Init(0, 0, 256, 262144, 0);
        let z = Cudd_ReadLogicZero(mn);
        let a = Cudd_bddIthVar(mn, 0);
        let b = Cudd_bddIthVar(mn, 1);
        let c = Cudd_bddIthVar(mn, 2);
        let d = Cudd_bddIthVar(mn, 3);

        let ab = Cudd_bddOr(mn, a, not(mn, b));
        let bc = Cudd_bddNand(mn, b, c);
        let ad = Cudd_bddOr(mn, not(mn, a), not(mn, d));
        let res = Cudd_bddAnd(mn,
                              Cudd_bddAnd(mn, ab, bc),
                              ad);

        Cudd_PrintDebug(mn, res, 1000, 2);
        // TODO: prints "4.6e300 minterms", what?

        println!("zero: {:?}", z);
        println!("ok");
        // panic!();
    }
}

<|endoftext|>


<commit_before>#![no_std]
#[macro_export]
macro_rules! simple_bitfield_field {
   ($name:ident, $t:ty,) => {};
   ($name:ident, $t:ty, _, $setter:ident: $msb:expr, $lsb:expr, $($rest:tt)*) => {
       impl $name {
           pub fn $setter(&mut self, value: $t) {
               self.set_range_($msb, $lsb, value);
           }
       }
       simple_bitfield_field!{$name, $t, $($rest)*}
   };
   ($name:ident, $t:ty, _, $setter:ident: $msb:expr, $lsb:expr; $count:expr, $($rest:tt)*) => {
       impl $name {
           pub fn $setter(&mut self, index: usize, value: $t) {
               debug_assert!(index < $count);
               let width = $msb - $lsb + 1;
               let lsb = $lsb + index*width;
               let msb = lsb + width - 1;
               self.set_range_(msb, lsb, value);
           }
       }
       simple_bitfield_field!{$name, $t, $($rest)*}
   };
   ($name:ident, $t:ty, $getter:ident, _: $msb:expr, $lsb:expr, $($rest:tt)*) => {
       impl $name {
           pub fn $getter(&self) -> $t {
               self.get_range_($msb, $lsb)
           }
       }
       simple_bitfield_field!{$name, $t, $($rest)*}
   };
   ($name:ident, $t:ty, $getter:ident, _: $msb:expr, $lsb:expr; $count:expr, $($rest:tt)*) => {
       impl $name {
           pub fn $getter(&self, index: usize) -> $t {
               debug_assert!(index < $count);
               let width = $msb - $lsb + 1;
               let lsb = $lsb + index*width;
               let msb = lsb + width - 1;
               self.get_range_(msb, lsb)
           }
       }
       simple_bitfield_field!{$name, $t, $($rest)*}
   };
   ($name:ident, $t:ty, $getter:ident, $setter:ident: $msb:expr, $lsb:expr, $($rest:tt)*) => {
       simple_bitfield_field!{$name, $t, $getter, _: $msb, $lsb, }
       simple_bitfield_field!{$name, $t, _, $setter: $msb, $lsb, }
       simple_bitfield_field!{$name, $t, $($rest)*}
   };
   ($name:ident, $t:ty, $getter:ident, $setter:ident: $msb:expr, $lsb:expr; $count:expr, $($rest:tt)*) => {
         simple_bitfield_field!{$name, $t, $getter, _: $msb, $lsb; $count, }
         simple_bitfield_field!{$name, $t, _, $setter: $msb, $lsb; $count, }
         simple_bitfield_field!{$name, $t, $($rest)*}
   };
}

#[macro_export]
macro_rules! simple_bitfield {
    ($name:ident, $t:ty, $($rest:tt)*) => {
         #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
         #[repr(C)]
         pub struct $name(pub $t);
         impl $name {
             fn get_range_(&self, msb: usize, lsb: usize) -> $t {
                 let bit_len = mem::size_of::<$t>()*8;
                 (self.0 << (bit_len - msb - 1)) >> (bit_len - msb - 1 + lsb)
             }

             fn set_range_(&mut self, msb: usize, lsb: usize, value: $t) {
                 let bit_len = mem::size_of::<$t>()*8;
                 let mask: $t = !(0 as $t) << (bit_len - msb - 1) >> (bit_len - msb - 1 + lsb) << (lsb);
                 self.0 &= !mask;
                 self.0 |= (value << lsb) & mask;
             }
         }
         simple_bitfield_field!{$name, $t, $($rest)*}
    }
}
<commit_msg>Only make one impl block<commit_after>#![no_std]
#[macro_export]
macro_rules! simple_bitfield_field {
   ($t:ty,) => {};
   ($t:ty, _, $setter:ident: $msb:expr, $lsb:expr, $($rest:tt)*) => {
       pub fn $setter(&mut self, value: $t) {
           self.set_range_($msb, $lsb, value);
       }
       simple_bitfield_field!{$t, $($rest)*}
   };
   ($t:ty, _, $setter:ident: $msb:expr, $lsb:expr; $count:expr, $($rest:tt)*) => {
       pub fn $setter(&mut self, index: usize, value: $t) {
           debug_assert!(index < $count);
           let width = $msb - $lsb + 1;
           let lsb = $lsb + index*width;
           let msb = lsb + width - 1;
           self.set_range_(msb, lsb, value);
       }
       simple_bitfield_field!{$t, $($rest)*}
   };
   ($t:ty, $getter:ident, _: $msb:expr, $lsb:expr, $($rest:tt)*) => {
       pub fn $getter(&self) -> $t {
           self.get_range_($msb, $lsb)
       }
       simple_bitfield_field!{$t, $($rest)*}
   };
   ($t:ty, $getter:ident, _: $msb:expr, $lsb:expr; $count:expr, $($rest:tt)*) => {
       pub fn $getter(&self, index: usize) -> $t {
           debug_assert!(index < $count);
           let width = $msb - $lsb + 1;
           let lsb = $lsb + index*width;
           let msb = lsb + width - 1;
           self.get_range_(msb, lsb)
       }
       simple_bitfield_field!{$t, $($rest)*}
   };
   ($t:ty, $getter:ident, $setter:ident: $msb:expr, $lsb:expr, $($rest:tt)*) => {
       simple_bitfield_field!{$t, $getter, _: $msb, $lsb, }
       simple_bitfield_field!{$t, _, $setter: $msb, $lsb, }
       simple_bitfield_field!{$t, $($rest)*}
   };
   ($t:ty, $getter:ident, $setter:ident: $msb:expr, $lsb:expr; $count:expr, $($rest:tt)*) => {
         simple_bitfield_field!{$t, $getter, _: $msb, $lsb; $count, }
         simple_bitfield_field!{$t, _, $setter: $msb, $lsb; $count, }
         simple_bitfield_field!{$t, $($rest)*}
   };
}

#[macro_export]
macro_rules! simple_bitfield {
    ($name:ident, $t:ty, $($rest:tt)*) => {
         #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
         #[repr(C)]
         pub struct $name(pub $t);
         impl $name {
             fn get_range_(&self, msb: usize, lsb: usize) -> $t {
                 let bit_len = mem::size_of::<$t>()*8;
                 (self.0 << (bit_len - msb - 1)) >> (bit_len - msb - 1 + lsb)
             }

             fn set_range_(&mut self, msb: usize, lsb: usize, value: $t) {
                 let bit_len = mem::size_of::<$t>()*8;
                 let mask: $t = !(0 as $t) << (bit_len - msb - 1) >> (bit_len - msb - 1 + lsb) << (lsb);
                 self.0 &= !mask;
                 self.0 |= (value << lsb) & mask;
             }
             simple_bitfield_field!{$t, $($rest)*}
         }
    }
}
<|endoftext|>


<commit_before>#[allow(dead_code)]
#[allow(non_camel_case_types)]

/**
 * Abstraction of libnl core functionality
 */
#[link(name="nl-3")]
extern "C" {
	// Exposed socket functions
	fn nl_socket_alloc() -> *const nl_sock;
	fn nl_socket_free(socket: *const nl_sock);
	fn nl_socket_get_fd(socket: *const nl_sock) -> i32;
	fn nl_socket_set_buffer_size(socket: *const nl_sock, rxbuf: int, txbuf: int) -> i32;

	fn nl_socket_set_local_port(socket: *const nl_sock, port: u32);
	fn nl_socket_get_local_port(socket: *const nl_sock) -> u32;

	fn nl_connect(socket: *const nl_sock, protocol: u32) -> i32;
	fn nl_close(socket: *const nl_sock);

	fn nl_socket_set_cb(socket: *const nl_sock, cb: *const nl_cb);
	fn nl_socket_get_cb(socket: *const nl_sock) -> nl_cb;

	// Exposed socket transceiver
	fn nl_send_simple(socket: *const nl_sock, msg_type: int, flags: int, buf: *const u8, size: int) -> int;
	// Exposed msg functions
	fn nlmsg_alloc() -> *const nl_msg;
	fn nlmsg_free(msg: *const nl_msg);

}

// exposed structures - these are wrapped
struct nl_sock;
struct nl_msg;
struct nl_cb;

// RSNL datatypes wrapping the libnl data structures
pub struct socket {
	ptr: *const nl_sock
}

/* library version of nlmsghdr
 * the name is altered to differentiate
 * between the native libnl and rsnl
 */
pub struct rsnl_msghdr {
	nlmsg_len: u32,
	nlmsg_type: u16,
	nlmsg_flags: u16,
	nlmsg_seq: u32,
	nlmsg_pid: u32
}

pub struct msg {
	ptr: *const nl_msg
}

pub enum NetlinkProtocol {
	route,
	unused,
	usersock,
	firewall,
	sock_diag,
	nflog,
	xfrm,
	selinux,
	iscsi,
	audit,
	fib_lookup,
	connector,
	netfilter,
	ip6_fw,
	dnrtmsg,
	kobject_uevent,
	DMEVENTS,
	scsitransport,
	ecryptfs,
	rdma,
	crypto,
	zu= 30
}

impl socket {
	pub fn new() -> socket {
		unsafe {
			let nlptr = nl_socket_alloc();
			socket {
				ptr: nlptr
			}
		}
	}

	pub fn free(&self) {
		unsafe {
			nl_socket_free(self.ptr)
		}
	}

	pub fn set_buffer_size(&self, rxbuf: int, txbuf: int) -> i32 {
		unsafe {
			nl_socket_set_buffer_size(self.ptr, rxbuf, txbuf)
		}
	}

	pub fn connect(&self, protocol: NetlinkProtocol) -> i32 {
		unsafe { nl_connect(self.ptr, protocol as u32) }
	}

	pub fn close(&self) {
		unsafe{ nl_close(self.ptr) }
	}

	pub fn get_fd(&self) -> i32 {
		unsafe { nl_socket_get_fd(self.ptr) }
	}

	pub fn get_local_port(&self) -> u32 {
		unsafe { nl_socket_get_local_port(self.ptr) }
	}

	pub fn set_local_port(&self, port: u32) {
		unsafe { nl_socket_set_local_port(self.ptr, port) }
	}

	pub fn send_simple(&self, msg_type: int, flags: int, buf: *const u8, size: int) -> int {
		unsafe { nl_send_simple(self.ptr, msg_type, flags, buf, size) }
	}
}

impl msg {
	pub fn new() -> msg {
	unsafe {
		let nlmsg = nlmsg_alloc();
		msg {
			ptr: nlmsg
		}
	}
	}

	pub fn free(&self) {
		unsafe{ nlmsg_free(self.ptr); }
	}
}
<commit_msg>Fix build with recent Rust<commit_after>#![feature(libc)]

#[allow(dead_code)]
#[allow(non_camel_case_types)]

extern crate libc;
use libc::c_int;

/**
 * Abstraction of libnl core functionality
 */
#[link(name="nl-3")]
extern "C" {
	// Exposed socket functions
	fn nl_socket_alloc() -> *const nl_sock;
	fn nl_socket_free(socket: *const nl_sock);
	fn nl_socket_get_fd(socket: *const nl_sock) -> i32;
	fn nl_socket_set_buffer_size(socket: *const nl_sock, rxbuf: c_int, txbuf: c_int) -> i32;

	fn nl_socket_set_local_port(socket: *const nl_sock, port: u32);
	fn nl_socket_get_local_port(socket: *const nl_sock) -> u32;

	fn nl_connect(socket: *const nl_sock, protocol: u32) -> i32;
	fn nl_close(socket: *const nl_sock);

	fn nl_socket_set_cb(socket: *const nl_sock, cb: *const nl_cb);
	fn nl_socket_get_cb(socket: *const nl_sock) -> nl_cb;

	// Exposed socket transceiver
	fn nl_send_simple(socket: *const nl_sock, msg_type: c_int, flags: c_int, buf: *const u8, size: c_int) -> i32;
	// Exposed msg functions
	fn nlmsg_alloc() -> *const nl_msg;
	fn nlmsg_free(msg: *const nl_msg);

}

// exposed structures - these are wrapped
struct nl_sock;
struct nl_msg;
struct nl_cb;

// RSNL datatypes wrapping the libnl data structures
pub struct socket {
	ptr: *const nl_sock
}

/* library version of nlmsghdr
 * the name is altered to differentiate
 * between the native libnl and rsnl
 */
pub struct rsnl_msghdr {
	nlmsg_len: u32,
	nlmsg_type: u16,
	nlmsg_flags: u16,
	nlmsg_seq: u32,
	nlmsg_pid: u32
}

pub struct msg {
	ptr: *const nl_msg
}

pub enum NetlinkProtocol {
	route,
	unused,
	usersock,
	firewall,
	sock_diag,
	nflog,
	xfrm,
	selinux,
	iscsi,
	audit,
	fib_lookup,
	connector,
	netfilter,
	ip6_fw,
	dnrtmsg,
	kobject_uevent,
	DMEVENTS,
	scsitransport,
	ecryptfs,
	rdma,
	crypto,
	zu= 30
}

impl socket {
	pub fn new() -> socket {
		unsafe {
			let nlptr = nl_socket_alloc();
			socket {
				ptr: nlptr
			}
		}
	}

	pub fn free(&self) {
		unsafe {
			nl_socket_free(self.ptr)
		}
	}

	pub fn set_buffer_size(&self, rxbuf: c_int, txbuf: c_int) -> i32 {
		unsafe {
			nl_socket_set_buffer_size(self.ptr, rxbuf, txbuf)
		}
	}

	pub fn connect(&self, protocol: NetlinkProtocol) -> i32 {
		unsafe { nl_connect(self.ptr, protocol as u32) }
	}

	pub fn close(&self) {
		unsafe{ nl_close(self.ptr) }
	}

	pub fn get_fd(&self) -> i32 {
		unsafe { nl_socket_get_fd(self.ptr) }
	}

	pub fn get_local_port(&self) -> u32 {
		unsafe { nl_socket_get_local_port(self.ptr) }
	}

	pub fn set_local_port(&self, port: u32) {
		unsafe { nl_socket_set_local_port(self.ptr, port) }
	}

	pub fn send_simple(&self, msg_type: c_int, flags: c_int, buf: *const u8, size: c_int) -> c_int {
		unsafe { nl_send_simple(self.ptr, msg_type, flags, buf, size) }
	}
}

impl msg {
	pub fn new() -> msg {
	unsafe {
		let nlmsg = nlmsg_alloc();
		msg {
			ptr: nlmsg
		}
	}
	}

	pub fn free(&self) {
		unsafe{ nlmsg_free(self.ptr); }
	}
}
<|endoftext|>


<commit_before>#![feature(btree_range, collections_bound)]

#[macro_use]
extern crate log;

use std::collections::{BTreeMap, BTreeSet, Bound};

/// The version type alias used to uniquely reference migrations.
pub type Version = i64;

/// All migrations will implement this trait, and a migration trait specific to the chosen adapter.
/// This trait defines the metadata for tracking migration sequence and for human reference.
pub trait Migration {
    /// An ordered (but not necessarily sequential), unique identifier for this migration.
    /// Registered migrations will be applied in ascending order by version.
    fn version(&self) -> Version;

    /// A message describing the effects of this migration.
    fn description(&self) -> &'static str;
}

/// Efficiently implement the `Migration` trait for a given type.
///
/// ## Example
///
/// ```rust
/// # #[macro_use]
/// # extern crate schemamama;
/// struct MyMigration;
/// migration!(MyMigration, 100, "create some lovely database tables");
///
/// # fn main() {
/// use schemamama::Migration;
/// let m = MyMigration;
/// assert_eq!(m.version(), 100);
/// assert_eq!(m.description(), "create some lovely database tables");
/// # }
/// ```
#[macro_export]
macro_rules! migration {
    ($ty:ident, $version:expr, $description:expr) => {
        impl $crate::Migration for $ty {
            fn version(&self) -> $crate::Version { $version }
            fn description(&self) -> &'static str { $description }
        }
    }
}

/// Use this trait to connect the migrator to your chosen database technology.
pub trait Adapter {
    /// An alias to a specific trait that extends `Migration`. Typically, the aforementioned trait
    /// will declare functions that the adapter will use to migrate upwards and downwards.
    type MigrationType: Migration + ?Sized;

    /// Returns the latest migration version, or `None` if no migrations have been recorded. Can
    /// panic if necessary.
    fn current_version(&self) -> Option<Version>;

    /// Returns a set of the versions of all of the currently applied migrations. Can panic if
    /// necessary.
    fn migrated_versions(&self) -> BTreeSet<Version>;

    /// Applies the specified migration. Can panic if necessary.
    fn apply_migration(&self, migration: &Self::MigrationType);

    /// Reverts the specified migration. Can panic if necessary.
    fn revert_migration(&self, migration: &Self::MigrationType);
}

/// Maintains an ordered collection of migrations to utilize.
pub struct Migrator<T: Adapter> {
    adapter: T,
    migrations: BTreeMap<Version, Box<T::MigrationType>>
}

impl<T: Adapter> Migrator<T> {
    /// Create a migrator with a given adapter.
    pub fn new(adapter: T) -> Migrator<T> {
        Migrator { adapter: adapter, migrations: BTreeMap::new() }
    }

    /// Get a reference to the adapter.
    pub fn adapter(&self) -> &T {
        &self.adapter
    }

    /// Register a migration. If a migration with the same version is already registered, a warning
    /// is logged and the registration fails.
    pub fn register(&mut self, migration: Box<T::MigrationType>) {
        let version = migration.version();
        if self.version_registered(version) {
            warn!("Migration with version {:?} is already registered", version);
        } else {
            self.migrations.insert(version, migration);
        }
    }

    /// Returns true is a migration with the provided version has been registered.
    pub fn version_registered(&self, version: Version) -> bool {
        self.migrations.contains_key(&version)
    }

    /// Returns the lowest version of the registered migrations, or `None` if no migrations have
    /// been registered.
    pub fn first_version(&self) -> Option<Version> {
        self.migrations.keys().next().map(|v| *v)
    }

    /// Returns the highest version of the registered migrations, or `None` if no migrations have
    /// been registered.
    pub fn last_version(&self) -> Option<Version> {
        self.migrations.keys().last().map(|v| *v)
    }

    /// Returns the latest migration version, or `None` if no migrations have been recorded.
    ///
    /// ## Panics
    ///
    /// Panics if there is an underlying problem retrieving the current version from the adapter.
    pub fn current_version(&self) -> Option<Version> {
        self.adapter.current_version()
    }

    /// Returns a set of the versions of all of the currently applied migrations.
    ///
    /// ## Panics
    ///
    /// Panics if there is an underlying problem retrieving the current versions from the adapter.
    pub fn migrated_versions(&self) -> BTreeSet<Version> {
        self.adapter.migrated_versions()
    }

    /// Rollback to the specified version (exclusive), or rollback to the state before any
    /// registered migrations were applied if `None` is specified.
    ///
    /// ## Panics
    ///
    /// Panics if there is an underlying problem reverting any of the matched migrations.
    pub fn down(&self, to: Option<Version>) {
        let current_version = self.current_version();
        let migrated_versions = self.migrated_versions();
        let source = match current_version {
            Some(ref version) => Bound::Included(version),
            None => return
        };
        let destination = match to {
            Some(ref version) => Bound::Excluded(version),
            None => Bound::Unbounded
        };
        for (version, migration) in self.migrations.range(destination, source).rev() {
            if !migrated_versions.contains(version) {
                continue;
            }
            info!("Reverting migration {:?}: {}", version, migration.description());
            self.adapter.revert_migration(migration);
        }
    }

    /// Migrate to the specified version (inclusive).
    ///
    /// ## Panics
    ///
    /// Panics if there is an underlying problem applying any of the matched migrations.
    pub fn up(&self, to: Version) {
        let migrated_versions = self.migrated_versions();
        let destination = Bound::Included(&to);
        for (version, migration) in self.migrations.range(Bound::Unbounded, destination) {
            if migrated_versions.contains(version) {
                continue;
            }
            info!("Applying migration {:?}: {}", version, migration.description());
            self.adapter.apply_migration(migration);
        }
    }
}
<commit_msg>(enhance) Remove nightly features<commit_after>#[macro_use]
extern crate log;

use std::collections::{BTreeMap, BTreeSet};

/// The version type alias used to uniquely reference migrations.
pub type Version = i64;

/// All migrations will implement this trait, and a migration trait specific to the chosen adapter.
/// This trait defines the metadata for tracking migration sequence and for human reference.
pub trait Migration {
    /// An ordered (but not necessarily sequential), unique identifier for this migration.
    /// Registered migrations will be applied in ascending order by version.
    fn version(&self) -> Version;

    /// A message describing the effects of this migration.
    fn description(&self) -> &'static str;
}

/// Efficiently implement the `Migration` trait for a given type.
///
/// ## Example
///
/// ```rust
/// # #[macro_use]
/// # extern crate schemamama;
/// struct MyMigration;
/// migration!(MyMigration, 100, "create some lovely database tables");
///
/// # fn main() {
/// use schemamama::Migration;
/// let m = MyMigration;
/// assert_eq!(m.version(), 100);
/// assert_eq!(m.description(), "create some lovely database tables");
/// # }
/// ```
#[macro_export]
macro_rules! migration {
    ($ty:ident, $version:expr, $description:expr) => {
        impl $crate::Migration for $ty {
            fn version(&self) -> $crate::Version { $version }
            fn description(&self) -> &'static str { $description }
        }
    }
}

/// Use this trait to connect the migrator to your chosen database technology.
pub trait Adapter {
    /// An alias to a specific trait that extends `Migration`. Typically, the aforementioned trait
    /// will declare functions that the adapter will use to migrate upwards and downwards.
    type MigrationType: Migration + ?Sized;

    /// Returns the latest migration version, or `None` if no migrations have been recorded. Can
    /// panic if necessary.
    fn current_version(&self) -> Option<Version>;

    /// Returns a set of the versions of all of the currently applied migrations. Can panic if
    /// necessary.
    fn migrated_versions(&self) -> BTreeSet<Version>;

    /// Applies the specified migration. Can panic if necessary.
    fn apply_migration(&self, migration: &Self::MigrationType);

    /// Reverts the specified migration. Can panic if necessary.
    fn revert_migration(&self, migration: &Self::MigrationType);
}

/// Maintains an ordered collection of migrations to utilize.
pub struct Migrator<T: Adapter> {
    adapter: T,
    migrations: BTreeMap<Version, Box<T::MigrationType>>
}

impl<T: Adapter> Migrator<T> {
    /// Create a migrator with a given adapter.
    pub fn new(adapter: T) -> Migrator<T> {
        Migrator { adapter: adapter, migrations: BTreeMap::new() }
    }

    /// Get a reference to the adapter.
    pub fn adapter(&self) -> &T {
        &self.adapter
    }

    /// Register a migration. If a migration with the same version is already registered, a warning
    /// is logged and the registration fails.
    pub fn register(&mut self, migration: Box<T::MigrationType>) {
        let version = migration.version();
        if self.version_registered(version) {
            warn!("Migration with version {:?} is already registered", version);
        } else {
            self.migrations.insert(version, migration);
        }
    }

    /// Returns true is a migration with the provided version has been registered.
    pub fn version_registered(&self, version: Version) -> bool {
        self.migrations.contains_key(&version)
    }

    /// Returns the lowest version of the registered migrations, or `None` if no migrations have
    /// been registered.
    pub fn first_version(&self) -> Option<Version> {
        self.migrations.keys().next().map(|v| *v)
    }

    /// Returns the highest version of the registered migrations, or `None` if no migrations have
    /// been registered.
    pub fn last_version(&self) -> Option<Version> {
        self.migrations.keys().last().map(|v| *v)
    }

    /// Returns the latest migration version, or `None` if no migrations have been recorded.
    ///
    /// ## Panics
    ///
    /// Panics if there is an underlying problem retrieving the current version from the adapter.
    pub fn current_version(&self) -> Option<Version> {
        self.adapter.current_version()
    }

    /// Returns a set of the versions of all of the currently applied migrations.
    ///
    /// ## Panics
    ///
    /// Panics if there is an underlying problem retrieving the current versions from the adapter.
    pub fn migrated_versions(&self) -> BTreeSet<Version> {
        self.adapter.migrated_versions()
    }

    /// Rollback to the specified version (exclusive), or rollback to the state before any
    /// registered migrations were applied if `None` is specified.
    ///
    /// ## Panics
    ///
    /// Panics if there is an underlying problem reverting any of the matched migrations.
    pub fn down(&self, to: Option<Version>) {
        let from = match self.current_version() {
            Some(version) => version,
            None => return,
        };

        let migrated_versions = self.migrated_versions();
        let targets = self.migrations.iter().rev().filter(|&(v, _)| within_range(*v, to, from));
        for (version, migration) in targets {
            if !migrated_versions.contains(version) {
                continue;
            }
            info!("Reverting migration {:?}: {}", version, migration.description());
            self.adapter.revert_migration(migration);
        }
    }

    /// Migrate to the specified version (inclusive).
    ///
    /// ## Panics
    ///
    /// Panics if there is an underlying problem applying any of the matched migrations.
    pub fn up(&self, to: Version) {
        let migrated_versions = self.migrated_versions();
        let targets = self.migrations.iter().filter(|&(v, _)| within_range(*v, None, to));
        for (version, migration) in targets {
            if migrated_versions.contains(version) {
                continue;
            }
            info!("Applying migration {:?}: {}", version, migration.description());
            self.adapter.apply_migration(migration);
        }
    }
}

// Tests whether a `Version` is within a range defined by the exclusive `low` and the inclusive
// `high` bounds.
fn within_range(version: Version, low: Option<Version>, high: Version) -> bool {
    // Inclusive upper bound:
    if version > high {
        return false;
    }

    // Exclusive lower bound:
    match low {
        Some(low) => version > low,
        None => true,
    }
}

#[test]
fn test_within_range() {
    assert!(within_range(0, None, 5));
    assert!(within_range(5, None, 5));
    assert!(!within_range(6, None, 5));
    assert!(!within_range(1, Some(2), 5));
    assert!(!within_range(2, Some(2), 5));
    assert!(within_range(3, Some(2), 5));
    assert!(within_range(5, Some(2), 5));
    assert!(!within_range(6, Some(2), 5));
}
<|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]: http://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.15")]
#![deny(missing_docs)]
// Suppressed clippy_pedantic lints
#![allow(
    // private Deserializer::next
    clippy::should_implement_trait,
    // things are often more readable this way
    clippy::cast_lossless,
    clippy::module_name_repetitions,
    clippy::option_if_let_else,
    clippy::single_match_else,
    clippy::use_self,
    // code is acceptable
    clippy::cast_possible_wrap,
    clippy::cast_precision_loss,
    clippy::cast_sign_loss,
    // not practical
    clippy::indexing_slicing,
    clippy::missing_docs_in_private_items,
    // not stable
    clippy::checked_conversions,
    clippy::empty_enum,
    // meh, some things won't fail
    clippy::unwrap_used,
    // noisy
    clippy::missing_errors_doc,
    clippy::must_use_candidate,
    // we support older compilers
    clippy::manual_strip,
    clippy::match_like_matches_macro,
)]

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};
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 needless_pass_by_value clippy pedantic 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]: http://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.15")]
#![deny(missing_docs)]
// Suppressed clippy_pedantic lints
#![allow(
    // private Deserializer::next
    clippy::should_implement_trait,
    // things are often more readable this way
    clippy::cast_lossless,
    clippy::module_name_repetitions,
    clippy::needless_pass_by_value,
    clippy::option_if_let_else,
    clippy::single_match_else,
    clippy::use_self,
    // code is acceptable
    clippy::cast_possible_wrap,
    clippy::cast_precision_loss,
    clippy::cast_sign_loss,
    // not practical
    clippy::indexing_slicing,
    clippy::missing_docs_in_private_items,
    // not stable
    clippy::checked_conversions,
    clippy::empty_enum,
    // meh, some things won't fail
    clippy::unwrap_used,
    // noisy
    clippy::missing_errors_doc,
    clippy::must_use_candidate,
    // we support older compilers
    clippy::manual_strip,
    clippy::match_like_matches_macro,
)]

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