<commit_before>use std::fs;
use std::io;
use std::io::Write;
use std::env;
use std::path::Path;

use runas;
use console::{style, user_attended};
use app_dirs;
use serde_json;
use chrono::{DateTime, Duration, Utc};
use failure::{Error, ResultExt};

use api::{Api, SentryCliRelease};
use config::Config;
use constants::{APP_INFO, VERSION};
use utils::fs::{is_writable, set_executable_mode};
use utils::system::{is_homebrew_install, is_npm_install, QuietExit};

#[cfg(windows)]
fn rename_exe(exe: &Path, downloaded_path: &Path, elevate: bool) -> Result<(), Error> {
    // so on windows you can rename a running executable but you cannot delete it.
    // we move the old executable to a temporary location (this most likely only
    // works if they are on the same FS) and then put the new in place.  This
    // will leave the old executable in the temp path lying around so let's hope
    // that windows cleans up temp files there (spoiler: it does not)
    let tmp = env::temp_dir().join(".sentry-cli.tmp");

    if elevate {
        runas::Command::new("cmd")
            .arg("/c")
            .arg("move")
            .arg(&exe)
            .arg(&tmp)
            .arg("&")
            .arg("move")
            .arg(&downloaded_path)
            .arg(&exe)
            .arg("&")
            .arg("del")
            .arg(&tmp)
            .status()?;
    } else {
        fs::rename(&exe, &tmp)?;
        fs::rename(&downloaded_path, &exe)?;
        fs::remove_file(&tmp).ok();
    }

    Ok(())
}

#[cfg(not(windows))]
fn rename_exe(exe: &Path, downloaded_path: &Path, elevate: bool) -> Result<(), Error> {
    if elevate {
        println!("Need to sudo to overwrite {}", exe.display());
        runas::Command::new("mv")
            .arg(&downloaded_path)
            .arg(&exe)
            .status()?;
    } else {
        fs::rename(&downloaded_path, &exe)?;
    }
    Ok(())
}

#[derive(Default, Serialize, Deserialize)]
pub struct LastUpdateCheck {
    pub last_check_timestamp: Option<DateTime<Utc>>,
    pub last_check_version: Option<String>,
    pub last_fetched_version: Option<String>,
}

impl LastUpdateCheck {
    pub fn update_for_info(&mut self, ui: SentryCliUpdateInfo) {
        self.last_check_timestamp = Some(Utc::now());
        self.last_check_version = Some(ui.current_version().to_string());
        self.last_fetched_version = Some(ui.latest_version().to_string());
    }

    pub fn should_run_check(&self) -> bool {
        if_chain! {
            if let Some(ts) = self.last_check_timestamp;
            if let Some(ref ver) = self.last_check_version;
            then {
                ver.as_str() != VERSION || ts < Utc::now() - Duration::hours(12)
            } else {
                true
            }
        }
    }

    pub fn is_outdated(&self) -> bool {
        if_chain! {
            if let Some(ref release_v) = self.last_fetched_version;
            if let Some(ref check_v) = self.last_check_version;
            then {
                release_v.as_str() != VERSION &&
                check_v.as_str() == VERSION
            } else {
                false
            }
        }
    }

    pub fn latest_version(&self) -> &str {
        self.last_fetched_version
            .as_ref()
            .map(|x| x.as_str())
            .unwrap_or("0.0")
    }
}

pub struct SentryCliUpdateInfo {
    latest_release: Option<SentryCliRelease>,
}

impl SentryCliUpdateInfo {
    pub fn have_version_info(&self) -> bool {
        self.latest_release.is_some()
    }

    pub fn is_latest_version(&self) -> bool {
        self.latest_version() == VERSION
    }

    pub fn current_version(&self) -> &str {
        VERSION
    }

    pub fn latest_version(&self) -> &str {
        if let Some(ref rel) = self.latest_release {
            rel.version.as_str()
        } else {
            "0.0"
        }
    }

    pub fn download_url(&self) -> Result<&str, Error> {
        if let Some(ref rel) = self.latest_release {
            Ok(rel.download_url.as_str())
        } else {
            bail!("Could not get download URL for latest release.");
        }
    }

    pub fn assert_updatable(&self) -> Result<(), Error> {
        if is_homebrew_install() {
            println!("This installation of sentry-cli is managed through homebrew");
            println!("Please use homebrew to update sentry-cli:");
            println!("");
            println!("{} brew upgrade sentry-cli", style("$").dim());
            return Err(QuietExit(1).into());
        }
        if is_npm_install() {
            println!("This installation of sentry-cli is managed through npm/yarn");
            println!("Please use npm/yearn to update sentry-cli");
            return Err(QuietExit(1).into());
        }
        if self.latest_release.is_none() {
            bail!("Could not get the latest release version.");
        }
        Ok(())
    }

    pub fn download(&self) -> Result<(), Error> {
        let exe = env::current_exe()?;
        let elevate = !is_writable(&exe);
        info!("expecting elevation for update: {}", elevate);
        let tmp_path = if elevate {
            env::temp_dir().join(".sentry-cli.part")
        } else {
            exe.parent().unwrap().join(".sentry-cli.part")
        };
        let mut f = fs::File::create(&tmp_path)?;
        let api = Api::get_current();
        match api.download_with_progress(self.download_url()?, &mut f) {
            Ok(_) => {}
            Err(err) => {
                fs::remove_file(tmp_path).ok();
                bail!(err);
            }
        };

        set_executable_mode(&tmp_path)?;
        rename_exe(&exe, &tmp_path, elevate)?;
        Ok(())
    }
}

pub fn get_latest_sentrycli_release() -> Result<SentryCliUpdateInfo, Error> {
    let api = Api::get_current();
    Ok(SentryCliUpdateInfo {
        latest_release: if let Ok(release) = api.get_latest_sentrycli_release() {
            release
        } else {
            None
        },
    })
}

pub fn can_update_sentrycli() -> bool {
    !is_homebrew_install() && !is_npm_install()
}

fn update_nagger_impl() -> Result<(), Error> {
    let mut path = app_dirs::app_root(app_dirs::AppDataType::UserCache, APP_INFO)
        .with_context(|_| "Could not get cache folder")?;
    path.push("updatecheck");

    let mut check: LastUpdateCheck;
    if let Ok(f) = fs::File::open(&path) {
        check = serde_json::from_reader(io::BufReader::new(f))?;
    } else {
        check = Default::default();
    }

    if check.should_run_check() {
        let ui = get_latest_sentrycli_release()?;
        if ui.have_version_info() {
            check.update_for_info(ui);
            let mut f = fs::File::create(&path)?;
            serde_json::to_writer_pretty(&mut f, &check)?;
            f.write_all(b"\n")?;
        }
    }

    if check.is_outdated() {
        println_stderr!("");
        println_stderr!(
            "{}",
            style(format!(
                "sentry-cli update to {} is available!",
                check.latest_version()
            )).yellow()
        );
        println_stderr!("{}", style("run sentry-cli update to update").dim());
    }

    Ok(())
}

pub fn run_sentrycli_update_nagger() {
    let config = match Config::get_current_opt() {
        Some(config) => config,
        None => return,
    };

    // Only update if we are compiled as unmanaged version (default)
    if cfg!(feature = "managed") {
        return;
    }

    // Do not run update nagger if stdout/stdin is not a terminal
    if !user_attended() {
        return;
    }

    // npm installs do not get an update check.  We might want to relax this later
    // to support update checks for global npm installs but not dependency installs.
    if is_npm_install() {
        return;
    }

    // if the update nagger is disabled, do not run it.
    if config.disable_update_nagger() {
        info!("update nagger was disabled, not running update checks");
        return;
    }

    update_nagger_impl().ok();
}
<commit_msg>feat: Use installation method specific update prompts (#306)<commit_after>use std::fs;
use std::io;
use std::io::Write;
use std::env;
use std::path::Path;

use runas;
use console::{style, user_attended};
use app_dirs;
use serde_json;
use chrono::{DateTime, Duration, Utc};
use failure::{Error, ResultExt};

use api::{Api, SentryCliRelease};
use config::Config;
use constants::{APP_INFO, VERSION};
use utils::fs::{is_writable, set_executable_mode};
use utils::system::{is_homebrew_install, is_npm_install, QuietExit};

#[cfg(windows)]
fn rename_exe(exe: &Path, downloaded_path: &Path, elevate: bool) -> Result<(), Error> {
    // so on windows you can rename a running executable but you cannot delete it.
    // we move the old executable to a temporary location (this most likely only
    // works if they are on the same FS) and then put the new in place.  This
    // will leave the old executable in the temp path lying around so let's hope
    // that windows cleans up temp files there (spoiler: it does not)
    let tmp = env::temp_dir().join(".sentry-cli.tmp");

    if elevate {
        runas::Command::new("cmd")
            .arg("/c")
            .arg("move")
            .arg(&exe)
            .arg(&tmp)
            .arg("&")
            .arg("move")
            .arg(&downloaded_path)
            .arg(&exe)
            .arg("&")
            .arg("del")
            .arg(&tmp)
            .status()?;
    } else {
        fs::rename(&exe, &tmp)?;
        fs::rename(&downloaded_path, &exe)?;
        fs::remove_file(&tmp).ok();
    }

    Ok(())
}

#[cfg(not(windows))]
fn rename_exe(exe: &Path, downloaded_path: &Path, elevate: bool) -> Result<(), Error> {
    if elevate {
        println!("Need to sudo to overwrite {}", exe.display());
        runas::Command::new("mv")
            .arg(&downloaded_path)
            .arg(&exe)
            .status()?;
    } else {
        fs::rename(&downloaded_path, &exe)?;
    }
    Ok(())
}

#[derive(Default, Serialize, Deserialize)]
pub struct LastUpdateCheck {
    pub last_check_timestamp: Option<DateTime<Utc>>,
    pub last_check_version: Option<String>,
    pub last_fetched_version: Option<String>,
}

impl LastUpdateCheck {
    pub fn update_for_info(&mut self, ui: SentryCliUpdateInfo) {
        self.last_check_timestamp = Some(Utc::now());
        self.last_check_version = Some(ui.current_version().to_string());
        self.last_fetched_version = Some(ui.latest_version().to_string());
    }

    pub fn should_run_check(&self) -> bool {
        if_chain! {
            if let Some(ts) = self.last_check_timestamp;
            if let Some(ref ver) = self.last_check_version;
            then {
                ver.as_str() != VERSION || ts < Utc::now() - Duration::hours(12)
            } else {
                true
            }
        }
    }

    pub fn is_outdated(&self) -> bool {
        if_chain! {
            if let Some(ref release_v) = self.last_fetched_version;
            if let Some(ref check_v) = self.last_check_version;
            then {
                release_v.as_str() != VERSION &&
                check_v.as_str() == VERSION
            } else {
                false
            }
        }
    }

    pub fn latest_version(&self) -> &str {
        self.last_fetched_version
            .as_ref()
            .map(|x| x.as_str())
            .unwrap_or("0.0")
    }
}

pub struct SentryCliUpdateInfo {
    latest_release: Option<SentryCliRelease>,
}

impl SentryCliUpdateInfo {
    pub fn have_version_info(&self) -> bool {
        self.latest_release.is_some()
    }

    pub fn is_latest_version(&self) -> bool {
        self.latest_version() == VERSION
    }

    pub fn current_version(&self) -> &str {
        VERSION
    }

    pub fn latest_version(&self) -> &str {
        if let Some(ref rel) = self.latest_release {
            rel.version.as_str()
        } else {
            "0.0"
        }
    }

    pub fn download_url(&self) -> Result<&str, Error> {
        if let Some(ref rel) = self.latest_release {
            Ok(rel.download_url.as_str())
        } else {
            bail!("Could not get download URL for latest release.");
        }
    }

    pub fn assert_updatable(&self) -> Result<(), Error> {
        if is_homebrew_install() {
            println!("This installation of sentry-cli is managed through homebrew");
            println!("Please use homebrew to update sentry-cli:");
            println!("");
            println!("{} brew upgrade sentry-cli", style("$").dim());
            return Err(QuietExit(1).into());
        }
        if is_npm_install() {
            println!("This installation of sentry-cli is managed through npm/yarn");
            println!("Please use npm/yarn to update sentry-cli");
            return Err(QuietExit(1).into());
        }
        if self.latest_release.is_none() {
            bail!("Could not get the latest release version.");
        }
        Ok(())
    }

    pub fn download(&self) -> Result<(), Error> {
        let exe = env::current_exe()?;
        let elevate = !is_writable(&exe);
        info!("expecting elevation for update: {}", elevate);
        let tmp_path = if elevate {
            env::temp_dir().join(".sentry-cli.part")
        } else {
            exe.parent().unwrap().join(".sentry-cli.part")
        };
        let mut f = fs::File::create(&tmp_path)?;
        let api = Api::get_current();
        match api.download_with_progress(self.download_url()?, &mut f) {
            Ok(_) => {}
            Err(err) => {
                fs::remove_file(tmp_path).ok();
                bail!(err);
            }
        };

        set_executable_mode(&tmp_path)?;
        rename_exe(&exe, &tmp_path, elevate)?;
        Ok(())
    }
}

pub fn get_latest_sentrycli_release() -> Result<SentryCliUpdateInfo, Error> {
    let api = Api::get_current();
    Ok(SentryCliUpdateInfo {
        latest_release: if let Ok(release) = api.get_latest_sentrycli_release() {
            release
        } else {
            None
        },
    })
}

pub fn can_update_sentrycli() -> bool {
    !is_homebrew_install() && !is_npm_install()
}

fn update_nagger_impl() -> Result<(), Error> {
    let mut path = app_dirs::app_root(app_dirs::AppDataType::UserCache, APP_INFO)
        .with_context(|_| "Could not get cache folder")?;
    path.push("updatecheck");

    let mut check: LastUpdateCheck;
    if let Ok(f) = fs::File::open(&path) {
        check = serde_json::from_reader(io::BufReader::new(f))?;
    } else {
        check = Default::default();
    }

    if check.should_run_check() {
        let ui = get_latest_sentrycli_release()?;
        if ui.have_version_info() {
            check.update_for_info(ui);
            let mut f = fs::File::create(&path)?;
            serde_json::to_writer_pretty(&mut f, &check)?;
            f.write_all(b"\n")?;
        }
    }

    if check.is_outdated() {
        println_stderr!("");
        println_stderr!(
            "{}",
            style(format!(
                "sentry-cli update to {} is available!",
                check.latest_version()
            )).yellow()
        );
        if is_homebrew_install() {
            println_stderr!("{}", style("run brew upgrade sentry-cli to update").dim());
        } else if is_npm_install() {
            println_stderr!("{}", style("Please use npm/yarn to update sentry-cli").dim())
        } else {
            println_stderr!("{}", style("run sentry-cli update to update").dim());
        }
    }

    Ok(())
}

pub fn run_sentrycli_update_nagger() {
    let config = match Config::get_current_opt() {
        Some(config) => config,
        None => return,
    };

    // Only update if we are compiled as unmanaged version (default)
    if cfg!(feature = "managed") {
        return;
    }

    // Do not run update nagger if stdout/stdin is not a terminal
    if !user_attended() {
        return;
    }

    // npm installs do not get an update check.  We might want to relax this later
    // to support update checks for global npm installs but not dependency installs.
    if is_npm_install() {
        return;
    }

    // if the update nagger is disabled, do not run it.
    if config.disable_update_nagger() {
        info!("update nagger was disabled, not running update checks");
        return;
    }

    update_nagger_impl().ok();
}
<|endoftext|>


<commit_before>// This file is part of the uutils coreutils package.
//
// (c) Jian Zeng <anonymousknight96@gmail.com>
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
//
//! Aims to provide platform-independent methods to obtain login records
//!
//! **ONLY** support linux, macos and freebsd for the time being
//!
//! # Examples:
//!
//! ```
//! use uucore::utmpx::Utmpx;
//! for ut in Utmpx::iter_all_records() {
//!     if ut.is_user_process() {
//!         println!("{}: {}", ut.host(), ut.user())
//!     }
//! }
//! ```
//!
//! Specifying the path to login record:
//!
//! ```
//! use uucore::utmpx::Utmpx;
//! for ut in Utmpx::iter_all_records().read_from("/some/where/else") {
//!     if ut.is_user_process() {
//!         println!("{}: {}", ut.host(), ut.user())
//!     }
//! }
//! ```

use super::libc;
pub extern crate time;
use self::time::{Timespec, Tm};

use std::io::Result as IOResult;
use std::io::Error as IOError;
use std::ptr;
use std::ffi::CString;

pub use self::ut::*;
use libc::utmpx;
// pub use libc::getutxid;
// pub use libc::getutxline;
// pub use libc::pututxline;
pub use libc::getutxent;
pub use libc::setutxent;
pub use libc::endutxent;
#[cfg(any(target_os = "macos", target_os = "linux"))]
pub use libc::utmpxname;
#[cfg(target_os = "freebsd")]
pub unsafe extern "C" fn utmpxname(_file: *const libc::c_char) -> libc::c_int {
    0
}

// In case the c_char array doesn't end with NULL
macro_rules! chars2string {
    ($arr:expr) => (
        $arr.iter().take_while(|i| **i > 0).map(|&i| i as u8 as char).collect::<String>()
    )
}

#[cfg(target_os = "linux")]
mod ut {
    pub static DEFAULT_FILE: &'static str = "/var/run/utmp";

    pub use libc::__UT_LINESIZE as UT_LINESIZE;
    pub use libc::__UT_NAMESIZE as UT_NAMESIZE;
    pub use libc::__UT_HOSTSIZE as UT_HOSTSIZE;
    pub const UT_IDSIZE: usize = 4;

    pub use libc::EMPTY;
    pub use libc::RUN_LVL;
    pub use libc::BOOT_TIME;
    pub use libc::NEW_TIME;
    pub use libc::OLD_TIME;
    pub use libc::INIT_PROCESS;
    pub use libc::LOGIN_PROCESS;
    pub use libc::USER_PROCESS;
    pub use libc::DEAD_PROCESS;
    pub use libc::ACCOUNTING;
}

#[cfg(target_os = "macos")]
mod ut {
    pub static DEFAULT_FILE: &'static str = "/var/run/utmpx";

    pub use libc::_UTX_LINESIZE as UT_LINESIZE;
    pub use libc::_UTX_USERSIZE as UT_NAMESIZE;
    pub use libc::_UTX_HOSTSIZE as UT_HOSTSIZE;
    pub use libc::_UTX_IDSIZE as UT_IDSIZE;

    pub use libc::EMPTY;
    pub use libc::RUN_LVL;
    pub use libc::BOOT_TIME;
    pub use libc::NEW_TIME;
    pub use libc::OLD_TIME;
    pub use libc::INIT_PROCESS;
    pub use libc::LOGIN_PROCESS;
    pub use libc::USER_PROCESS;
    pub use libc::DEAD_PROCESS;
    pub use libc::ACCOUNTING;
    pub use libc::SIGNATURE;
    pub use libc::SHUTDOWN_TIME;
}

#[cfg(target_os = "freebsd")]
mod ut {
    use super::libc;

    pub static DEFAULT_FILE: &'static str = "";

    pub const UT_LINESIZE: usize = 16;
    pub const UT_NAMESIZE: usize = 32;
    pub const UT_IDSIZE: usize = 8;
    pub const UT_HOSTSIZE: usize = 128;

    pub use libc::EMPTY;
    pub use libc::BOOT_TIME;
    pub use libc::OLD_TIME;
    pub use libc::NEW_TIME;
    pub use libc::USER_PROCESS;
    pub use libc::INIT_PROCESS;
    pub use libc::LOGIN_PROCESS;
    pub use libc::DEAD_PROCESS;
    pub use libc::SHUTDOWN_TIME;
}

pub struct Utmpx {
    inner: utmpx,
}

impl Utmpx {
    /// A.K.A. ut.ut_type
    pub fn record_type(&self) -> i16 {
        self.inner.ut_type as i16
    }
    /// A.K.A. ut.ut_pid
    pub fn pid(&self) -> i32 {
        self.inner.ut_pid as i32
    }
    /// A.K.A. ut.ut_id
    pub fn terminal_suffix(&self) -> String {
        chars2string!(self.inner.ut_id)
    }
    /// A.K.A. ut.ut_user
    pub fn user(&self) -> String {
        chars2string!(self.inner.ut_user)
    }
    /// A.K.A. ut.ut_host
    pub fn host(&self) -> String {
        chars2string!(self.inner.ut_host)
    }
    /// A.K.A. ut.ut_line
    pub fn tty_device(&self) -> String {
        chars2string!(self.inner.ut_line)
    }
    /// A.K.A. ut.ut_tv
    pub fn login_time(&self) -> Tm {
        time::at(Timespec::new(
            self.inner.ut_tv.tv_sec as i64,
            self.inner.ut_tv.tv_usec as i32,
        ))
    }
    /// A.K.A. ut.ut_exit
    ///
    /// Return (e_termination, e_exit)
    #[cfg(any(target_os = "linux", target_os = "android"))]
    pub fn exit_status(&self) -> (i16, i16) {
        (self.inner.ut_exit.e_termination, self.inner.ut_exit.e_exit)
    }
    /// A.K.A. ut.ut_exit
    ///
    /// Return (0, 0) on Non-Linux platform
    #[cfg(not(any(target_os = "linux", target_os = "android")))]
    pub fn exit_status(&self) -> (i16, i16) {
        (0, 0)
    }
    /// Consumes the `Utmpx`, returning the underlying C struct utmpx
    pub fn into_inner(self) -> utmpx {
        self.inner
    }
    pub fn is_user_process(&self) -> bool {
        !self.user().is_empty() && self.record_type() == USER_PROCESS
    }

    /// Canonicalize host name using DNS
    pub fn canon_host(&self) -> IOResult<String> {
        const AI_CANONNAME: libc::c_int = 0x2;
        let host = self.host();
        let host = host.split(':').nth(0).unwrap();
        let hints = libc::addrinfo {
            ai_flags: AI_CANONNAME,
            ai_family: 0,
            ai_socktype: 0,
            ai_protocol: 0,
            ai_addrlen: 0,
            ai_addr: ptr::null_mut(),
            ai_canonname: ptr::null_mut(),
            ai_next: ptr::null_mut(),
        };
        let c_host = CString::new(host).unwrap();
        let mut res = ptr::null_mut();
        let status = unsafe {
            libc::getaddrinfo(
                c_host.as_ptr(),
                ptr::null(),
                &hints as *const _,
                &mut res as *mut _,
            )
        };
        if status == 0 {
            let info: libc::addrinfo = unsafe { ptr::read(res as *const _) };
            // http://lists.gnu.org/archive/html/bug-coreutils/2006-09/msg00300.html
            // says Darwin 7.9.0 getaddrinfo returns 0 but sets
            // res->ai_canonname to NULL.
            let ret = if info.ai_canonname.is_null() {
                Ok(String::from(host))
            } else {
                Ok(unsafe { CString::from_raw(info.ai_canonname).into_string().unwrap() })
            };
            unsafe {
                libc::freeaddrinfo(res);
            }
            ret
        } else {
            Err(IOError::last_os_error())
        }
    }
    pub fn iter_all_records() -> UtmpxIter {
        UtmpxIter
    }
}

/// Iterator of login records
pub struct UtmpxIter;

impl UtmpxIter {
    /// Sets the name of the utmpx-format file for the other utmpx functions to access.
    ///
    /// If not set, default record file will be used(file path depends on the target OS)
    pub fn read_from(self, f: &str) -> Self {
        let res = unsafe { utmpxname(CString::new(f).unwrap().as_ptr()) };
        if res != 0 {
            println!("Warning: {}", IOError::last_os_error());
        }
        unsafe {
            setutxent();
        }
        self
    }
}

impl Iterator for UtmpxIter {
    type Item = Utmpx;
    fn next(&mut self) -> Option<Self::Item> {
        unsafe {
            let res = getutxent();
            if !res.is_null() {
                Some(Utmpx {
                    inner: ptr::read(res as *const _),
                })
            } else {
                endutxent();
                None
            }
        }
    }
}
<commit_msg>Fix more clippy warnings and remove redundant 'static<commit_after>// This file is part of the uutils coreutils package.
//
// (c) Jian Zeng <anonymousknight96@gmail.com>
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
//
//! Aims to provide platform-independent methods to obtain login records
//!
//! **ONLY** support linux, macos and freebsd for the time being
//!
//! # Examples:
//!
//! ```
//! use uucore::utmpx::Utmpx;
//! for ut in Utmpx::iter_all_records() {
//!     if ut.is_user_process() {
//!         println!("{}: {}", ut.host(), ut.user())
//!     }
//! }
//! ```
//!
//! Specifying the path to login record:
//!
//! ```
//! use uucore::utmpx::Utmpx;
//! for ut in Utmpx::iter_all_records().read_from("/some/where/else") {
//!     if ut.is_user_process() {
//!         println!("{}: {}", ut.host(), ut.user())
//!     }
//! }
//! ```

use super::libc;
pub extern crate time;
use self::time::{Timespec, Tm};

use std::io::Result as IOResult;
use std::io::Error as IOError;
use std::ptr;
use std::ffi::CString;

pub use self::ut::*;
use libc::utmpx;
// pub use libc::getutxid;
// pub use libc::getutxline;
// pub use libc::pututxline;
pub use libc::getutxent;
pub use libc::setutxent;
pub use libc::endutxent;
#[cfg(any(target_os = "macos", target_os = "linux"))]
pub use libc::utmpxname;
#[cfg(target_os = "freebsd")]
pub unsafe extern "C" fn utmpxname(_file: *const libc::c_char) -> libc::c_int {
    0
}

// In case the c_char array doesn't end with NULL
macro_rules! chars2string {
    ($arr:expr) => (
        $arr.iter().take_while(|i| **i > 0).map(|&i| i as u8 as char).collect::<String>()
    )
}

#[cfg(target_os = "linux")]
mod ut {
    pub static DEFAULT_FILE: &str = "/var/run/utmp";

    pub use libc::__UT_LINESIZE as UT_LINESIZE;
    pub use libc::__UT_NAMESIZE as UT_NAMESIZE;
    pub use libc::__UT_HOSTSIZE as UT_HOSTSIZE;
    pub const UT_IDSIZE: usize = 4;

    pub use libc::EMPTY;
    pub use libc::RUN_LVL;
    pub use libc::BOOT_TIME;
    pub use libc::NEW_TIME;
    pub use libc::OLD_TIME;
    pub use libc::INIT_PROCESS;
    pub use libc::LOGIN_PROCESS;
    pub use libc::USER_PROCESS;
    pub use libc::DEAD_PROCESS;
    pub use libc::ACCOUNTING;
}

#[cfg(target_os = "macos")]
mod ut {
    pub static DEFAULT_FILE: &str = "/var/run/utmpx";

    pub use libc::_UTX_LINESIZE as UT_LINESIZE;
    pub use libc::_UTX_USERSIZE as UT_NAMESIZE;
    pub use libc::_UTX_HOSTSIZE as UT_HOSTSIZE;
    pub use libc::_UTX_IDSIZE as UT_IDSIZE;

    pub use libc::EMPTY;
    pub use libc::RUN_LVL;
    pub use libc::BOOT_TIME;
    pub use libc::NEW_TIME;
    pub use libc::OLD_TIME;
    pub use libc::INIT_PROCESS;
    pub use libc::LOGIN_PROCESS;
    pub use libc::USER_PROCESS;
    pub use libc::DEAD_PROCESS;
    pub use libc::ACCOUNTING;
    pub use libc::SIGNATURE;
    pub use libc::SHUTDOWN_TIME;
}

#[cfg(target_os = "freebsd")]
mod ut {
    use super::libc;

    pub static DEFAULT_FILE: &str = "";

    pub const UT_LINESIZE: usize = 16;
    pub const UT_NAMESIZE: usize = 32;
    pub const UT_IDSIZE: usize = 8;
    pub const UT_HOSTSIZE: usize = 128;

    pub use libc::EMPTY;
    pub use libc::BOOT_TIME;
    pub use libc::OLD_TIME;
    pub use libc::NEW_TIME;
    pub use libc::USER_PROCESS;
    pub use libc::INIT_PROCESS;
    pub use libc::LOGIN_PROCESS;
    pub use libc::DEAD_PROCESS;
    pub use libc::SHUTDOWN_TIME;
}

pub struct Utmpx {
    inner: utmpx,
}

impl Utmpx {
    /// A.K.A. ut.ut_type
    pub fn record_type(&self) -> i16 {
        self.inner.ut_type as i16
    }
    /// A.K.A. ut.ut_pid
    pub fn pid(&self) -> i32 {
        self.inner.ut_pid as i32
    }
    /// A.K.A. ut.ut_id
    pub fn terminal_suffix(&self) -> String {
        chars2string!(self.inner.ut_id)
    }
    /// A.K.A. ut.ut_user
    pub fn user(&self) -> String {
        chars2string!(self.inner.ut_user)
    }
    /// A.K.A. ut.ut_host
    pub fn host(&self) -> String {
        chars2string!(self.inner.ut_host)
    }
    /// A.K.A. ut.ut_line
    pub fn tty_device(&self) -> String {
        chars2string!(self.inner.ut_line)
    }
    /// A.K.A. ut.ut_tv
    pub fn login_time(&self) -> Tm {
        time::at(Timespec::new(
            self.inner.ut_tv.tv_sec as i64,
            self.inner.ut_tv.tv_usec as i32,
        ))
    }
    /// A.K.A. ut.ut_exit
    ///
    /// Return (e_termination, e_exit)
    #[cfg(any(target_os = "linux", target_os = "android"))]
    pub fn exit_status(&self) -> (i16, i16) {
        (self.inner.ut_exit.e_termination, self.inner.ut_exit.e_exit)
    }
    /// A.K.A. ut.ut_exit
    ///
    /// Return (0, 0) on Non-Linux platform
    #[cfg(not(any(target_os = "linux", target_os = "android")))]
    pub fn exit_status(&self) -> (i16, i16) {
        (0, 0)
    }
    /// Consumes the `Utmpx`, returning the underlying C struct utmpx
    pub fn into_inner(self) -> utmpx {
        self.inner
    }
    pub fn is_user_process(&self) -> bool {
        !self.user().is_empty() && self.record_type() == USER_PROCESS
    }

    /// Canonicalize host name using DNS
    pub fn canon_host(&self) -> IOResult<String> {
        const AI_CANONNAME: libc::c_int = 0x2;
        let host = self.host();
        let host = host.split(':').nth(0).unwrap();
        let hints = libc::addrinfo {
            ai_flags: AI_CANONNAME,
            ai_family: 0,
            ai_socktype: 0,
            ai_protocol: 0,
            ai_addrlen: 0,
            ai_addr: ptr::null_mut(),
            ai_canonname: ptr::null_mut(),
            ai_next: ptr::null_mut(),
        };
        let c_host = CString::new(host).unwrap();
        let mut res = ptr::null_mut();
        let status = unsafe {
            libc::getaddrinfo(
                c_host.as_ptr(),
                ptr::null(),
                &hints as *const _,
                &mut res as *mut _,
            )
        };
        if status == 0 {
            let info: libc::addrinfo = unsafe { ptr::read(res as *const _) };
            // http://lists.gnu.org/archive/html/bug-coreutils/2006-09/msg00300.html
            // says Darwin 7.9.0 getaddrinfo returns 0 but sets
            // res->ai_canonname to NULL.
            let ret = if info.ai_canonname.is_null() {
                Ok(String::from(host))
            } else {
                Ok(unsafe { CString::from_raw(info.ai_canonname).into_string().unwrap() })
            };
            unsafe {
                libc::freeaddrinfo(res);
            }
            ret
        } else {
            Err(IOError::last_os_error())
        }
    }
    pub fn iter_all_records() -> UtmpxIter {
        UtmpxIter
    }
}

/// Iterator of login records
pub struct UtmpxIter;

impl UtmpxIter {
    /// Sets the name of the utmpx-format file for the other utmpx functions to access.
    ///
    /// If not set, default record file will be used(file path depends on the target OS)
    pub fn read_from(self, f: &str) -> Self {
        let res = unsafe { utmpxname(CString::new(f).unwrap().as_ptr()) };
        if res != 0 {
            println!("Warning: {}", IOError::last_os_error());
        }
        unsafe {
            setutxent();
        }
        self
    }
}

impl Iterator for UtmpxIter {
    type Item = Utmpx;
    fn next(&mut self) -> Option<Self::Item> {
        unsafe {
            let res = getutxent();
            if !res.is_null() {
                Some(Utmpx {
                    inner: ptr::read(res as *const _),
                })
            } else {
                endutxent();
                None
            }
        }
    }
}
<|endoftext|>


<commit_before>use simd::f32x4;
use simple_parallel::Pool;
use std::slice::ChunksMut;
use num_cpus;

#[derive(PartialEq)]
#[derive(Clone)]
#[derive(Copy)]
pub struct Complex
{
	pub real: f32,
	pub imag: f32
}

impl Complex
{
	pub fn new(real: f32, imag: f32) -> Complex
	{
		return Complex { real: real, imag: imag };
	}
}

pub struct DataBuffer
{
	pool: Pool,
}

impl DataBuffer
{
	#[allow(unused_variables)]
	pub fn new(name: &str) -> DataBuffer
	{
		return DataBuffer { pool: Pool::new(num_cpus::get()) };
	}
}

#[derive(Debug)]
#[derive(PartialEq)]
struct Chunk
{
	pub id: usize,
	pub start: usize,
	pub end: usize,
	pub step_size: usize
}

#[allow(dead_code)]
impl Chunk
{
	fn perform_parallel_execution(array_length: usize) -> bool
	{
		array_length > 1000000
	}

	fn partition_in_number(array: &mut [f32], array_length: usize, step_size: usize, number_of_chunks: usize) -> ChunksMut<f32>
	{
		let mut chunk_size = array_length / number_of_chunks;
		chunk_size -= chunk_size % step_size;
		if chunk_size > 0
		{
			array[0 .. array_length].chunks_mut(chunk_size)
		}
		else
		{
			array[0 .. array_length].chunks_mut(array_length)
		}
	}

	fn partition(array: &mut [f32], array_length: usize, step_size: usize) -> ChunksMut<f32>
	{
		Chunk::partition_in_number(array, array_length, step_size, num_cpus::get())
	}

	fn execute<F>(array: & mut [f32], step_size: usize, buffer:  &mut DataBuffer, function: F)
		where F: Fn(&mut [f32])  + Send + 'static + Sync
	{
		let array_length = array.len();
		Chunk::execute_partial(array, array_length, step_size, buffer, function);
	}

	fn execute_partial<F>(array: & mut [f32], array_length: usize, step_size: usize, buffer:  &mut DataBuffer, function: F)
		where F: Fn(&mut [f32]) + Send + 'static + Sync
	{
		if Chunk::perform_parallel_execution(array_length)
		{
			let chunks = Chunk::partition(array, array_length, step_size);
			let ref mut pool = buffer.pool; // TODO: Check if this saves time or if creating a new pool every time is as fast as reusing the pool
			pool.for_(chunks, |chunk|
				{
					function(chunk);
				});
		}
		else
		{
			function(&mut array[0..array_length]);
		}
	}

	fn execute_partial_with_arguments<T,F>(array: & mut [f32], array_length: usize, step_size: usize, buffer:  &mut DataBuffer, function: F, arguments: T)
		where F: Fn(& mut [f32], T) + Send + 'static + Sync, T : Sync + Copy
	{
		if Chunk::perform_parallel_execution(array_length)
		{
			let chunks = Chunk::partition(array, array_length, step_size);
			let ref mut pool = buffer.pool;
			pool.for_(chunks, |chunk|
				{
					function(chunk, arguments);
				});
		}
		else
		{
			function(&mut array[0..array_length], arguments);
		}
	}
}

pub struct DataVector<'a>
{
	pub data: &'a mut [f32],
	length: usize,
	delta: f64
}

#[allow(unused_variables)]
impl<'a> DataVector<'a>
{
	pub fn new<'b>(data: &'b mut [f32]) -> DataVector<'b>
	{
		let length = data.len();
		DataVector { data: data, length: length, delta: 1.0 }
	}

	pub fn len(&self) -> usize
	{
		self.length
    }

	pub fn delta(&self) -> f64
	{
		self.delta
	}

	pub fn inplace_complex_offset(&mut self, offset: Complex, buffer: &mut DataBuffer)
	{
		self.inplace_offset(&[offset.real, offset.imag, offset.real, offset.imag], buffer);
	}

	pub fn inplace_real_offset(&mut self, offset: f32, buffer: &mut DataBuffer)
	{
		self.inplace_offset(&[offset, offset, offset, offset], buffer);
	}

	fn inplace_offset(&mut self, offset: &[f32; 4], buffer: &mut DataBuffer)
	{
		let increment_vector = f32x4::load(offset, 0);
		let data_length = self.len();
		let scalar_length = data_length % 4;
		let vectorization_length = data_length - scalar_length;
		let mut array = &mut self.data;
		if Chunk::perform_parallel_execution(vectorization_length)
		{
			Chunk::execute_partial_with_arguments(&mut array, vectorization_length, 4, buffer, DataVector::inplace_offset_simd, increment_vector);
		}
		else
		{
			DataVector::inplace_offset_simd(array, increment_vector);
		}

		for i in vectorization_length..data_length
		{
			array[i] = array[i] + offset[i % 2];
		}
	}

	fn inplace_offset_simd(array: &mut [f32], increment_vector: f32x4)
	{
		let mut i = 0;
		while i < array.len()
		{
			let vector = f32x4::load(array, i);
			let incremented = vector + increment_vector;
			incremented.store(array, i);
			i += 4;
		}
	}

	pub fn inplace_real_scale(&mut self, factor: f32, buffer: &mut DataBuffer)
	{
		let scaling_vector = f32x4::splat(factor);
		let data_length = self.len();
		let scalar_length = data_length % 4;
		let vectorization_length = data_length - scalar_length;
		let mut array = &mut self.data;
		Chunk::execute_partial_with_arguments(&mut array, vectorization_length, 4, buffer, DataVector::inplace_real_scale_simd, scaling_vector);

		for i in vectorization_length..data_length
		{
			array[i] = array[i] * factor;
		}
	}

	fn inplace_real_scale_simd(array: &mut [f32], scaling_vector: f32x4)
	{
		let mut i = 0;
		while i < array.len()
		{
			let vector = f32x4::load(array, i);
			let scaled = vector * scaling_vector;
			scaled.store(array, i);
			i += 4;
		}
	}

	pub fn inplace_complex_scale(&mut self, factor: Complex, buffer: &mut DataBuffer)
	{
		let scaling_vector = f32x4::load(&[factor.real, factor.imag, factor.real, factor.imag], 0);
		let data_length = self.len();
		let scalar_length = data_length % 4;
		let vectorization_length = data_length - scalar_length;
		let mut array = &mut self.data;
		Chunk::execute_partial_with_arguments(&mut array, vectorization_length, 4, buffer, DataVector::inplace_complex_scale_simd, scaling_vector);

		for i in vectorization_length..data_length
		{
			array[i] = array[i] * if i % 2 == 0 { factor.real} else {factor.imag };
		}
	}

	fn inplace_complex_scale_simd(array: &mut [f32], scaling_vector: f32x4)
	{
		let mut i = 0;
		let shuffled = f32x4::new(scaling_vector.extract(1), scaling_vector.extract(0), scaling_vector.extract(3), scaling_vector.extract(2));
		let mut temp = [0.0; 4];
		while i < array.len()
		{
			let vector = f32x4::load(array, i);
			let parallel = vector * scaling_vector;
			let cross = vector * shuffled;
			parallel.store(&mut temp, 0);
			array[i] = temp[0] - temp[1];
			array[i + 2] = temp[2] - temp[3];
			cross.store(&mut temp, 0);
			array[i + 1] = temp[0] + temp[1];
			array[i + 3] = temp[2] + temp[3];
			i += 4;
		}

		/*let mut i = 0;
		while i < array.len()
		{
			let real = array[i];
			let imag = array[i + 1];
			array[i] = real * factor.real - imag * factor.imag;
			array[i + 1] = real * factor.imag + imag * factor.real;
			i += 2;
		}*/
	}
}

#[test]
fn partition_array_in_tiny_pieces()
{
	let mut array = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
	let chunks = Chunk::partition_in_number(&mut array, 8, 4, 8);
	assert_eq!(chunks.len(), 1);
	for chunk in chunks
	{
		assert_eq!(chunk.len(), 8);
	}
}

#[test]
fn partition_array()
{
	let mut array = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
	let chunks = Chunk::partition_in_number(&mut array, 8, 4, 2);
	assert_eq!(chunks.len(), 2);
	for chunk in chunks
	{
		assert_eq!(chunk.len(), 4);
	}
}

#[test]
fn partition_array_considering_step_size()
{
	let mut array = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 1.0];
	let chunks = Chunk::partition_in_number(&mut array, 8, 4, 2);
	assert_eq!(chunks.len(), 2);
	for chunk in chunks
	{
		assert_eq!(chunk.len(), 4);
	}
}

#[test]
fn add_real_one_test()
{
	let mut data = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
	let mut result = DataVector::new(&mut data);
	let mut buffer = DataBuffer::new("test");
	result.inplace_real_offset(1.0, &mut buffer);
	let expected = [2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
	assert_eq!(result.data, expected);
	assert_eq!(result.delta, 1.0);
}

#[test]
fn add_real_two_test()
{
	// Test also that vector calls are possible
	let mut data = vec!(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0);
	let mut result = DataVector::new(&mut data);
	let mut buffer = DataBuffer::new("test");
	result.inplace_real_offset(2.0, &mut buffer);
	assert_eq!(result.data, [3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0]);
}

#[test]
fn add_complex_test()
{
	let mut data = vec!(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0);
	let mut result = DataVector::new(&mut data);
	let mut buffer = DataBuffer::new("test");
	result.inplace_complex_offset(Complex::new(1.0, -1.0), &mut buffer);
	assert_eq!(result.data, [2.0, 1.0, 4.0, 3.0, 6.0, 5.0, 8.0, 7.0]);
}

#[test]
fn multiply_real_two_test()
{
	let mut data = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
	let mut result = DataVector::new(&mut data);
	let mut buffer = DataBuffer::new("test");
	result.inplace_real_scale(2.0, &mut buffer);
	let expected = [2.0, 4.0, 6.0, 8.0, 10.0, 12.0, 14.0, 16.0];
	assert_eq!(result.data, expected);
	assert_eq!(result.delta, 1.0);
}

#[test]
fn multiply_complex_test()
{
	let mut data = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
	let mut result = DataVector::new(&mut data);
	let mut buffer = DataBuffer::new("test");
	result.inplace_complex_scale(Complex::new(2.0, -3.0), &mut buffer);
	let expected = [8.0, 1.0, 18.0, -1.0, 28.0, -3.0, 38.0, -5.0];
	assert_eq!(result.data, expected);
	assert_eq!(result.delta, 1.0);
}<commit_msg>Looked into a TODO comment: Yes it seems to make sense to store the thread pool at a central location<commit_after>use simd::f32x4;
use simple_parallel::Pool;
use std::slice::ChunksMut;
use num_cpus;

#[derive(PartialEq)]
#[derive(Clone)]
#[derive(Copy)]
pub struct Complex
{
	pub real: f32,
	pub imag: f32
}

impl Complex
{
	pub fn new(real: f32, imag: f32) -> Complex
	{
		return Complex { real: real, imag: imag };
	}
}

pub struct DataBuffer
{
	// Storing the pool saves a little bit of initialization time
	pool: Pool,
}

impl DataBuffer
{
	#[allow(unused_variables)]
	pub fn new(name: &str) -> DataBuffer
	{
		return DataBuffer { pool: Pool::new(num_cpus::get()) };
	}
}

#[derive(Debug)]
#[derive(PartialEq)]
struct Chunk
{
	pub id: usize,
	pub start: usize,
	pub end: usize,
	pub step_size: usize
}

#[allow(dead_code)]
impl Chunk
{
	fn perform_parallel_execution(array_length: usize) -> bool
	{
		array_length > 1000000
	}

	fn partition_in_number(array: &mut [f32], array_length: usize, step_size: usize, number_of_chunks: usize) -> ChunksMut<f32>
	{
		let mut chunk_size = array_length / number_of_chunks;
		chunk_size -= chunk_size % step_size;
		if chunk_size > 0
		{
			array[0 .. array_length].chunks_mut(chunk_size)
		}
		else
		{
			array[0 .. array_length].chunks_mut(array_length)
		}
	}

	fn partition(array: &mut [f32], array_length: usize, step_size: usize) -> ChunksMut<f32>
	{
		Chunk::partition_in_number(array, array_length, step_size, num_cpus::get())
	}

	fn execute<F>(array: & mut [f32], step_size: usize, buffer:  &mut DataBuffer, function: F)
		where F: Fn(&mut [f32])  + Send + 'static + Sync
	{
		let array_length = array.len();
		Chunk::execute_partial(array, array_length, step_size, buffer, function);
	}

	fn execute_partial<F>(array: & mut [f32], array_length: usize, step_size: usize, buffer:  &mut DataBuffer, function: F)
		where F: Fn(&mut [f32]) + Send + 'static + Sync
	{
		if Chunk::perform_parallel_execution(array_length)
		{
			let chunks = Chunk::partition(array, array_length, step_size);
			let ref mut pool = buffer.pool;
			pool.for_(chunks, |chunk|
				{
					function(chunk);
				});
		}
		else
		{
			function(&mut array[0..array_length]);
		}
	}

	fn execute_partial_with_arguments<T,F>(array: & mut [f32], array_length: usize, step_size: usize, buffer:  &mut DataBuffer, function: F, arguments: T)
		where F: Fn(& mut [f32], T) + Send + 'static + Sync, T : Sync + Copy
	{
		if Chunk::perform_parallel_execution(array_length)
		{
			let chunks = Chunk::partition(array, array_length, step_size);
			let ref mut pool = buffer.pool;
			pool.for_(chunks, |chunk|
				{
					function(chunk, arguments);
				});
		}
		else
		{
			function(&mut array[0..array_length], arguments);
		}
	}
}

pub struct DataVector<'a>
{
	pub data: &'a mut [f32],
	length: usize,
	delta: f64
}

#[allow(unused_variables)]
impl<'a> DataVector<'a>
{
	pub fn new<'b>(data: &'b mut [f32]) -> DataVector<'b>
	{
		let length = data.len();
		DataVector { data: data, length: length, delta: 1.0 }
	}

	pub fn len(&self) -> usize
	{
		self.length
    }

	pub fn delta(&self) -> f64
	{
		self.delta
	}

	pub fn inplace_complex_offset(&mut self, offset: Complex, buffer: &mut DataBuffer)
	{
		self.inplace_offset(&[offset.real, offset.imag, offset.real, offset.imag], buffer);
	}

	pub fn inplace_real_offset(&mut self, offset: f32, buffer: &mut DataBuffer)
	{
		self.inplace_offset(&[offset, offset, offset, offset], buffer);
	}

	fn inplace_offset(&mut self, offset: &[f32; 4], buffer: &mut DataBuffer)
	{
		let increment_vector = f32x4::load(offset, 0);
		let data_length = self.len();
		let scalar_length = data_length % 4;
		let vectorization_length = data_length - scalar_length;
		let mut array = &mut self.data;
		if Chunk::perform_parallel_execution(vectorization_length)
		{
			Chunk::execute_partial_with_arguments(&mut array, vectorization_length, 4, buffer, DataVector::inplace_offset_simd, increment_vector);
		}
		else
		{
			DataVector::inplace_offset_simd(array, increment_vector);
		}

		for i in vectorization_length..data_length
		{
			array[i] = array[i] + offset[i % 2];
		}
	}

	fn inplace_offset_simd(array: &mut [f32], increment_vector: f32x4)
	{
		let mut i = 0;
		while i < array.len()
		{
			let vector = f32x4::load(array, i);
			let incremented = vector + increment_vector;
			incremented.store(array, i);
			i += 4;
		}
	}

	pub fn inplace_real_scale(&mut self, factor: f32, buffer: &mut DataBuffer)
	{
		let scaling_vector = f32x4::splat(factor);
		let data_length = self.len();
		let scalar_length = data_length % 4;
		let vectorization_length = data_length - scalar_length;
		let mut array = &mut self.data;
		Chunk::execute_partial_with_arguments(&mut array, vectorization_length, 4, buffer, DataVector::inplace_real_scale_simd, scaling_vector);

		for i in vectorization_length..data_length
		{
			array[i] = array[i] * factor;
		}
	}

	fn inplace_real_scale_simd(array: &mut [f32], scaling_vector: f32x4)
	{
		let mut i = 0;
		while i < array.len()
		{
			let vector = f32x4::load(array, i);
			let scaled = vector * scaling_vector;
			scaled.store(array, i);
			i += 4;
		}
	}

	pub fn inplace_complex_scale(&mut self, factor: Complex, buffer: &mut DataBuffer)
	{
		let scaling_vector = f32x4::load(&[factor.real, factor.imag, factor.real, factor.imag], 0);
		let data_length = self.len();
		let scalar_length = data_length % 4;
		let vectorization_length = data_length - scalar_length;
		let mut array = &mut self.data;
		Chunk::execute_partial_with_arguments(&mut array, vectorization_length, 4, buffer, DataVector::inplace_complex_scale_simd, scaling_vector);

		for i in vectorization_length..data_length
		{
			array[i] = array[i] * if i % 2 == 0 { factor.real} else {factor.imag };
		}
	}

	fn inplace_complex_scale_simd(array: &mut [f32], scaling_vector: f32x4)
	{
		let mut i = 0;
		let shuffled = f32x4::new(scaling_vector.extract(1), scaling_vector.extract(0), scaling_vector.extract(3), scaling_vector.extract(2));
		let mut temp = [0.0; 4];
		while i < array.len()
		{
			let vector = f32x4::load(array, i);
			let parallel = vector * scaling_vector;
			let cross = vector * shuffled;
			parallel.store(&mut temp, 0);
			array[i] = temp[0] - temp[1];
			array[i + 2] = temp[2] - temp[3];
			cross.store(&mut temp, 0);
			array[i + 1] = temp[0] + temp[1];
			array[i + 3] = temp[2] + temp[3];
			i += 4;
		}

		/*let mut i = 0;
		while i < array.len()
		{
			let real = array[i];
			let imag = array[i + 1];
			array[i] = real * factor.real - imag * factor.imag;
			array[i + 1] = real * factor.imag + imag * factor.real;
			i += 2;
		}*/
	}
}

#[test]
fn partition_array_in_tiny_pieces()
{
	let mut array = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
	let chunks = Chunk::partition_in_number(&mut array, 8, 4, 8);
	assert_eq!(chunks.len(), 1);
	for chunk in chunks
	{
		assert_eq!(chunk.len(), 8);
	}
}

#[test]
fn partition_array()
{
	let mut array = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
	let chunks = Chunk::partition_in_number(&mut array, 8, 4, 2);
	assert_eq!(chunks.len(), 2);
	for chunk in chunks
	{
		assert_eq!(chunk.len(), 4);
	}
}

#[test]
fn partition_array_considering_step_size()
{
	let mut array = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 1.0];
	let chunks = Chunk::partition_in_number(&mut array, 8, 4, 2);
	assert_eq!(chunks.len(), 2);
	for chunk in chunks
	{
		assert_eq!(chunk.len(), 4);
	}
}

#[test]
fn add_real_one_test()
{
	let mut data = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
	let mut result = DataVector::new(&mut data);
	let mut buffer = DataBuffer::new("test");
	result.inplace_real_offset(1.0, &mut buffer);
	let expected = [2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0];
	assert_eq!(result.data, expected);
	assert_eq!(result.delta, 1.0);
}

#[test]
fn add_real_two_test()
{
	// Test also that vector calls are possible
	let mut data = vec!(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0);
	let mut result = DataVector::new(&mut data);
	let mut buffer = DataBuffer::new("test");
	result.inplace_real_offset(2.0, &mut buffer);
	assert_eq!(result.data, [3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0]);
}

#[test]
fn add_complex_test()
{
	let mut data = vec!(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0);
	let mut result = DataVector::new(&mut data);
	let mut buffer = DataBuffer::new("test");
	result.inplace_complex_offset(Complex::new(1.0, -1.0), &mut buffer);
	assert_eq!(result.data, [2.0, 1.0, 4.0, 3.0, 6.0, 5.0, 8.0, 7.0]);
}

#[test]
fn multiply_real_two_test()
{
	let mut data = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
	let mut result = DataVector::new(&mut data);
	let mut buffer = DataBuffer::new("test");
	result.inplace_real_scale(2.0, &mut buffer);
	let expected = [2.0, 4.0, 6.0, 8.0, 10.0, 12.0, 14.0, 16.0];
	assert_eq!(result.data, expected);
	assert_eq!(result.delta, 1.0);
}

#[test]
fn multiply_complex_test()
{
	let mut data = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
	let mut result = DataVector::new(&mut data);
	let mut buffer = DataBuffer::new("test");
	result.inplace_complex_scale(Complex::new(2.0, -3.0), &mut buffer);
	let expected = [8.0, 1.0, 18.0, -1.0, 28.0, -3.0, 38.0, -5.0];
	assert_eq!(result.data, expected);
	assert_eq!(result.delta, 1.0);
}<|endoftext|>


<commit_before><commit_msg>Mark types in ruma_events::room::encrypted as non_exhaustive<commit_after><|endoftext|>


<commit_before>//! Tests for the registry

use std::sync::mpsc;
use std::thread;

use rustc_serialize::Decodable;
use rustc_serialize::json::ToJson;

use registry;
use registry::{LUA_READ, LUA_WRITE, LUA_PRIVATE, get_struct};

json_convertible! {
    #[derive(Debug, Clone, Eq, PartialEq)]
    struct Point {
        x: i32,
        y: i32
    }
}

const ERR: &'static str = "Key which was added no longer exists!";

#[test]
fn add_keys() {
    let num = 1i32;
    let double = -392f64;
    let string = "Hello world".to_string();
    let numbers = vec![1, 2, 3, 4, 5];
    let point = Point { x: -11, y: 12 };

    registry::set_struct("test_num".to_string(), LUA_READ, num.to_json());
    registry::set_struct("test_double".to_string(), LUA_READ, double);
    registry::set_struct("test_string".to_string(), LUA_READ, string.clone());
    registry::set_struct("test_numbers".to_string(), LUA_READ, numbers.clone());
    registry::set_struct("test_point".to_string(), LUA_READ, point.clone());

    assert!(registry::contains_key(&"test_num".to_string()));
    assert!(registry::contains_key(&"test_double".to_string()));
    assert!(registry::contains_key(&"test_string".to_string()));
    assert!(registry::contains_key(&"test_numbers".to_string()));
    assert!(registry::contains_key(&"test_point".to_string()));

    assert_eq!(get_struct::<_, i32>(&"test_num".to_string()).expect(ERR).1, num);
    assert_eq!(get_struct::<_, f64>(&"test_double".to_string()).expect(ERR).1, double);
    assert_eq!(get_struct::<_,String>(&"test_string".to_string()).expect(ERR).1, string);
    assert_eq!(get_struct::<_, Vec<i32>>(&"test_numbers".to_string()).expect(ERR).1,
               numbers);
    assert_eq!(get_struct::<_, Point>(&"test_point".to_string()).expect(ERR).1, point);

}

#[test]
fn lua_perms() {
    registry::set_struct("perm_none".to_string(), LUA_PRIVATE, 0);
    registry::set_struct("perm_read".to_string(), LUA_READ, 1);
    registry::set_struct("perm_write".to_string(), LUA_WRITE, 2);

    assert_eq!(get_struct::<_, i32>(&"perm_none".to_string()).expect(ERR).0, LUA_PRIVATE);
    assert_eq!(get_struct::<_, i32>(&"perm_read".to_string()).expect(ERR).0, LUA_READ);
    assert_eq!(get_struct::<_, i32>(&"perm_write".to_string()).expect(ERR).0, LUA_WRITE);
}

#[test]
fn multithreaded() {
    use std::time::Duration;
    let (tx, rx) = mpsc::channel();
    thread::sleep(Duration::from_millis(240));
    let num = 1i32;
    let double = -392f64;
    let string = "Hello world".to_string();
    let numbers = vec![1, 2, 3, 4, 5];
    let point = Point { x: -11, y: 12 };

    let tx1 = tx.clone();
    thread::spawn(move || {
        read_thread(String::from("test_num"), num, tx1);
    });
    let tx2 = tx.clone();
    thread::spawn(move || {
        read_thread(String::from("test_double"), double, tx2);
    });
    let tx3 = tx.clone();
    thread::spawn(move || {
        read_thread(String::from("test_string"), string, tx3);
    });
    let tx4 = tx.clone();
    thread::spawn(move || {
        read_thread(String::from("test_numbers"), numbers, tx4);
    });
    let tx5 = tx.clone();
    thread::spawn(move || {
        read_thread(String::from("test_point"), point, tx5);
    });

    let mut result = true;

    for _ in 0..5 {
        result = result && rx.recv().expect("Unable to connect to read thread");
    }
    assert!(result);
}

fn read_thread<T>(name: String, in_val: T, sender: mpsc::Sender<bool>)
where T: ::std::fmt::Debug + Decodable + PartialEq {
    for _ in 1 .. 50 {
        if let Ok(acc_val) = get_struct::<_, T>(&name) {
            let (acc, val) = acc_val;
            assert!(acc.contains(LUA_READ));
            assert_eq!(val, in_val);
        }
        else {
            sender.send(false).expect("Unable to reply to test thread");
        }
    }
    sender.send(true).expect("Unable to reply to test thread");
}
<commit_msg>Fix registry tests and split them up<commit_after>//! Tests for the registry

use std::sync::{mpsc, Arc};
use std::time::Duration;
use std::thread;
use std::ops::Deref;

use rustc_serialize::Decodable;
use rustc_serialize::json::{Json, ToJson};

use registry;
use registry::{AccessFlags, LUA_READ, LUA_WRITE, LUA_PRIVATE, get_struct};

json_convertible! {
    #[derive(Debug, Clone, Eq, PartialEq)]
    struct Point {
        x: i32,
        y: i32
    }
}

impl Point {
    fn new(x: i32, y: i32) -> Point {
        Point { x: x, y: y }
    }
}

const ERR: &'static str = "Key which was added no longer exists!";

fn prop_get() -> Json {
    Point::new(0, 0).to_json()
}

#[test]
fn add_keys() {
    let num = 1i32;
    let double = -392f64;
    let string = "Hello world".to_string();
    let numbers = vec![1, 2, 3, 4, 5];
    let point = Point::new(-11, 12);

    registry::set_struct("test_num".to_string(), LUA_READ, num.to_json());
    registry::set_struct("test_double".to_string(), LUA_READ, double);
    registry::set_struct("test_string".to_string(), LUA_READ, string.clone());
    registry::set_struct("test_numbers".to_string(), LUA_READ, numbers.clone());
    registry::set_struct("test_point".to_string(), LUA_READ, point.clone());
    registry::set_property_field("test_func".to_string(), Some(Arc::new(prop_get)), None);
}

#[test]
fn contains_keys() {
    thread::sleep(Duration::from_millis(240));
    assert!(registry::contains_key(&"test_num".to_string()), "num");
    assert!(registry::contains_key(&"test_double".to_string()), "double");
    assert!(registry::contains_key(&"test_string".to_string()), "string");
    assert!(registry::contains_key(&"test_numbers".to_string()), "numbers");
    assert!(registry::contains_key(&"test_point".to_string()), "point");
    assert!(registry::contains_key(&"test_func".to_string()), "func");
}

#[test]
fn keys_equal() {
    let num = 1i32;
    let double = -392f64;
    let string = "Hello world".to_string();
    let numbers = vec![1, 2, 3, 4, 5];
    let point = Point::new(-11, 12);
    thread::sleep(Duration::from_millis(240));
    assert_eq!(get_struct::<_, i32>(&"test_num".to_string()).expect(ERR).1, num);
    assert_eq!(get_struct::<_, f64>(&"test_double".to_string()).expect(ERR).1, double);
    assert_eq!(get_struct::<_,String>(&"test_string".to_string()).expect(ERR).1, string);
    assert_eq!(get_struct::<_, Vec<i32>>(&"test_numbers".to_string()).expect(ERR).1,
               numbers);
    assert_eq!(get_struct::<_, Point>(&"test_point".to_string()).expect(ERR).1, point);
    assert_eq!(get_struct::<_, Point>(&"test_func".to_string()).expect(ERR).1,
               Point::new(0, 0));
}

#[test]
fn key_perms() {
    thread::sleep(Duration::from_millis(240));
    registry::set_struct("perm_none".to_string(), LUA_PRIVATE, 0);
    registry::set_struct("perm_read".to_string(), LUA_READ, 1);
    registry::set_struct("perm_write".to_string(), LUA_WRITE, 2);

    assert_eq!(get_struct::<_, i32>(&"perm_none".to_string()).expect(ERR).0, LUA_PRIVATE);
    assert_eq!(get_struct::<_, i32>(&"perm_read".to_string()).expect(ERR).0, LUA_READ);
    assert_eq!(get_struct::<_, i32>(&"perm_write".to_string()).expect(ERR).0, LUA_WRITE);
    assert_eq!(registry::get_json(&"test_func".to_string()).expect(ERR).0, AccessFlags::all());
}

#[test]
fn multithreaded() {
    let (tx, rx) = mpsc::channel();
    thread::sleep(Duration::from_millis(240));
    let num = 1i32;
    let double = -392f64;
    let string = "Hello world".to_string();
    let numbers = vec![1, 2, 3, 4, 5];
    let point = Point { x: -11, y: 12 };

    let tx1 = tx.clone();
    thread::spawn(move || {
        read_thread(String::from("test_num"), num, tx1);
    });
    let tx2 = tx.clone();
    thread::spawn(move || {
        read_thread(String::from("test_double"), double, tx2);
    });
    let tx3 = tx.clone();
    thread::spawn(move || {
        read_thread(String::from("test_string"), string, tx3);
    });
    let tx4 = tx.clone();
    thread::spawn(move || {
        read_thread(String::from("test_numbers"), numbers, tx4);
    });
    let tx5 = tx.clone();
    thread::spawn(move || {
        read_thread(String::from("test_point"), point, tx5);
    });

    let mut result = true;

    for _ in 0..5 {
        result = result && rx.recv().expect("Unable to connect to read thread");
    }
    assert!(result);
}

fn read_thread<T>(name: String, in_val: T, sender: mpsc::Sender<bool>)
where T: ::std::fmt::Debug + Decodable + PartialEq {
    for _ in 1 .. 50 {
        if let Ok(acc_val) = get_struct::<_, T>(&name) {
            let (acc, val) = acc_val;
            assert!(acc.contains(LUA_READ));
            assert_eq!(val, in_val);
        }
        else {
            sender.send(false).expect("Unable to reply to test thread");
        }
    }
    sender.send(true).expect("Unable to reply to test thread");
}
<|endoftext|>


<commit_before>use std::str;
use std::str::FromStr;
use std::fmt;
use std::iter::FromIterator;

use req_resp::RequestOrResponse;

use result::Result;
use error::Error;

use assert_types::*;

use bytes::Bytes;

#[derive(Debug, PartialEq, Eq)]
pub enum PseudoHeaderName {
    // 8.1.2.3 Request Pseudo-Header Fields
    Method,
    Scheme,
    Authority,
    Path,

    // 8.1.2.4 Response Pseudo-Header Fields
    Status,
}

impl PseudoHeaderName {
    pub fn name(&self) -> &'static str {
        match *self {
            PseudoHeaderName::Method =>    ":method",
            PseudoHeaderName::Scheme =>    ":scheme",
            PseudoHeaderName::Authority => ":authority",
            PseudoHeaderName::Path =>      ":path",
            PseudoHeaderName::Status =>    ":status",
        }
    }

    pub fn parse(value: &[u8]) -> Result<PseudoHeaderName> {
        match value {
            b":method"    => Ok(PseudoHeaderName::Method),
            b":scheme"    => Ok(PseudoHeaderName::Scheme),
            b":authority" => Ok(PseudoHeaderName::Authority),
            b":path"      => Ok(PseudoHeaderName::Path),
            b":status"    => Ok(PseudoHeaderName::Status),
            _             => Err(Error::Other("invalid pseudo header")),
        }
    }

    pub fn req_or_resp(&self) -> RequestOrResponse {
        match *self {
            PseudoHeaderName::Method    => RequestOrResponse::Request,
            PseudoHeaderName::Scheme    => RequestOrResponse::Request,
            PseudoHeaderName::Authority => RequestOrResponse::Request,
            PseudoHeaderName::Path      => RequestOrResponse::Request,
            PseudoHeaderName::Status    => RequestOrResponse::Response,
        }
    }

    pub fn name_bytes(&self) -> Bytes {
        Bytes::from_static(self.name().as_bytes())
    }
}

#[allow(dead_code)]
pub struct HeaderName(Bytes);

impl<'a> From<&'a str> for HeaderName {
    fn from(_name: &'a str) -> HeaderName {
        unimplemented!()
    }
}

/// A convenience struct representing a part of a header (either the name or the value).
pub struct HeaderPart(Bytes);

impl fmt::Debug for HeaderPart {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self.0, fmt)
    }
}

impl From<Vec<u8>> for HeaderPart {
    fn from(vec: Vec<u8>) -> HeaderPart {
        HeaderPart(Bytes::from(vec))
    }
}

impl From<Bytes> for HeaderPart {
    fn from(bytes: Bytes) -> HeaderPart {
        HeaderPart(bytes)
    }
}

impl<'a> From<&'a [u8]> for HeaderPart {
    fn from(buf: &'a [u8]) -> HeaderPart {
        HeaderPart(Bytes::from(buf))
    }
}

macro_rules! from_static_size_array {
    ($N:expr) => (
        impl<'a> From<&'a [u8; $N]> for HeaderPart {
            fn from(buf: &'a [u8; $N]) -> HeaderPart {
                buf[..].into()
            }
        }
    );
}

macro_rules! impl_from_static_size_array {
    ($($N:expr,)+) => {
        $(
            from_static_size_array!($N);
        )+
    }
}

impl_from_static_size_array!(
    0,
    1,
    2,
    3,
    4,
    5,
    6,
    7,
    8,
    9,
    10,
    11,
    12,
    13,
    14,
    15,
    16,
    17,
    18,
    19,
    20,
    21,
    22,
    23,
);

impl From<String> for HeaderPart {
    fn from(s: String) -> HeaderPart {
        From::from(s.into_bytes())
    }
}

impl<'a> From<&'a str> for HeaderPart {
    fn from(s: &'a str) -> HeaderPart {
        From::from(s.as_bytes())
    }
}

#[derive(Debug, PartialEq, Eq, Clone)]
pub struct Header {
    pub name: Bytes,
    pub value: Bytes,
}

fn _assert_header_sync_send() {
    assert_sync::<Header>();
    assert_send::<Header>();
}

impl Header {
    /// Creates a new `Header` with the given name and value.
    ///
    /// The name and value need to be convertible into a `HeaderPart`.
    pub fn new<N: Into<HeaderPart>, V: Into<HeaderPart>>(name: N,
                                                                 value: V)
                                                                 -> Header {
        Header {
            name: name.into().0,
            value: value.into().0,
        }
    }

    /// Return a borrowed representation of the `Header` name.
    pub fn name(&self) -> &[u8] {
        &self.name
    }
    /// Return a borrowed representation of the `Header` value.
    pub fn value(&self) -> &[u8] {
        &self.value
    }

    /// name: value
    pub fn format(&self) -> String {
        format!("{}: {}", String::from_utf8_lossy(&self.name), String::from_utf8_lossy(&self.value))
    }

    pub fn is_preudo_header(&self) -> bool {
        self.name.len() != 0 && self.name[0] == b':'
    }

    pub fn validate(&self, request_or_response: RequestOrResponse) -> bool {
        if self.name.len() == 0 {
            return false;
        }
        if self.is_preudo_header() {
            let valid_named = match request_or_response {
                RequestOrResponse::Request => {
                    static REQ_HEADERS: &[&[u8]] = &[
                        b":method" as &[u8],
                        b":scheme",
                        b":authority",
                        b":path",
                    ];
                    REQ_HEADERS
                }
                RequestOrResponse::Response => {
                    static RESP_HEADERS: &[&[u8]] = &[
                        b":status" as &[u8],
                    ];
                    RESP_HEADERS
                }
            };
            if !valid_named.contains(&self.name.as_ref()) {
                return false;
            }
        }
        return true;
    }
}

impl<N: Into<HeaderPart>, V: Into<HeaderPart>> From<(N, V)> for Header {
    fn from(p: (N, V)) -> Header {
        Header::new(p.0, p.1)
    }
}

#[derive(Default, Debug, PartialEq, Eq, Clone)]
pub struct Headers(pub Vec<Header>);

impl Headers {
    pub fn new() -> Headers {
        Default::default()
    }

    /// Multiline string
    pub fn dump(&self) -> String {
        let mut r = String::new();
        for h in &self.0 {
            r.push_str(&h.format());
            r.push_str("\n");
        }
        r
    }

    pub fn new_get(path: &str) -> Headers {
        Headers(vec![
            Header::new(":method", "GET"),
            Header::new(":path", path),
        ])
    }

    pub fn new_post(path: &str) -> Headers {
        Headers(vec![
            Header::new(":method", "POST"),
            Header::new(":path", path),
        ])
    }

    pub fn from_status(code: u32) -> Headers {
        Headers(vec![
            Header::new(":status", format!("{}", code)),
        ])
    }

    pub fn ok_200() -> Headers {
        Headers::from_status(200)
    }

    pub fn not_found_404() -> Headers {
        Headers::from_status(404)
    }

    pub fn internal_error_500() -> Headers {
        Headers::from_status(500)
    }

    pub fn contains_preudo_headers(&self) -> bool {
        self.0.iter().any(|h| h.is_preudo_header())
    }

    pub fn validate(&self, request_or_response: RequestOrResponse) -> bool {
        let mut saw_regular_header = false;

        for header in &self.0 {
            if !header.validate(request_or_response) {
                return false;
            }

            // 8.1.2.1.  Pseudo-Header Fields
            // All pseudo-header fields MUST appear in the header block before
            // regular header fields.  Any request or response that contains a
            // pseudo-header field that appears in a header block after a regular
            // header field MUST be treated as malformed (Section 8.1.2.6).
            if header.is_preudo_header() {
                if saw_regular_header {
                    return false;
                }
            } else {
                saw_regular_header = true;
            }
        }
        true
    }

    pub fn get_opt<'a>(&'a self, name: &str) -> Option<&'a str> {
        self.0.iter()
            .find(|h| h.name() == name.as_bytes())
            .and_then(|h| str::from_utf8(h.value()).ok())
    }

    pub fn get<'a>(&'a self, name: &str) -> &'a str {
        self.get_opt(name).unwrap()
    }

    pub fn get_opt_parse<I : FromStr>(&self, name: &str) -> Option<I> {
        self.get_opt(name)
            .and_then(|h| h.parse().ok())
    }

    pub fn status(&self) -> u32 {
        self.get_opt_parse(":status").unwrap()
    }

    pub fn path(&self) -> &str {
        self.get(":path")
    }

    pub fn method(&self) -> &str {
        self.get(":method")
    }

    pub fn content_length(&self) -> Option<u64> {
        match self.get_opt("content-length") {
            Some(v) => v.parse().ok(),
            None => None,
        }
    }

    pub fn add(&mut self, name: &str, value: &str) {
        self.0.push(Header::new(name, value));
    }

    pub fn extend(&mut self, headers: Headers) {
        self.0.extend(headers.0);
    }
}

impl FromIterator<Header> for Headers {
    fn from_iter<T : IntoIterator<Item=Header>>(iter: T) -> Headers {
        Headers(iter.into_iter().collect())
    }
}

#[cfg(test)]
mod test {
    use solicit::header::Header;

    #[test]
    fn test_partial_eq_of_headers() {
        let fully_static = Header::new(b":method", b"GET");
        let static_name = Header::new(b":method", b"GET".to_vec());
        let other = Header::new(b":path", b"/");

        assert!(fully_static == static_name);
        assert!(fully_static != other);
        assert!(static_name != other);
    }

    #[test]
    fn test_debug() {
        assert_eq!(
            "Header { name: b\":method\", value: b\"GET\" }",
            format!("{:?}", Header::new(b":method", b"GET")));
        assert_eq!(
            "Header { name: b\":method\", value: b\"\\xcd\" }",
            format!("{:?}", Header::new(b":method", b"\xcd")));
    }
}
<commit_msg>Implement h2spec test http2/8.1.2/1<commit_after>use std::str;
use std::str::FromStr;
use std::fmt;
use std::iter::FromIterator;

use req_resp::RequestOrResponse;

use result::Result;
use error::Error;

use assert_types::*;

use bytes::Bytes;

#[derive(Debug, PartialEq, Eq)]
pub enum PseudoHeaderName {
    // 8.1.2.3 Request Pseudo-Header Fields
    Method,
    Scheme,
    Authority,
    Path,

    // 8.1.2.4 Response Pseudo-Header Fields
    Status,
}

impl PseudoHeaderName {
    pub fn name(&self) -> &'static str {
        match *self {
            PseudoHeaderName::Method =>    ":method",
            PseudoHeaderName::Scheme =>    ":scheme",
            PseudoHeaderName::Authority => ":authority",
            PseudoHeaderName::Path =>      ":path",
            PseudoHeaderName::Status =>    ":status",
        }
    }

    pub fn parse(value: &[u8]) -> Result<PseudoHeaderName> {
        match value {
            b":method"    => Ok(PseudoHeaderName::Method),
            b":scheme"    => Ok(PseudoHeaderName::Scheme),
            b":authority" => Ok(PseudoHeaderName::Authority),
            b":path"      => Ok(PseudoHeaderName::Path),
            b":status"    => Ok(PseudoHeaderName::Status),
            _             => Err(Error::Other("invalid pseudo header")),
        }
    }

    pub fn req_or_resp(&self) -> RequestOrResponse {
        match *self {
            PseudoHeaderName::Method    => RequestOrResponse::Request,
            PseudoHeaderName::Scheme    => RequestOrResponse::Request,
            PseudoHeaderName::Authority => RequestOrResponse::Request,
            PseudoHeaderName::Path      => RequestOrResponse::Request,
            PseudoHeaderName::Status    => RequestOrResponse::Response,
        }
    }

    pub fn name_bytes(&self) -> Bytes {
        Bytes::from_static(self.name().as_bytes())
    }
}

#[allow(dead_code)]
pub struct HeaderName(Bytes);

impl<'a> From<&'a str> for HeaderName {
    fn from(_name: &'a str) -> HeaderName {
        unimplemented!()
    }
}

/// A convenience struct representing a part of a header (either the name or the value).
pub struct HeaderPart(Bytes);

impl fmt::Debug for HeaderPart {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self.0, fmt)
    }
}

impl From<Vec<u8>> for HeaderPart {
    fn from(vec: Vec<u8>) -> HeaderPart {
        HeaderPart(Bytes::from(vec))
    }
}

impl From<Bytes> for HeaderPart {
    fn from(bytes: Bytes) -> HeaderPart {
        HeaderPart(bytes)
    }
}

impl<'a> From<&'a [u8]> for HeaderPart {
    fn from(buf: &'a [u8]) -> HeaderPart {
        HeaderPart(Bytes::from(buf))
    }
}

macro_rules! from_static_size_array {
    ($N:expr) => (
        impl<'a> From<&'a [u8; $N]> for HeaderPart {
            fn from(buf: &'a [u8; $N]) -> HeaderPart {
                buf[..].into()
            }
        }
    );
}

macro_rules! impl_from_static_size_array {
    ($($N:expr,)+) => {
        $(
            from_static_size_array!($N);
        )+
    }
}

impl_from_static_size_array!(
    0,
    1,
    2,
    3,
    4,
    5,
    6,
    7,
    8,
    9,
    10,
    11,
    12,
    13,
    14,
    15,
    16,
    17,
    18,
    19,
    20,
    21,
    22,
    23,
);

impl From<String> for HeaderPart {
    fn from(s: String) -> HeaderPart {
        From::from(s.into_bytes())
    }
}

impl<'a> From<&'a str> for HeaderPart {
    fn from(s: &'a str) -> HeaderPart {
        From::from(s.as_bytes())
    }
}

#[derive(Debug, PartialEq, Eq, Clone)]
pub struct Header {
    pub name: Bytes,
    pub value: Bytes,
}

fn _assert_header_sync_send() {
    assert_sync::<Header>();
    assert_send::<Header>();
}

impl Header {
    /// Creates a new `Header` with the given name and value.
    ///
    /// The name and value need to be convertible into a `HeaderPart`.
    pub fn new<N: Into<HeaderPart>, V: Into<HeaderPart>>(name: N,
                                                                 value: V)
                                                                 -> Header {
        Header {
            name: name.into().0,
            value: value.into().0,
        }
    }

    /// Return a borrowed representation of the `Header` name.
    pub fn name(&self) -> &[u8] {
        &self.name
    }
    /// Return a borrowed representation of the `Header` value.
    pub fn value(&self) -> &[u8] {
        &self.value
    }

    /// name: value
    pub fn format(&self) -> String {
        format!("{}: {}", String::from_utf8_lossy(&self.name), String::from_utf8_lossy(&self.value))
    }

    pub fn is_preudo_header(&self) -> bool {
        self.name.len() != 0 && self.name[0] == b':'
    }

    fn validate_header_name_char(b: u8) -> bool {
        // TODO: restrict more
        if b >= b'A' && b <= b'Z' {
            return false
        }
        true
    }

    pub fn validate(&self, request_or_response: RequestOrResponse) -> bool {
        if self.name.len() == 0 {
            return false;
        }

        if self.is_preudo_header() {
            let valid_named = match request_or_response {
                RequestOrResponse::Request => {
                    static REQ_HEADERS: &[&[u8]] = &[
                        b":method" as &[u8],
                        b":scheme",
                        b":authority",
                        b":path",
                    ];
                    REQ_HEADERS
                }
                RequestOrResponse::Response => {
                    static RESP_HEADERS: &[&[u8]] = &[
                        b":status" as &[u8],
                    ];
                    RESP_HEADERS
                }
            };
            if !valid_named.contains(&self.name.as_ref()) {
                return false;
            }
        }

        for c in &self.name {
            if !Header::validate_header_name_char(c) {
                return false;
            }
        }

        return true;
    }
}

impl<N: Into<HeaderPart>, V: Into<HeaderPart>> From<(N, V)> for Header {
    fn from(p: (N, V)) -> Header {
        Header::new(p.0, p.1)
    }
}

#[derive(Default, Debug, PartialEq, Eq, Clone)]
pub struct Headers(pub Vec<Header>);

impl Headers {
    pub fn new() -> Headers {
        Default::default()
    }

    /// Multiline string
    pub fn dump(&self) -> String {
        let mut r = String::new();
        for h in &self.0 {
            r.push_str(&h.format());
            r.push_str("\n");
        }
        r
    }

    pub fn new_get(path: &str) -> Headers {
        Headers(vec![
            Header::new(":method", "GET"),
            Header::new(":path", path),
        ])
    }

    pub fn new_post(path: &str) -> Headers {
        Headers(vec![
            Header::new(":method", "POST"),
            Header::new(":path", path),
        ])
    }

    pub fn from_status(code: u32) -> Headers {
        Headers(vec![
            Header::new(":status", format!("{}", code)),
        ])
    }

    pub fn ok_200() -> Headers {
        Headers::from_status(200)
    }

    pub fn not_found_404() -> Headers {
        Headers::from_status(404)
    }

    pub fn internal_error_500() -> Headers {
        Headers::from_status(500)
    }

    pub fn contains_preudo_headers(&self) -> bool {
        self.0.iter().any(|h| h.is_preudo_header())
    }

    pub fn validate(&self, request_or_response: RequestOrResponse) -> bool {
        let mut saw_regular_header = false;

        for header in &self.0 {
            if !header.validate(request_or_response) {
                return false;
            }

            // 8.1.2.1.  Pseudo-Header Fields
            // All pseudo-header fields MUST appear in the header block before
            // regular header fields.  Any request or response that contains a
            // pseudo-header field that appears in a header block after a regular
            // header field MUST be treated as malformed (Section 8.1.2.6).
            if header.is_preudo_header() {
                if saw_regular_header {
                    return false;
                }
            } else {
                saw_regular_header = true;
            }
        }
        true
    }

    pub fn get_opt<'a>(&'a self, name: &str) -> Option<&'a str> {
        self.0.iter()
            .find(|h| h.name() == name.as_bytes())
            .and_then(|h| str::from_utf8(h.value()).ok())
    }

    pub fn get<'a>(&'a self, name: &str) -> &'a str {
        self.get_opt(name).unwrap()
    }

    pub fn get_opt_parse<I : FromStr>(&self, name: &str) -> Option<I> {
        self.get_opt(name)
            .and_then(|h| h.parse().ok())
    }

    pub fn status(&self) -> u32 {
        self.get_opt_parse(":status").unwrap()
    }

    pub fn path(&self) -> &str {
        self.get(":path")
    }

    pub fn method(&self) -> &str {
        self.get(":method")
    }

    pub fn content_length(&self) -> Option<u64> {
        match self.get_opt("content-length") {
            Some(v) => v.parse().ok(),
            None => None,
        }
    }

    pub fn add(&mut self, name: &str, value: &str) {
        self.0.push(Header::new(name, value));
    }

    pub fn extend(&mut self, headers: Headers) {
        self.0.extend(headers.0);
    }
}

impl FromIterator<Header> for Headers {
    fn from_iter<T : IntoIterator<Item=Header>>(iter: T) -> Headers {
        Headers(iter.into_iter().collect())
    }
}

#[cfg(test)]
mod test {
    use solicit::header::Header;

    #[test]
    fn test_partial_eq_of_headers() {
        let fully_static = Header::new(b":method", b"GET");
        let static_name = Header::new(b":method", b"GET".to_vec());
        let other = Header::new(b":path", b"/");

        assert!(fully_static == static_name);
        assert!(fully_static != other);
        assert!(static_name != other);
    }

    #[test]
    fn test_debug() {
        assert_eq!(
            "Header { name: b\":method\", value: b\"GET\" }",
            format!("{:?}", Header::new(b":method", b"GET")));
        assert_eq!(
            "Header { name: b\":method\", value: b\"\\xcd\" }",
            format!("{:?}", Header::new(b":method", b"\xcd")));
    }
}
<|endoftext|>


<commit_before>#![forbid(unsafe_code)]

extern crate xml;

use std::io::{BufReader, SeekFrom};
use std::io::prelude::*;
use std::fs::File;
use std::str;

use xml::reader::EventReader;
use xml::writer::EmitterConfig;

macro_rules! unwrap_all {
    ($($e:expr);+) => {{
        $($e.unwrap();)+
    }}
}

#[test]
fn reading_writing_equal_with_namespaces() {
    let mut f = File::open("tests/documents/sample_2.xml").unwrap();
    let mut b = Vec::new();

    {
        let r = EventReader::new(BufReader::new(&mut f));
        let mut w = EmitterConfig::default().perform_indent(true).create_writer(&mut b);

        for e in r {
            match e {
                Ok(e) => if let Some(e) = e.as_writer_event() {
                    match w.write(e) {
                        Ok(_) => {},
                        Err(e) => panic!("Writer error: {:?}", e)
                    }
                },
                Err(e) => panic!("Error: {}", e)
            }
        }
    }

    f.seek(SeekFrom::Start(0)).unwrap();
    let mut fs = String::new();
    f.read_to_string(&mut fs).unwrap();

    let bs = String::from_utf8(b).unwrap();

    assert_eq!(fs.trim(), bs.trim());
}

#[test]
fn writing_simple() {
    use xml::writer::XmlEvent;

    let mut b = Vec::new();

    {
        let mut w = EmitterConfig::new().write_document_declaration(false).create_writer(&mut b);

        w.write(XmlEvent::start_element("h:hello").ns("h", "urn:hello-world")).unwrap();
        w.write("hello world").unwrap();
        w.write(XmlEvent::end_element()).unwrap();
    }

    assert_eq!(
        str::from_utf8(&b).unwrap(),
        r#"<h:hello xmlns:h="urn:hello-world">hello world</h:hello>"#
    );
}

#[test]
fn writing_empty_elements_with_normalizing() {
    use xml::writer::XmlEvent;

    let mut b = Vec::new();

    {
        let mut w = EmitterConfig::new().write_document_declaration(false).create_writer(&mut b);

        unwrap_all! {
            w.write(XmlEvent::start_element("hello"));
            w.write(XmlEvent::start_element("world"));
            w.write(XmlEvent::end_element());
            w.write(XmlEvent::end_element())
        }
    }

    assert_eq!(str::from_utf8(&b).unwrap(), r#"<hello><world /></hello>"#);
}

#[test]
fn writing_empty_elements_without_normalizing() {
    use xml::writer::XmlEvent;

    let mut b = Vec::new();

    {
        let mut w = EmitterConfig::new()
            .write_document_declaration(false)
            .normalize_empty_elements(false)
            .create_writer(&mut b);

        unwrap_all! {
            w.write(XmlEvent::start_element("hello"));
            w.write(XmlEvent::start_element("world"));
            w.write(XmlEvent::end_element());
            w.write(XmlEvent::end_element())
        }
    }

    assert_eq!(str::from_utf8(&b).unwrap(), r#"<hello><world></world></hello>"#);
}

#[test]
fn writing_empty_elements_without_pad_self_closing() {
    use xml::writer::XmlEvent;

    let mut b = Vec::new();

    {
        let mut w = EmitterConfig::new()
            .write_document_declaration(false)
            .pad_self_closing(false)
            .create_writer(&mut b);

        unwrap_all! {
            w.write(XmlEvent::start_element("hello"));
            w.write(XmlEvent::start_element("world"));
            w.write(XmlEvent::end_element());
            w.write(XmlEvent::end_element())
        }
    }

    assert_eq!(str::from_utf8(&b).unwrap(), r#"<hello><world/></hello>"#);
}

#[test]
fn writing_comments_with_indentation() {
    use xml::writer::XmlEvent;

    let mut b = Vec::new();

    {
        let mut w = EmitterConfig::new()
            .write_document_declaration(false)
            .perform_indent(true)
            .create_writer(&mut b);

        unwrap_all! {
            w.write(XmlEvent::start_element("hello"));
            w.write(XmlEvent::start_element("world"));
            w.write(XmlEvent::comment("  this is a manually padded comment\t"));
            w.write(XmlEvent::comment("this is an unpadded comment"));
            w.write(XmlEvent::end_element());
            w.write(XmlEvent::end_element())
        }
    }

    assert_eq!(
        str::from_utf8(&b).unwrap(),
        "<hello>
  <world>
    <!--  this is a manually padded comment\t-->
    <!-- this is an unpadded comment -->
  </world>
</hello>");
}

#[test]
fn issue_112_overriding_namepace_prefix() {
    use xml::writer::XmlEvent;

    let mut b = Vec::new();

    {
        let mut w = EmitterConfig::new()
            .write_document_declaration(false)
            .create_writer(&mut b);

        unwrap_all! {
            w.write(XmlEvent::start_element("iq").ns("", "jabber:client").ns("a", "urn:A"));
            w.write(XmlEvent::start_element("bind").ns("", "urn:ietf:params:xml:ns:xmpp-bind"));
            w.write(XmlEvent::end_element());
            w.write(XmlEvent::start_element("whatever").ns("a", "urn:X"));
            w.write(XmlEvent::end_element());
            w.write(XmlEvent::end_element())
        }
    }

    assert_eq!(
        str::from_utf8(&b).unwrap(),
        r#"<iq xmlns="jabber:client" xmlns:a="urn:A"><bind xmlns="urn:ietf:params:xml:ns:xmpp-bind" /><whatever xmlns:a="urn:X" /></iq>"#
    )
}

#[test]
fn attribute_escaping() {
    use xml::writer::XmlEvent;

    let mut b = Vec::new();

    {
        let mut w = EmitterConfig::new()
            .write_document_declaration(false)
            .perform_indent(true)
            .create_writer(&mut b);

        unwrap_all! {
            w.write(
                XmlEvent::start_element("hello")
                    .attr("testLt", "<")
                    .attr("testGt", ">")
            );
            w.write(XmlEvent::end_element());
            w.write(
                XmlEvent::start_element("hello")
                    .attr("testQuot", "\"")
                    .attr("testApos", "\'")
            );
            w.write(XmlEvent::end_element());
            w.write(
                XmlEvent::start_element("hello")
                    .attr("testAmp", "&")
            );
            w.write(XmlEvent::end_element());
            w.write(
                XmlEvent::start_element("hello")
                    .attr("testNl", "\n")
                    .attr("testCr", "\r")
            );
            w.write(XmlEvent::end_element());
            w.write(
                XmlEvent::start_element("hello")
                    .attr("testNl", "\\n")
                    .attr("testCr", "\\r")
            );
            w.write(XmlEvent::end_element())
        }
    }
    assert_eq!(
        str::from_utf8(&b).unwrap(),
        "<hello testLt=\"<\" testGt=\">\" />
<hello testQuot=\""\" testApos=\"'\" />
<hello testAmp=\"&\" />
<hello testNl=\"
\" testCr=\"
\" />
<hello testNl=\"\\n\" testCr=\"\\r\" />"
    );
}<commit_msg>Add test for explicit true for pad_self_closing<commit_after>#![forbid(unsafe_code)]

extern crate xml;

use std::io::{BufReader, SeekFrom};
use std::io::prelude::*;
use std::fs::File;
use std::str;

use xml::reader::EventReader;
use xml::writer::EmitterConfig;

macro_rules! unwrap_all {
    ($($e:expr);+) => {{
        $($e.unwrap();)+
    }}
}

#[test]
fn reading_writing_equal_with_namespaces() {
    let mut f = File::open("tests/documents/sample_2.xml").unwrap();
    let mut b = Vec::new();

    {
        let r = EventReader::new(BufReader::new(&mut f));
        let mut w = EmitterConfig::default().perform_indent(true).create_writer(&mut b);

        for e in r {
            match e {
                Ok(e) => if let Some(e) = e.as_writer_event() {
                    match w.write(e) {
                        Ok(_) => {},
                        Err(e) => panic!("Writer error: {:?}", e)
                    }
                },
                Err(e) => panic!("Error: {}", e)
            }
        }
    }

    f.seek(SeekFrom::Start(0)).unwrap();
    let mut fs = String::new();
    f.read_to_string(&mut fs).unwrap();

    let bs = String::from_utf8(b).unwrap();

    assert_eq!(fs.trim(), bs.trim());
}

#[test]
fn writing_simple() {
    use xml::writer::XmlEvent;

    let mut b = Vec::new();

    {
        let mut w = EmitterConfig::new().write_document_declaration(false).create_writer(&mut b);

        w.write(XmlEvent::start_element("h:hello").ns("h", "urn:hello-world")).unwrap();
        w.write("hello world").unwrap();
        w.write(XmlEvent::end_element()).unwrap();
    }

    assert_eq!(
        str::from_utf8(&b).unwrap(),
        r#"<h:hello xmlns:h="urn:hello-world">hello world</h:hello>"#
    );
}

#[test]
fn writing_empty_elements_with_normalizing() {
    use xml::writer::XmlEvent;

    let mut b = Vec::new();

    {
        let mut w = EmitterConfig::new().write_document_declaration(false).create_writer(&mut b);

        unwrap_all! {
            w.write(XmlEvent::start_element("hello"));
            w.write(XmlEvent::start_element("world"));
            w.write(XmlEvent::end_element());
            w.write(XmlEvent::end_element())
        }
    }

    assert_eq!(str::from_utf8(&b).unwrap(), r#"<hello><world /></hello>"#);
}

#[test]
fn writing_empty_elements_without_normalizing() {
    use xml::writer::XmlEvent;

    let mut b = Vec::new();

    {
        let mut w = EmitterConfig::new()
            .write_document_declaration(false)
            .normalize_empty_elements(false)
            .create_writer(&mut b);

        unwrap_all! {
            w.write(XmlEvent::start_element("hello"));
            w.write(XmlEvent::start_element("world"));
            w.write(XmlEvent::end_element());
            w.write(XmlEvent::end_element())
        }
    }

    assert_eq!(str::from_utf8(&b).unwrap(), r#"<hello><world></world></hello>"#);
}

#[test]
fn writing_empty_elements_without_pad_self_closing() {
    use xml::writer::XmlEvent;

    let mut b = Vec::new();

    {
        let mut w = EmitterConfig::new()
            .write_document_declaration(false)
            .pad_self_closing(false)
            .create_writer(&mut b);

        unwrap_all! {
            w.write(XmlEvent::start_element("hello"));
            w.write(XmlEvent::start_element("world"));
            w.write(XmlEvent::end_element());
            w.write(XmlEvent::end_element())
        }
    }

    assert_eq!(str::from_utf8(&b).unwrap(), r#"<hello><world/></hello>"#);
}
#[test]
fn writing_empty_elements_pad_self_closing_explicit() {
    use xml::writer::XmlEvent;

    let mut b = Vec::new();

    {
        let mut w = EmitterConfig::new()
            .write_document_declaration(false)
            .pad_self_closing(true)
            .create_writer(&mut b);

        unwrap_all! {
            w.write(XmlEvent::start_element("hello"));
            w.write(XmlEvent::start_element("world"));
            w.write(XmlEvent::end_element());
            w.write(XmlEvent::end_element())
        }
    }

    assert_eq!(str::from_utf8(&b).unwrap(), r#"<hello><world /></hello>"#);
}

#[test]
fn writing_comments_with_indentation() {
    use xml::writer::XmlEvent;

    let mut b = Vec::new();

    {
        let mut w = EmitterConfig::new()
            .write_document_declaration(false)
            .perform_indent(true)
            .create_writer(&mut b);

        unwrap_all! {
            w.write(XmlEvent::start_element("hello"));
            w.write(XmlEvent::start_element("world"));
            w.write(XmlEvent::comment("  this is a manually padded comment\t"));
            w.write(XmlEvent::comment("this is an unpadded comment"));
            w.write(XmlEvent::end_element());
            w.write(XmlEvent::end_element())
        }
    }

    assert_eq!(
        str::from_utf8(&b).unwrap(),
        "<hello>
  <world>
    <!--  this is a manually padded comment\t-->
    <!-- this is an unpadded comment -->
  </world>
</hello>");
}

#[test]
fn issue_112_overriding_namepace_prefix() {
    use xml::writer::XmlEvent;

    let mut b = Vec::new();

    {
        let mut w = EmitterConfig::new()
            .write_document_declaration(false)
            .create_writer(&mut b);

        unwrap_all! {
            w.write(XmlEvent::start_element("iq").ns("", "jabber:client").ns("a", "urn:A"));
            w.write(XmlEvent::start_element("bind").ns("", "urn:ietf:params:xml:ns:xmpp-bind"));
            w.write(XmlEvent::end_element());
            w.write(XmlEvent::start_element("whatever").ns("a", "urn:X"));
            w.write(XmlEvent::end_element());
            w.write(XmlEvent::end_element())
        }
    }

    assert_eq!(
        str::from_utf8(&b).unwrap(),
        r#"<iq xmlns="jabber:client" xmlns:a="urn:A"><bind xmlns="urn:ietf:params:xml:ns:xmpp-bind" /><whatever xmlns:a="urn:X" /></iq>"#
    )
}

#[test]
fn attribute_escaping() {
    use xml::writer::XmlEvent;

    let mut b = Vec::new();

    {
        let mut w = EmitterConfig::new()
            .write_document_declaration(false)
            .perform_indent(true)
            .create_writer(&mut b);

        unwrap_all! {
            w.write(
                XmlEvent::start_element("hello")
                    .attr("testLt", "<")
                    .attr("testGt", ">")
            );
            w.write(XmlEvent::end_element());
            w.write(
                XmlEvent::start_element("hello")
                    .attr("testQuot", "\"")
                    .attr("testApos", "\'")
            );
            w.write(XmlEvent::end_element());
            w.write(
                XmlEvent::start_element("hello")
                    .attr("testAmp", "&")
            );
            w.write(XmlEvent::end_element());
            w.write(
                XmlEvent::start_element("hello")
                    .attr("testNl", "\n")
                    .attr("testCr", "\r")
            );
            w.write(XmlEvent::end_element());
            w.write(
                XmlEvent::start_element("hello")
                    .attr("testNl", "\\n")
                    .attr("testCr", "\\r")
            );
            w.write(XmlEvent::end_element())
        }
    }
    assert_eq!(
        str::from_utf8(&b).unwrap(),
        "<hello testLt=\"<\" testGt=\">\" />
<hello testQuot=\""\" testApos=\"'\" />
<hello testAmp=\"&\" />
<hello testNl=\"
\" testCr=\"
\" />
<hello testNl=\"\\n\" testCr=\"\\r\" />"
    );
}<|endoftext|>


<commit_before>use jsrs_parser::ast::Exp::{self, BinExp, Float, Int, Var };
use nalgebra::ApproxEq;

// Checks for equality between two values where an exact equality cannot be found (i.e. with
// floats)
pub trait MostlyEq {
    fn mostly_eq(&self, other: &Self) -> bool;
}

impl MostlyEq for Exp {
    fn mostly_eq(&self, other: &Self) -> bool {
        match (self, other) {
            (&BinExp(ref a1, ref o1, ref b1), &BinExp(ref a2, ref o2, ref b2)) =>
                (&*a1).mostly_eq(a2) && o1 == o2 && (&*b1).mostly_eq(&*b2),
            (&Float(f1), &Float(f2)) => f1.approx_eq(&f2),
            (&Int(i1), &Int(i2)) => i1 == i2,
            (&Var(ref v1), &Var(ref v2)) => v1 == v2,
            _ => false
        }
    }
}
<commit_msg>moved mostlyeq out to separate test module<commit_after>use jsrs_parser::ast::Exp::{self, BinExp, Float, Int, Var};
use jsrs_parser::ast::Stmt::{self, Assign, Decl};
use nalgebra::ApproxEq;

// Checks for equality between two values where an exact equality cannot be found (i.e. with
// floats)
pub trait MostlyEq {
    fn mostly_eq(&self, other: &Self) -> bool;
}

impl MostlyEq for Exp {
    fn mostly_eq(&self, other: &Self) -> bool {
        match (self, other) {
            (&BinExp(ref a1, ref o1, ref b1), &BinExp(ref a2, ref o2, ref b2)) =>
                (&*a1).mostly_eq(a2) && o1 == o2 && (&*b1).mostly_eq(&*b2),
            (&Float(f1), &Float(f2)) => f1.approx_eq(&f2),
            (&Int(i1), &Int(i2)) => i1 == i2,
            (&Var(ref v1), &Var(ref v2)) => v1 == v2,
            _ => false
        }
    }
}

impl MostlyEq for Stmt {
    fn mostly_eq(&self, other: &Self) -> bool {
        match (self, other) {
            (&Assign(ref v1, ref e1), &Assign(ref v2, ref e2)) |
            (&Decl(ref v1, ref e1), &Decl(ref v2, ref e2)) => v1 == v2 && e1.mostly_eq(e2),
            _ => false
        }
    }
}
<|endoftext|>