<commit_before>//! Use this library to open a path or URL using the program configured on the system.
//!
//! # Usage
//!
//! Open the given URL in the default web browser.
//!
//! ```no_run
//! open::that("http://rust-lang.org").unwrap();
//! ```
//!
//! Alternatively, specify the program to be used to open the path or URL.
//!
//! ```no_run
//! open::with("http://rust-lang.org", "firefox").unwrap();
//! ```
//!
//! # Notes
//!
//! As an operating system program is used, the open operation can fail.
//! Therefore, you are advised to at least check the result and behave
//! accordingly, e.g. by letting the user know that the open operation failed.
//!
//! ```no_run
//! let path = "http://rust-lang.org";
//!
//! match open::that(path) {
//!     Ok(()) => println!("Opened '{}' successfully.", path),
//!     Err(err) => eprintln!("An error occurred when opening '{}': {}", path, err),
//! }
//! ```

#[cfg(target_os = "windows")]
use windows as os;

#[cfg(target_os = "macos")]
use macos as os;

#[cfg(target_os = "ios")]
use ios as os;

#[cfg(target_os = "haiku")]
use haiku as os;

#[cfg(any(
    target_os = "linux",
    target_os = "android",
    target_os = "freebsd",
    target_os = "dragonfly",
    target_os = "netbsd",
    target_os = "openbsd",
    target_os = "illumos",
    target_os = "solaris"
))]
use unix as os;

#[cfg(not(any(
    target_os = "linux",
    target_os = "android",
    target_os = "freebsd",
    target_os = "dragonfly",
    target_os = "netbsd",
    target_os = "openbsd",
    target_os = "illumos",
    target_os = "solaris",
    target_os = "ios",
    target_os = "macos",
    target_os = "windows",
    target_os = "haiku"
)))]
compile_error!("open is not supported on this platform");

use std::{
    ffi::OsStr,
    io,
    process::{Command, Output, Stdio},
    thread,
};

type Result = io::Result<()>;

/// Open path with the default application.
///
/// # Examples
///
/// ```no_run
/// let path = "http://rust-lang.org";
///
/// match open::that(path) {
///     Ok(()) => println!("Opened '{}' successfully.", path),
///     Err(err) => panic!("An error occurred when opening '{}': {}", path, err),
/// }
/// ```
///
/// # Errors
///
/// A [`std::io::Error`] is returned on failure. Because different operating systems
/// handle errors differently it is recommend to not match on a certain error.
pub fn that<T: AsRef<OsStr>>(path: T) -> Result {
    os::that(path)
}

/// Open path with the given application.
///
/// # Examples
///
/// ```no_run
/// let path = "http://rust-lang.org";
/// let app = "firefox";
///
/// match open::with(path, app) {
///     Ok(()) => println!("Opened '{}' successfully.", path),
///     Err(err) => panic!("An error occurred when opening '{}': {}", path, err),
/// }
/// ```
///
/// # Errors
///
/// A [`std::io::Error`] is returned on failure. Because different operating systems
/// handle errors differently it is recommend to not match on a certain error.
pub fn with<T: AsRef<OsStr>>(path: T, app: impl Into<String>) -> Result {
    os::with(path, app)
}

/// Open path with the default application in a new thread.
///
/// See documentation of [`that`] for more details.
pub fn that_in_background<T: AsRef<OsStr>>(path: T) -> thread::JoinHandle<Result> {
    let path = path.as_ref().to_os_string();
    thread::spawn(|| that(path))
}

/// Open path with the given application in a new thread.
///
/// See documentation of [`with`] for more details.
pub fn with_in_background<T: AsRef<OsStr>>(
    path: T,
    app: impl Into<String>,
) -> thread::JoinHandle<Result> {
    let path = path.as_ref().to_os_string();
    let app = app.into();
    thread::spawn(|| with(path, app))
}

trait IntoResult<T> {
    fn into_result(self) -> T;
}

impl IntoResult<Result> for io::Result<Output> {
    fn into_result(self) -> Result {
        match self {
            Ok(o) if o.status.success() => Ok(()),
            Ok(o) => Err(from_output(o)),
            Err(err) => Err(err),
        }
    }
}

#[cfg(windows)]
impl IntoResult<Result> for std::os::raw::c_int {
    fn into_result(self) -> Result {
        match self {
            i if i > 32 => Ok(()),
            _ => Err(io::Error::last_os_error()),
        }
    }
}

fn from_output(output: Output) -> io::Error {
    let error_msg = match output.stderr.is_empty() {
        true => output.status.to_string(),
        false => format!(
            "{} ({})",
            String::from_utf8_lossy(&output.stderr).trim(),
            output.status
        ),
    };

    io::Error::new(io::ErrorKind::Other, error_msg)
}

trait CommandExt {
    fn output_stderr(&mut self) -> io::Result<Output>;
}

impl CommandExt for Command {
    fn output_stderr(&mut self) -> io::Result<Output> {
        let mut process = self
            .stdin(Stdio::null())
            .stdout(Stdio::null())
            .stderr(Stdio::piped())
            .spawn()?;

        // Consume all stderr - it's open just for a few programs which can't handle it being closed.
        use std::io::Read;
        let mut stderr = vec![0; 256];
        let mut stderr_src = process.stderr.take().expect("piped stderr");

        let len = stderr_src.read(&mut stderr).unwrap_or(0);
        stderr.truncate(len);

        // consume the rest to avoid blocking
        std::io::copy(&mut stderr_src, &mut std::io::sink()).ok();

        let status = process.wait()?;
        Ok(Output {
            status,
            stderr,
            stdout: vec![],
        })
    }
}

#[cfg(windows)]
mod windows {
    use std::{ffi::OsStr, io, os::windows::ffi::OsStrExt, ptr};

    use std::os::raw::c_int;
    use windows_sys::Win32::UI::Shell::ShellExecuteW;

    use crate::{IntoResult, Result};

    fn convert_path(path: &OsStr) -> io::Result<Vec<u16>> {
        let mut maybe_result: Vec<_> = path.encode_wide().collect();
        if maybe_result.iter().any(|&u| u == 0) {
            return Err(io::Error::new(
                io::ErrorKind::InvalidInput,
                "path contains NUL byte(s)",
            ));
        }
        maybe_result.push(0);
        Ok(maybe_result)
    }

    pub fn that<T: AsRef<OsStr>>(path: T) -> Result {
        const SW_SHOW: c_int = 5;

        let path = convert_path(path.as_ref())?;
        let operation: Vec<u16> = OsStr::new("open\0").encode_wide().collect();
        let result = unsafe {
            ShellExecuteW(
                0,
                operation.as_ptr(),
                path.as_ptr(),
                ptr::null(),
                ptr::null(),
                SW_SHOW,
            )
        };
        (result as c_int).into_result()
    }

    pub fn with<T: AsRef<OsStr>>(path: T, app: impl Into<String>) -> Result {
        const SW_SHOW: c_int = 5;

        let path = convert_path(path.as_ref())?;
        let operation: Vec<u16> = OsStr::new("open\0").encode_wide().collect();
        let app_name: Vec<u16> = OsStr::new(&format!("{}\0", app.into()))
            .encode_wide()
            .collect();
        let result = unsafe {
            ShellExecuteW(
                0,
                operation.as_ptr(),
                app_name.as_ptr(),
                path.as_ptr(),
                ptr::null(),
                SW_SHOW,
            )
        };
        (result as c_int).into_result()
    }
}

#[cfg(target_os = "macos")]
mod macos {
    use std::{ffi::OsStr, process::Command};

    use crate::{CommandExt, IntoResult, Result};

    pub fn that<T: AsRef<OsStr>>(path: T) -> Result {
        Command::new("/usr/bin/open")
            .arg(path.as_ref())
            .output_stderr()
            .into_result()
    }

    pub fn with<T: AsRef<OsStr>>(path: T, app: impl Into<String>) -> Result {
        Command::new("/usr/bin/open")
            .arg(path.as_ref())
            .arg("-a")
            .arg(app.into())
            .output_stderr()
            .into_result()
    }
}

#[cfg(target_os = "ios")]
mod ios {
    use std::{ffi::OsStr, process::Command};

    use crate::{CommandExt, IntoResult, Result};

    pub fn that<T: AsRef<OsStr>>(path: T) -> Result {
        Command::new("uiopen")
            .arg("--url")
            .arg(path.as_ref())
            .output_stderr()
            .into_result()
    }

    pub fn with<T: AsRef<OsStr>>(path: T, app: impl Into<String>) -> Result {
        Command::new("uiopen")
            .arg("--url")
            .arg(path.as_ref())
            .arg("--bundleid")
            .arg(app.into())
            .output_stderr()
            .into_result()
    }
}

#[cfg(target_os = "haiku")]
mod haiku {
    use std::{ffi::OsStr, process::Command};

    use crate::{CommandExt, IntoResult, Result};

    pub fn that<T: AsRef<OsStr>>(path: T) -> Result {
        Command::new("/bin/open")
            .arg(path.as_ref())
            .output_stderr()
            .into_result()
    }

    pub fn with<T: AsRef<OsStr>>(path: T, app: impl Into<String>) -> Result {
        Command::new(app.into())
            .arg(path.as_ref())
            .output_stderr()
            .into_result()
    }
}

#[cfg(any(
    target_os = "linux",
    target_os = "android",
    target_os = "freebsd",
    target_os = "dragonfly",
    target_os = "netbsd",
    target_os = "openbsd",
    target_os = "illumos",
    target_os = "solaris"
))]
mod unix {
    use std::{
        env,
        ffi::{OsStr, OsString},
        path::{Path, PathBuf},
        process::Command,
    };

    use crate::{CommandExt, IntoResult, Result};

    pub fn that<T: AsRef<OsStr>>(path: T) -> Result {
        let path = path.as_ref();
        let open_handlers = [
            ("xdg-open", &[path] as &[_]),
            ("gio", &[OsStr::new("open"), path]),
            ("gnome-open", &[path]),
            ("kde-open", &[path]),
            ("wslview", &[&wsl_path(path)]),
        ];

        let mut unsuccessful = None;
        let mut io_error = None;

        for (command, args) in &open_handlers {
            let result = Command::new(command).args(*args).output_stderr();

            match result {
                Ok(o) if o.status.success() => return Ok(()),
                Ok(o) => unsuccessful = unsuccessful.or_else(|| Some(crate::from_output(o))),
                Err(err) => io_error = io_error.or(Some(err)),
            }
        }

        Err(unsuccessful
            .or(io_error)
            .expect("successful cases don't get here"))
    }

    pub fn with<T: AsRef<OsStr>>(path: T, app: impl Into<String>) -> Result {
        Command::new(app.into())
            .arg(path.as_ref())
            .output_stderr()
            .into_result()
    }

    // Polyfill to workaround absolute path bug in wslu(wslview). In versions before
    // v3.1.1, wslview is unable to find absolute paths. `wsl_path` converts an
    // absolute path into a relative path starting from the current directory. If
    // the path is already a relative path or the conversion fails the original path
    // is returned.
    fn wsl_path<T: AsRef<OsStr>>(path: T) -> OsString {
        fn path_relative_to_current_dir<T: AsRef<OsStr>>(path: T) -> Option<PathBuf> {
            let path = Path::new(&path);

            if path.is_relative() {
                return None;
            }

            let base = env::current_dir().ok()?;
            pathdiff::diff_paths(path, base)
        }

        match path_relative_to_current_dir(&path) {
            None => OsString::from(&path),
            Some(relative) => OsString::from(relative),
        }
    }
}
<commit_msg>fix!: Assure `that(…)` is non-blocking on linux<commit_after>//! Use this library to open a path or URL using the program configured on the system.
//!
//! # Usage
//!
//! Open the given URL in the default web browser.
//!
//! ```no_run
//! open::that("http://rust-lang.org").unwrap();
//! ```
//!
//! Alternatively, specify the program to be used to open the path or URL.
//!
//! ```no_run
//! open::with("http://rust-lang.org", "firefox").unwrap();
//! ```
//!
//! # Notes
//!
//! As an operating system program is used, the open operation can fail.
//! Therefore, you are advised to at least check the result and behave
//! accordingly, e.g. by letting the user know that the open operation failed.
//!
//! ```no_run
//! let path = "http://rust-lang.org";
//!
//! match open::that(path) {
//!     Ok(()) => println!("Opened '{}' successfully.", path),
//!     Err(err) => eprintln!("An error occurred when opening '{}': {}", path, err),
//! }
//! ```

#[cfg(target_os = "windows")]
use windows as os;

#[cfg(target_os = "macos")]
use macos as os;

#[cfg(target_os = "ios")]
use ios as os;

#[cfg(target_os = "haiku")]
use haiku as os;

#[cfg(any(
    target_os = "linux",
    target_os = "android",
    target_os = "freebsd",
    target_os = "dragonfly",
    target_os = "netbsd",
    target_os = "openbsd",
    target_os = "illumos",
    target_os = "solaris"
))]
use unix as os;

#[cfg(not(any(
    target_os = "linux",
    target_os = "android",
    target_os = "freebsd",
    target_os = "dragonfly",
    target_os = "netbsd",
    target_os = "openbsd",
    target_os = "illumos",
    target_os = "solaris",
    target_os = "ios",
    target_os = "macos",
    target_os = "windows",
    target_os = "haiku"
)))]
compile_error!("open is not supported on this platform");

use std::{
    ffi::OsStr,
    io,
    process::{Command, Stdio},
    thread,
};

/// Open path with the default application.
///
/// # Examples
///
/// ```no_run
/// let path = "http://rust-lang.org";
///
/// match open::that(path) {
///     Ok(()) => println!("Opened '{}' successfully.", path),
///     Err(err) => panic!("An error occurred when opening '{}': {}", path, err),
/// }
/// ```
///
/// # Errors
///
/// A [`std::io::Error`] is returned on failure. Because different operating systems
/// handle errors differently it is recommend to not match on a certain error.
pub fn that<T: AsRef<OsStr>>(path: T) -> io::Result<()> {
    os::that(path)
}

/// Open path with the given application.
///
/// # Examples
///
/// ```no_run
/// let path = "http://rust-lang.org";
/// let app = "firefox";
///
/// match open::with(path, app) {
///     Ok(()) => println!("Opened '{}' successfully.", path),
///     Err(err) => panic!("An error occurred when opening '{}': {}", path, err),
/// }
/// ```
///
/// # Errors
///
/// A [`std::io::Error`] is returned on failure. Because different operating systems
/// handle errors differently it is recommend to not match on a certain error.
pub fn with<T: AsRef<OsStr>>(path: T, app: impl Into<String>) -> io::Result<()> {
    os::with(path, app)
}

/// Open path with the default application in a new thread.
///
/// See documentation of [`that`] for more details.
pub fn that_in_background<T: AsRef<OsStr>>(path: T) -> thread::JoinHandle<io::Result<()>> {
    let path = path.as_ref().to_os_string();
    thread::spawn(|| that(path))
}

/// Open path with the given application in a new thread.
///
/// See documentation of [`with`] for more details.
pub fn with_in_background<T: AsRef<OsStr>>(
    path: T,
    app: impl Into<String>,
) -> thread::JoinHandle<io::Result<()>> {
    let path = path.as_ref().to_os_string();
    let app = app.into();
    thread::spawn(|| with(path, app))
}

trait IntoResult<T> {
    fn into_result(self) -> T;
}

impl IntoResult<io::Result<()>> for io::Result<std::process::ExitStatus> {
    fn into_result(self) -> io::Result<()> {
        match self {
            Ok(status) if status.success() => Ok(()),
            Ok(status) => Err(io::Error::new(
                io::ErrorKind::Other,
                format!("Launcher failed with {:?}", status),
            )),
            Err(err) => Err(err),
        }
    }
}

#[cfg(windows)]
impl IntoResult<io::Result<()>> for std::os::raw::c_int {
    fn into_result(self) -> io::Result<()> {
        match self {
            i if i > 32 => Ok(()),
            _ => Err(io::Error::last_os_error()),
        }
    }
}

trait CommandExt {
    fn status_without_output(&mut self) -> io::Result<std::process::ExitStatus>;
}

impl CommandExt for Command {
    fn status_without_output(&mut self) -> io::Result<std::process::ExitStatus> {
        let mut process = self
            .stdin(Stdio::null())
            .stdout(Stdio::null())
            .stderr(Stdio::null())
            .spawn()?;

        process.wait()
    }
}

#[cfg(windows)]
mod windows {
    use std::{ffi::OsStr, io, os::windows::ffi::OsStrExt, ptr};

    use std::os::raw::c_int;
    use windows_sys::Win32::UI::Shell::ShellExecuteW;

    use crate::IntoResult;

    fn convert_path(path: &OsStr) -> io::Result<Vec<u16>> {
        let mut maybe_result: Vec<_> = path.encode_wide().collect();
        if maybe_result.iter().any(|&u| u == 0) {
            return Err(io::Error::new(
                io::ErrorKind::InvalidInput,
                "path contains NUL byte(s)",
            ));
        }
        maybe_result.push(0);
        Ok(maybe_result)
    }

    pub fn that<T: AsRef<OsStr>>(path: T) -> io::Result<()> {
        const SW_SHOW: c_int = 5;

        let path = convert_path(path.as_ref())?;
        let operation: Vec<u16> = OsStr::new("open\0").encode_wide().collect();
        let result = unsafe {
            ShellExecuteW(
                0,
                operation.as_ptr(),
                path.as_ptr(),
                ptr::null(),
                ptr::null(),
                SW_SHOW,
            )
        };
        (result as c_int).into_result()
    }

    pub fn with<T: AsRef<OsStr>>(path: T, app: impl Into<String>) -> io::Result<()> {
        const SW_SHOW: c_int = 5;

        let path = convert_path(path.as_ref())?;
        let operation: Vec<u16> = OsStr::new("open\0").encode_wide().collect();
        let app_name: Vec<u16> = OsStr::new(&format!("{}\0", app.into()))
            .encode_wide()
            .collect();
        let result = unsafe {
            ShellExecuteW(
                0,
                operation.as_ptr(),
                app_name.as_ptr(),
                path.as_ptr(),
                ptr::null(),
                SW_SHOW,
            )
        };
        (result as c_int).into_result()
    }
}

#[cfg(target_os = "macos")]
mod macos {
    use std::{ffi::OsStr, io, process::Command};

    use crate::{CommandExt, IntoResult};

    pub fn that<T: AsRef<OsStr>>(path: T) -> io::Result<()> {
        Command::new("/usr/bin/open")
            .arg(path.as_ref())
            .status_without_output()
            .into_result()
    }

    pub fn with<T: AsRef<OsStr>>(path: T, app: impl Into<String>) -> io::Result<()> {
        Command::new("/usr/bin/open")
            .arg(path.as_ref())
            .arg("-a")
            .arg(app.into())
            .status_without_output()
            .into_result()
    }
}

#[cfg(target_os = "ios")]
mod ios {
    use std::{ffi::OsStr, io, process::Command};

    use crate::{CommandExt, IntoResult};

    pub fn that<T: AsRef<OsStr>>(path: T) -> io::Result<()> {
        Command::new("uiopen")
            .arg("--url")
            .arg(path.as_ref())
            .status_without_output()
            .into_result()
    }

    pub fn with<T: AsRef<OsStr>>(path: T, app: impl Into<String>) -> io::Result<()> {
        Command::new("uiopen")
            .arg("--url")
            .arg(path.as_ref())
            .arg("--bundleid")
            .arg(app.into())
            .status_without_output()
            .into_result()
    }
}

#[cfg(target_os = "haiku")]
mod haiku {
    use std::{ffi::OsStr, io, process::Command};

    use crate::{CommandExt, IntoResult};

    pub fn that<T: AsRef<OsStr>>(path: T) -> io::Result<()> {
        Command::new("/bin/open")
            .arg(path.as_ref())
            .status_without_output()
            .into_result()
    }

    pub fn with<T: AsRef<OsStr>>(path: T, app: impl Into<String>) -> io::Result<()> {
        Command::new(app.into())
            .arg(path.as_ref())
            .status_without_output()
            .into_result()
    }
}

#[cfg(any(
    target_os = "linux",
    target_os = "android",
    target_os = "freebsd",
    target_os = "dragonfly",
    target_os = "netbsd",
    target_os = "openbsd",
    target_os = "illumos",
    target_os = "solaris"
))]
mod unix {
    use std::{
        env,
        ffi::{OsStr, OsString},
        io,
        path::{Path, PathBuf},
        process::Command,
    };

    use crate::{CommandExt, IntoResult};

    pub fn that<T: AsRef<OsStr>>(path: T) -> io::Result<()> {
        let path = path.as_ref();
        let open_handlers = [
            ("xdg-open", &[path] as &[_]),
            ("gio", &[OsStr::new("open"), path]),
            ("gnome-open", &[path]),
            ("kde-open", &[path]),
            ("wslview", &[&wsl_path(path)]),
        ];

        let mut unsuccessful = None;
        let mut io_error = None;

        for (command, args) in &open_handlers {
            let result = Command::new(command).args(*args).status_without_output();

            match result {
                Ok(status) if status.success() => return Ok(()),
                Ok(status) => {
                    unsuccessful = unsuccessful.or_else(|| {
                        Some(std::io::Error::new(
                            std::io::ErrorKind::Other,
                            status.to_string(),
                        ))
                    })
                }
                Err(err) => io_error = io_error.or(Some(err)),
            }
        }

        Err(unsuccessful
            .or(io_error)
            .expect("successful cases don't get here"))
    }

    pub fn with<T: AsRef<OsStr>>(path: T, app: impl Into<String>) -> io::Result<()> {
        Command::new(app.into())
            .arg(path.as_ref())
            .status_without_output()
            .into_result()
    }

    // Polyfill to workaround absolute path bug in wslu(wslview). In versions before
    // v3.1.1, wslview is unable to find absolute paths. `wsl_path` converts an
    // absolute path into a relative path starting from the current directory. If
    // the path is already a relative path or the conversion fails the original path
    // is returned.
    fn wsl_path<T: AsRef<OsStr>>(path: T) -> OsString {
        fn path_relative_to_current_dir<T: AsRef<OsStr>>(path: T) -> Option<PathBuf> {
            let path = Path::new(&path);

            if path.is_relative() {
                return None;
            }

            let base = env::current_dir().ok()?;
            pathdiff::diff_paths(path, base)
        }

        match path_relative_to_current_dir(&path) {
            None => OsString::from(&path),
            Some(relative) => OsString::from(relative),
        }
    }
}
<|endoftext|>


<commit_before>extern crate glium;
extern crate cgmath;

mod types;
pub mod camera;
pub mod transform;
pub mod graphics;
pub mod mesh;
pub use graphics::{Graphics, Gfx, Shader, Renderer};
pub use mesh::Mesh;
pub use types::{Mat4, Scalar};
<commit_msg>Uniforms implement macro.<commit_after>pub extern crate glium;
extern crate cgmath;

mod types;
pub mod camera;
pub mod transform;
pub mod graphics;
pub mod mesh;
pub use graphics::{Graphics, Gfx, Shader, Renderer};
pub use mesh::Mesh;
pub use types::{Mat4, Scalar};

#[macro_export]
macro_rules! implement_uniforms {
    ($tyname: ty, $( $field: ident ),*) => {
        impl $crate::glium::uniforms::Uniforms for $tyname {
            fn visit_values<'a, F>(&'a self, mut f: F)
                where F: FnMut(&str, $crate::glium::uniforms::UniformValue<'a>)
            {
                use $crate::glium::uniforms::AsUniformValue;
                $(f(stringify!($field), (&self.$field).as_uniform_value());)*
            }
        }
    };
}<|endoftext|>


<commit_before>extern crate websocket;
extern crate rustc_serialize;

use std::thread;

use rustc_serialize::json;

use websocket::{Client, Message, WebSocketStream};
use websocket::client::request::Url;
use websocket::client::sender::Sender;
use websocket::client::receiver::Receiver;

use websocket::ws::sender::Sender as SenderTrait;
use websocket::ws::receiver::Receiver as ReceiverTrait;

use std::sync::Arc;
use std::sync::Mutex;

#[derive(Clone)]
pub struct ChatClient {
    nick: String,
    channel: String,
    sender: Arc<Mutex<Sender<WebSocketStream>>>,
    receiver: Arc<Mutex<Receiver<WebSocketStream>>>,
}

impl ChatClient {
    /// Creates a new connection to hack.chat.
    ///
    /// #Example
    ///
    /// ```
    /// extern crate hackchat;
    /// use hackchat::{ChatClient, ChatEvent};
    ///
    /// fn main() {
    ///     let mut conn = ChatClient::new("TestBot", "botDev"); //Connects to the ?botDev channel
    ///     conn.start_ping_thread(); //Sends ping packets regularly
    ///
    ///     for event in conn.iter() {
    ///         match event {
    ///             ChatEvent::Message(nick, message, trip_code) => {
    ///                 println!("<{}> {}", nick, message);
    ///             }
    ///         }
    ///     }
    /// }
    /// ```
    pub fn new(nick: &str, channel: &str) -> ChatClient {
        let url = Url::parse("wss://hack.chat/chat-ws").unwrap();
        let request = Client::connect(url).unwrap();
        let response = request.send().unwrap();

        let client = response.begin();
        let (mut sender, receiver) = client.split();

        let join_packet = json::encode(&JoinPacket {
            cmd: "join".to_string(),
            nick: nick.to_string(),
            channel: channel.to_string()
        }).unwrap();
        let message = Message::Text(join_packet);
        sender.send_message(message).unwrap();

        return ChatClient {
            nick: nick.to_string(),
            channel: channel.to_string(),
            sender: Arc::new(Mutex::new(sender)),
            receiver: Arc::new(Mutex::new(receiver))
        };
    }

    /// Sends a message to the current channel.
    ///
    /// ```
    /// let mut chat = ChatClient::new("TestBot", "botDev");
    /// chat.send_message("Hello there people".to_string());
    /// ```
    pub fn send_message(&mut self, message: String) {
        let chat_packet = json::encode(&ChatPacketSend {
            cmd: "chat".to_string(),
            text: message
        }).unwrap();
        let message = Message::Text(chat_packet);
        self.sender.lock().unwrap().send_message(message).unwrap();
    }

    fn send_ping(&mut self) {
        let ping_packet = json::encode(&GenericPacket {
            cmd: "ping".to_string()
        }).unwrap();
        let message = Message::Text(ping_packet);
        self.sender.lock().unwrap().send_message(message).unwrap();
    }

    pub fn send_stats_request(&mut self) {
        let stats_packet = json::encode(&GenericPacket {
            cmd: "stats".to_string()
        }).unwrap();
        let message = Message::Text(stats_packet);
        self.sender.lock().unwrap().send_message(message).unwrap();
    }

    /// Starts the ping thread, whichs sends regular pings to keep the connection open.
    pub fn start_ping_thread(&mut self) {
        let mut chat_clone = self.clone();
        thread::spawn(move|| {
            loop {
                thread::sleep_ms(60 * 1000);
                chat_clone.send_ping();
            }
        });
    }

    /// Returns an iterator of hack.chat events such as messages.
    pub fn iter(&mut self) -> ChatClient {
        return self.clone();
    }
}

impl Iterator for ChatClient {
    type Item = ChatEvent;
    fn next(&mut self) -> Option<ChatEvent> {
        loop {
            let message = match self.receiver.lock().unwrap().recv_message() {
                Ok(message) => message,
                Err(e) => {
                    println!("{}", e);
                    continue;
                }
            };
            match message {
                Message::Text(data) => {
                    let cmdpacket: GenericPacket = json::decode(&data).unwrap();
                    let cmd = cmdpacket.cmd;
                    if cmd == "chat" {
                        let decodedpacket: ChatPacket = json::decode(&data).unwrap();
                        if decodedpacket.nick != self.nick {
                            return Some(ChatEvent::Message (
                                    decodedpacket.nick,
                                    decodedpacket.text,
                                    decodedpacket.trip.unwrap_or("".to_string())
                            ));
                        }else {
                            continue;
                        }
                    }else if cmd == "info" {
                        let decodedpacket: InfoWarnPacket = json::decode(&data).unwrap();
                        return Some(ChatEvent::Info (
                            decodedpacket.text
                        ));
                    }else if cmd == "onlineAdd" {
                        let decodedpacket: OnlineChangePacket = json::decode(&data).unwrap();
                        return Some(ChatEvent::JoinRoom (
                            decodedpacket.nick
                        ));
                    }else if cmd == "onlineRemove" {
                        let decodedpacket: OnlineChangePacket = json::decode(&data).unwrap();
                        return Some(ChatEvent::LeaveRoom (
                            decodedpacket.nick
                        ));
                    }else {
                        continue;
                    }
                },
                Message::Ping(data) => {
                    self.sender.lock().unwrap().send_message(Message::Pong(data)).unwrap();
                },
                _ => {
                    return None;
                }
            };
            return None;
        }
    }
}

/// Various Hack.chat events
pub enum ChatEvent {
    /// Raised when there is a new message from the channel
    /// The format is ChatEvent::Message(nick, text, trip_code)
    Message (String, String, String),
    /// Rasied when someone joins the channel
    /// The format is ChatEvent::JoinRoom(nick)
    JoinRoom (String),
    /// Raised when someone leaves the channel
    /// The format is ChatEvent::LeaveRoom(nick)
    LeaveRoom (String),
    Info (String)
}

#[derive(RustcEncodable, RustcDecodable)]
struct GenericPacket {
    cmd: String
}

#[derive(RustcDecodable)]
struct ChatPacket {
    nick: String,
    text: String,
    trip: Option<String>
}

#[derive(RustcDecodable)]
struct OnlineChangePacket {
    nick: String
}

#[derive(RustcDecodable)]
struct InfoWarnPacket {
    text: String
}

#[derive(RustcEncodable)]
struct JoinPacket {
    cmd: String,
    channel: String,
    nick: String
}

#[derive(RustcEncodable)]
struct ChatPacketSend {
    cmd: String,
    text: String
}
<commit_msg>Expanded documentation.<commit_after>extern crate websocket;
extern crate rustc_serialize;

use std::thread;

use rustc_serialize::json;

use websocket::{Client, Message, WebSocketStream};
use websocket::client::request::Url;
use websocket::client::sender::Sender;
use websocket::client::receiver::Receiver;

use websocket::ws::sender::Sender as SenderTrait;
use websocket::ws::receiver::Receiver as ReceiverTrait;

use std::sync::Arc;
use std::sync::Mutex;

//! #rust-hackchat
//! A client library for Hack.chat.
//!
//! This library allows you to make custom clients and bots for Hack.chat using Rust.
//!
//! #Examples
//!
//! ```
//! extern crate hackchat;
//! use hackchat::{ChatClient, ChatEvent};
//!
//! fn main() {
//!     let mut conn = ChatClient::new("TestBot", "botDev"); //Connects to the ?botDev channel
//!     conn.start_ping_thread(); //Sends ping packets regularly
//!
//!     for event in conn.iter() {
//!         match event {
//!             ChatEvent::Message(nick, message, trip_code) => {
//!                 println!("<{}> {}", nick, message);
//!             },
//!             _ => {}
//!         }
//!     }
//! }
//! ```

#[derive(Clone)]
pub struct ChatClient {
    nick: String,
    channel: String,
    sender: Arc<Mutex<Sender<WebSocketStream>>>,
    receiver: Arc<Mutex<Receiver<WebSocketStream>>>,
}

impl ChatClient {
    /// Creates a new connection to hack.chat.
    pub fn new(nick: &str, channel: &str) -> ChatClient {
        let url = Url::parse("wss://hack.chat/chat-ws").unwrap();
        let request = Client::connect(url).unwrap();
        let response = request.send().unwrap();

        let client = response.begin();
        let (mut sender, receiver) = client.split();

        let join_packet = json::encode(&JoinPacket {
            cmd: "join".to_string(),
            nick: nick.to_string(),
            channel: channel.to_string()
        }).unwrap();
        let message = Message::Text(join_packet);
        sender.send_message(message).unwrap();

        return ChatClient {
            nick: nick.to_string(),
            channel: channel.to_string(),
            sender: Arc::new(Mutex::new(sender)),
            receiver: Arc::new(Mutex::new(receiver))
        };
    }

    /// Sends a message to the current channel.
    ///
    /// ```
    /// let mut chat = ChatClient::new("TestBot", "botDev");
    /// chat.send_message("Hello there people".to_string());
    /// ```
    pub fn send_message(&mut self, message: String) {
        let chat_packet = json::encode(&ChatPacketSend {
            cmd: "chat".to_string(),
            text: message
        }).unwrap();
        let message = Message::Text(chat_packet);
        self.sender.lock().unwrap().send_message(message).unwrap();
    }

    fn send_ping(&mut self) {
        let ping_packet = json::encode(&GenericPacket {
            cmd: "ping".to_string()
        }).unwrap();
        let message = Message::Text(ping_packet);
        self.sender.lock().unwrap().send_message(message).unwrap();
    }

    pub fn send_stats_request(&mut self) {
        let stats_packet = json::encode(&GenericPacket {
            cmd: "stats".to_string()
        }).unwrap();
        let message = Message::Text(stats_packet);
        self.sender.lock().unwrap().send_message(message).unwrap();
    }

    /// Starts the ping thread, whichs sends regular pings to keep the connection open.
    pub fn start_ping_thread(&mut self) {
        let mut chat_clone = self.clone();
        thread::spawn(move|| {
            loop {
                thread::sleep_ms(60 * 1000);
                chat_clone.send_ping();
            }
        });
    }

    /// Returns an iterator of hack.chat events such as messages.
    pub fn iter(&mut self) -> ChatClient {
        return self.clone();
    }
}

impl Iterator for ChatClient {
    type Item = ChatEvent;
    fn next(&mut self) -> Option<ChatEvent> {
        loop {
            let message = match self.receiver.lock().unwrap().recv_message() {
                Ok(message) => message,
                Err(e) => {
                    println!("{}", e);
                    continue;
                }
            };
            match message {
                Message::Text(data) => {
                    let cmdpacket: GenericPacket = json::decode(&data).unwrap();
                    let cmd = cmdpacket.cmd;
                    if cmd == "chat" {
                        let decodedpacket: ChatPacket = json::decode(&data).unwrap();
                        if decodedpacket.nick != self.nick {
                            return Some(ChatEvent::Message (
                                    decodedpacket.nick,
                                    decodedpacket.text,
                                    decodedpacket.trip.unwrap_or("".to_string())
                            ));
                        }else {
                            continue;
                        }
                    }else if cmd == "info" {
                        let decodedpacket: InfoWarnPacket = json::decode(&data).unwrap();
                        return Some(ChatEvent::Info (
                            decodedpacket.text
                        ));
                    }else if cmd == "onlineAdd" {
                        let decodedpacket: OnlineChangePacket = json::decode(&data).unwrap();
                        return Some(ChatEvent::JoinRoom (
                            decodedpacket.nick
                        ));
                    }else if cmd == "onlineRemove" {
                        let decodedpacket: OnlineChangePacket = json::decode(&data).unwrap();
                        return Some(ChatEvent::LeaveRoom (
                            decodedpacket.nick
                        ));
                    }else {
                        continue;
                    }
                },
                Message::Ping(data) => {
                    self.sender.lock().unwrap().send_message(Message::Pong(data)).unwrap();
                },
                _ => {
                    return None;
                }
            };
            return None;
        }
    }
}

/// Various Hack.chat events
pub enum ChatEvent {
    /// Raised when there is a new message from the channel
    /// The format is ChatEvent::Message(nick, text, trip_code)
    Message (String, String, String),
    /// Rasied when someone joins the channel
    /// The format is ChatEvent::JoinRoom(nick)
    JoinRoom (String),
    /// Raised when someone leaves the channel
    /// The format is ChatEvent::LeaveRoom(nick)
    LeaveRoom (String),
    /// Raised when there is an event from the channel itself.
    /// Some examples include:
    ///
    /// * The result of the stats requests
    /// * A user being banned.
    Info (String)
}

#[derive(RustcEncodable, RustcDecodable)]
struct GenericPacket {
    cmd: String
}

#[derive(RustcDecodable)]
struct ChatPacket {
    nick: String,
    text: String,
    trip: Option<String>
}

#[derive(RustcDecodable)]
struct OnlineChangePacket {
    nick: String
}

#[derive(RustcDecodable)]
struct InfoWarnPacket {
    text: String
}

#[derive(RustcEncodable)]
struct JoinPacket {
    cmd: String,
    channel: String,
    nick: String
}

#[derive(RustcEncodable)]
struct ChatPacketSend {
    cmd: String,
    text: String
}
<|endoftext|>


<commit_before>extern crate futures;

use futures::{Async, Future, Poll};

use std::marker::PhantomData;
use std::mem;

mod and_then;
mod chain;
mod map;

pub use and_then::AndThen;
pub use map::TaskedMap;

pub trait TaskedFuture {
    type Item;
    type Error;
    type Task;

    fn poll(&mut self, t: &mut Self::Task) -> Poll<Self::Item, Self::Error>;

    fn map<F, U>(self, f: F) -> TaskedMap<Self, F> where F: FnOnce(Self::Item, &mut Self::Task) -> U, Self: Sized {
        map::new(self, f)
    }

    fn and_then<F, B>(self, f: F) -> AndThen<Self, B, F>
        where F: FnOnce(Self::Item, &mut Self::Task) -> B,
              B: IntoTaskedFuture<Error = Self::Error, Task=Self::Task>,
              Self: Sized,
    {
        and_then::new(self, f)
    }

}

pub trait IntoTaskedFuture {
    /// The future that this type can be converted into.
    type TaskedFuture: TaskedFuture<Item=Self::Item, Error=Self::Error, Task=Self::Task>;

    type Item;
    type Error;
    type Task;

    fn into_tasked_future(self) -> Self::TaskedFuture;
}

impl<T> IntoTaskedFuture for T where T: TaskedFuture {
    type TaskedFuture = Self;

    type Item = <Self as TaskedFuture>::Item;
    type Error = <Self as TaskedFuture>::Error;
    type Task = <Self as TaskedFuture>::Task;

    fn into_tasked_future(self) -> Self::TaskedFuture {
        self
    }
}

pub trait FutureTaskedExt: Future + Sized {
    fn into_tasked<T>(self) -> TaskedFutureAdaptor<Self, T> {
        TaskedFutureAdaptor::new(self)
    }
}

impl<F: Future + Sized> FutureTaskedExt for F {}

pub struct TaskedFutureAdaptor<F, T> {
    future: F,
    _phantom_data: PhantomData<T>,
}

impl<F, T> TaskedFutureAdaptor<F, T> {
    pub fn new(future: F) -> TaskedFutureAdaptor<F, T> {
        TaskedFutureAdaptor {
            future: future,
            _phantom_data: PhantomData,
        }
    }
}

impl<F, I, E, T> TaskedFuture for TaskedFutureAdaptor<F, T> where F: Future<Item=I, Error=E> {
    type Item = I;
    type Error = E;
    type Task = T;

    fn poll(&mut self, _task: &mut Self::Task) -> Poll<I, E> {
        self.future.poll()
    }
}

type BoxedExecutorFuture<T, E> = Box<TaskedFuture<Item=(), Error=E, Task=T>>;

pub struct TaskExecutorQueue<T, E> {
    queue: Vec<BoxedExecutorFuture<T, E>>,
}

impl<T, E> TaskExecutorQueue<T, E> {
    pub fn new() -> TaskExecutorQueue<T, E> {
        TaskExecutorQueue {
            queue: Vec::new(),
        }
    }
}

impl<T, E> Default for TaskExecutorQueue<T, E> {
    fn default() -> TaskExecutorQueue<T, E> {
        TaskExecutorQueue {
            queue: Vec::new(),
        }
    }
}

pub trait TaskExecutor: Sized {
    type Error;

    fn task_executor_mut(&mut self) -> &mut TaskExecutorQueue<Self, Self::Error>;

    fn spawn<F>(&mut self, future: F) where F: TaskedFuture<Item=(), Error=Self::Error, Task=Self> + 'static {
        self.task_executor_mut().queue.push(Box::new(future))
    }

    fn poll(&mut self) -> Poll<(), Self::Error> {
        let len = self.task_executor_mut().queue.len();
        let queue = mem::replace(&mut self.task_executor_mut().queue, Vec::with_capacity(len));

        let mut to_append = Vec::with_capacity(len);
        for mut future in queue {
            if let Async::NotReady = try!(future.poll(self)) {
                to_append.push(future);
            }
        }

        self.task_executor_mut().queue.append(&mut to_append);

        if self.task_executor_mut().queue.is_empty() {
            Ok(Async::Ready(()))
        } else {
            Ok(Async::NotReady)
        }
    }

    fn into_future(self) -> TaskExecutorFuture<Self> {
        TaskExecutorFuture { inner: self }
    }
}

pub struct TaskExecutorFuture<T> {
    inner: T
}

impl<T, E> Future for TaskExecutorFuture<T> where T: TaskExecutor<Error=E> {
    type Item = ();
    type Error = E;

    fn poll(&mut self) -> Poll<(), Self::Error> {
        self.inner.poll()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use futures::{Async, Future, empty, oneshot, task, lazy};
    use std::sync::Arc;
    use std::cell::Cell;

    struct DummyUnpark;

    impl task::Unpark for DummyUnpark {
        fn unpark(&self) {}
    }

    #[derive(Default)]
    struct TestTask {
        data: Arc<Cell<u8>>,
        executor: TaskExecutorQueue<TestTask, ()>,
    }

    impl TaskExecutor for TestTask {
        type Error = ();

        fn task_executor_mut(&mut self) -> &mut TaskExecutorQueue<Self, Self::Error> {
            &mut self.executor
        }
    }

    #[test]
    fn simple_no_future() {
        let mut task = TestTask::default();
        assert_eq!(Ok(Async::Ready(())), task.poll());
    }

    #[test]
    fn simple_empty_future() {
        let mut task = TestTask::default();

        task.spawn(empty().into_tasked());

        assert_eq!(Ok(Async::NotReady), task.poll());
    }

    #[test]
    fn simeple_oneshot() {
        let (c, p) = oneshot();

        let mut task = TestTask::default();
        task.spawn(p.map_err(|_| ()).into_tasked());

        let mut task = task::spawn(task.into_future());

        assert_eq!(Ok(Async::NotReady), task.poll_future(Arc::new(DummyUnpark)));

        c.complete(());

        assert_eq!(Ok(Async::Ready(())), task.poll_future(Arc::new(DummyUnpark)));
    }

    #[test]
    fn map() {
        let (c, p) = oneshot();

        let mut task = TestTask::default();
        task.spawn(
            p.map_err(|_| ())
                .into_tasked::<TestTask>()
                .map(|res, task| task.data.set(res))
        );

        let data = task.data.clone();
        let mut task = task::spawn(task.into_future());

        assert_eq!(Ok(Async::NotReady), task.poll_future(Arc::new(DummyUnpark)));
        assert_eq!(data.get(), 0);

        c.complete(2u8);

        assert_eq!(Ok(Async::Ready(())), task.poll_future(Arc::new(DummyUnpark)));
        assert_eq!(data.get(), 2);
    }

    #[test]
    fn and_then() {
        let (c, p) = oneshot();

        let mut task = TestTask::default();
        task.spawn(
            p.map_err(|_| ())
                .into_tasked::<TestTask>()
                .and_then(|_, _| {
                    lazy(|| Ok(2u8))
                        .map_err(|_: ()| ())
                        .into_tasked::<TestTask>()
                        .map(|res, task| task.data.set(res))
                })
        );

        let data = task.data.clone();
        let mut task = task::spawn(task.into_future());

        assert_eq!(Ok(Async::NotReady), task.poll_future(Arc::new(DummyUnpark)));
        assert_eq!(data.get(), 0);

        c.complete(2u8);

        assert_eq!(Ok(Async::Ready(())), task.poll_future(Arc::new(DummyUnpark)));
        assert_eq!(data.get(), 2);
    }

    #[test]
    fn spawn() {
        let mut task = TestTask::default();
        task.spawn(
            lazy(|| Ok(()))
                .map_err(|_: ()| ())
                .into_tasked::<TestTask>()
                .map(|_, task| {
                    task.spawn(
                        lazy(|| Ok(2u8))
                            .map_err(|_: ()| ())
                            .into_tasked::<TestTask>()
                            .map(|res, task| task.data.set(res))
                    )
                })
        );

        let data = task.data.clone();
        let mut task = task::spawn(task.into_future());

        assert_eq!(data.get(), 0u8);
        while let Async::NotReady = task.poll_future(Arc::new(DummyUnpark)).unwrap() {}
        assert_eq!(data.get(), 2u8);
    }
}
<commit_msg>Add spawn_fn<commit_after>extern crate futures;

use futures::{Async, Future, Poll};

use std::marker::PhantomData;
use std::mem;

mod and_then;
mod chain;
mod map;

pub use and_then::AndThen;
pub use map::TaskedMap;

pub trait TaskedFuture {
    type Item;
    type Error;
    type Task;

    fn poll(&mut self, t: &mut Self::Task) -> Poll<Self::Item, Self::Error>;

    fn map<F, U>(self, f: F) -> TaskedMap<Self, F> where F: FnOnce(Self::Item, &mut Self::Task) -> U, Self: Sized {
        map::new(self, f)
    }

    fn and_then<F, B>(self, f: F) -> AndThen<Self, B, F>
        where F: FnOnce(Self::Item, &mut Self::Task) -> B,
              B: IntoTaskedFuture<Error = Self::Error, Task=Self::Task>,
              Self: Sized,
    {
        and_then::new(self, f)
    }

}

struct FnWrapper<F, T, R, E> where F: FnMut(&mut T) -> Poll<R, E> {
    func: F,
    _phantom_data: PhantomData<(T, R, E)>,
}

impl<F, T, R, E> FnWrapper<F, T, R, E> where F: FnMut(&mut T) -> Poll<R, E> {
    pub fn new(f: F) -> FnWrapper<F, T, R, E> {
        FnWrapper {
            func: f,
            _phantom_data: PhantomData,
        }
    }
}

impl<F, T, R, E> TaskedFuture for FnWrapper<F, T, R, E> where F: FnMut(&mut T) -> Poll<R, E> {
    type Item = R;
    type Error = E;
    type Task = T;

    fn poll(&mut self, task: &mut Self::Task) -> Poll<Self::Item, Self::Error> {
        (self.func)(task)
    }
}

pub trait IntoTaskedFuture {
    /// The future that this type can be converted into.
    type TaskedFuture: TaskedFuture<Item=Self::Item, Error=Self::Error, Task=Self::Task>;

    type Item;
    type Error;
    type Task;

    fn into_tasked_future(self) -> Self::TaskedFuture;
}

impl<T> IntoTaskedFuture for T where T: TaskedFuture {
    type TaskedFuture = Self;

    type Item = <Self as TaskedFuture>::Item;
    type Error = <Self as TaskedFuture>::Error;
    type Task = <Self as TaskedFuture>::Task;

    fn into_tasked_future(self) -> Self::TaskedFuture {
        self
    }
}

pub trait FutureTaskedExt: Future + Sized {
    fn into_tasked<T>(self) -> TaskedFutureAdaptor<Self, T> {
        TaskedFutureAdaptor::new(self)
    }
}

impl<F: Future + Sized> FutureTaskedExt for F {}

pub struct TaskedFutureAdaptor<F, T> {
    future: F,
    _phantom_data: PhantomData<T>,
}

impl<F, T> TaskedFutureAdaptor<F, T> {
    pub fn new(future: F) -> TaskedFutureAdaptor<F, T> {
        TaskedFutureAdaptor {
            future: future,
            _phantom_data: PhantomData,
        }
    }
}

impl<F, I, E, T> TaskedFuture for TaskedFutureAdaptor<F, T> where F: Future<Item=I, Error=E> {
    type Item = I;
    type Error = E;
    type Task = T;

    fn poll(&mut self, _task: &mut Self::Task) -> Poll<I, E> {
        self.future.poll()
    }
}

type BoxedExecutorFuture<T, E> = Box<TaskedFuture<Item=(), Error=E, Task=T>>;

pub struct TaskExecutorQueue<T, E> {
    queue: Vec<BoxedExecutorFuture<T, E>>,
}

impl<T, E> TaskExecutorQueue<T, E> {
    pub fn new() -> TaskExecutorQueue<T, E> {
        TaskExecutorQueue {
            queue: Vec::new(),
        }
    }
}

impl<T, E> Default for TaskExecutorQueue<T, E> {
    fn default() -> TaskExecutorQueue<T, E> {
        TaskExecutorQueue {
            queue: Vec::new(),
        }
    }
}

pub trait TaskExecutor: Sized + 'static {
    type Error;

    fn task_executor_mut(&mut self) -> &mut TaskExecutorQueue<Self, Self::Error>;

    fn spawn<F>(&mut self, future: F) where F: IntoTaskedFuture<Item=(), Error=Self::Error, Task=Self> + 'static {
        self.task_executor_mut().queue.push(Box::new(future.into_tasked_future()))
    }

    fn spawn_fn<F>(&mut self, func: F) where F: FnMut(&mut Self) -> Poll<(), Self::Error> + 'static {
        self.task_executor_mut().queue.push(Box::new(FnWrapper::new(func)))
    }

    fn poll(&mut self) -> Poll<(), Self::Error> {
        let len = self.task_executor_mut().queue.len();
        let queue = mem::replace(&mut self.task_executor_mut().queue, Vec::with_capacity(len));

        let mut to_append = Vec::with_capacity(len);
        for mut future in queue {
            if let Async::NotReady = try!(future.poll(self)) {
                to_append.push(future);
            }
        }

        self.task_executor_mut().queue.append(&mut to_append);

        if self.task_executor_mut().queue.is_empty() {
            Ok(Async::Ready(()))
        } else {
            Ok(Async::NotReady)
        }
    }

    fn into_future(self) -> TaskExecutorFuture<Self> {
        TaskExecutorFuture { inner: self }
    }
}

pub struct TaskExecutorFuture<T> {
    inner: T
}

impl<T, E> Future for TaskExecutorFuture<T> where T: TaskExecutor<Error=E> {
    type Item = ();
    type Error = E;

    fn poll(&mut self) -> Poll<(), Self::Error> {
        self.inner.poll()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use futures::{Async, Future, empty, oneshot, task, lazy};
    use std::sync::Arc;
    use std::cell::Cell;

    struct DummyUnpark;

    impl task::Unpark for DummyUnpark {
        fn unpark(&self) {}
    }

    #[derive(Default)]
    struct TestTask {
        data: Arc<Cell<u8>>,
        executor: TaskExecutorQueue<TestTask, ()>,
    }

    impl TaskExecutor for TestTask {
        type Error = ();

        fn task_executor_mut(&mut self) -> &mut TaskExecutorQueue<Self, Self::Error> {
            &mut self.executor
        }
    }

    #[test]
    fn simple_no_future() {
        let mut task = TestTask::default();
        assert_eq!(Ok(Async::Ready(())), task.poll());
    }

    #[test]
    fn simple_empty_future() {
        let mut task = TestTask::default();

        task.spawn(empty().into_tasked());

        assert_eq!(Ok(Async::NotReady), task.poll());
    }

    #[test]
    fn simeple_oneshot() {
        let (c, p) = oneshot();

        let mut task = TestTask::default();
        task.spawn(p.map_err(|_| ()).into_tasked());

        let mut task = task::spawn(task.into_future());

        assert_eq!(Ok(Async::NotReady), task.poll_future(Arc::new(DummyUnpark)));

        c.complete(());

        assert_eq!(Ok(Async::Ready(())), task.poll_future(Arc::new(DummyUnpark)));
    }

    #[test]
    fn map() {
        let (c, p) = oneshot();

        let mut task = TestTask::default();
        task.spawn(
            p.map_err(|_| ())
                .into_tasked::<TestTask>()
                .map(|res, task| task.data.set(res))
        );

        let data = task.data.clone();
        let mut task = task::spawn(task.into_future());

        assert_eq!(Ok(Async::NotReady), task.poll_future(Arc::new(DummyUnpark)));
        assert_eq!(data.get(), 0);

        c.complete(2u8);

        assert_eq!(Ok(Async::Ready(())), task.poll_future(Arc::new(DummyUnpark)));
        assert_eq!(data.get(), 2);
    }

    #[test]
    fn and_then() {
        let (c, p) = oneshot();

        let mut task = TestTask::default();
        task.spawn(
            p.map_err(|_| ())
                .into_tasked::<TestTask>()
                .and_then(|_, _| {
                    lazy(|| Ok(2u8))
                        .map_err(|_: ()| ())
                        .into_tasked::<TestTask>()
                        .map(|res, task| task.data.set(res))
                })
        );

        let data = task.data.clone();
        let mut task = task::spawn(task.into_future());

        assert_eq!(Ok(Async::NotReady), task.poll_future(Arc::new(DummyUnpark)));
        assert_eq!(data.get(), 0);

        c.complete(2u8);

        assert_eq!(Ok(Async::Ready(())), task.poll_future(Arc::new(DummyUnpark)));
        assert_eq!(data.get(), 2);
    }

    #[test]
    fn spawn() {
        let mut task = TestTask::default();
        task.spawn(
            lazy(|| Ok(()))
                .map_err(|_: ()| ())
                .into_tasked::<TestTask>()
                .map(|_, task| {
                    task.spawn(
                        lazy(|| Ok(2u8))
                            .map_err(|_: ()| ())
                            .into_tasked::<TestTask>()
                            .map(|res, task| task.data.set(res))
                    )
                })
        );

        let data = task.data.clone();
        let mut task = task::spawn(task.into_future());

        assert_eq!(data.get(), 0u8);
        while let Async::NotReady = task.poll_future(Arc::new(DummyUnpark)).unwrap() {}
        assert_eq!(data.get(), 2u8);
    }

    #[test]
    fn fnmut() {
        let mut task = TestTask::default();
        task.spawn_fn(|task| {
            task.data.set(task.data.get() + 1);
            Ok(Async::NotReady)
        });

        let data = task.data.clone();
        assert_eq!(Ok(Async::NotReady), task.poll());
        assert_eq!(data.get(), 1);
        assert_eq!(Ok(Async::NotReady), task.poll());
        assert_eq!(data.get(), 2);
    }
}
<|endoftext|>


<commit_before>//! This crate provides a stable, safe and scoped threadpool.
//!
//! It can be used to execute a number of short-lived jobs in parallel
//! without the need to respawn the underlying threads.
//!
//! Jobs are runnable by borrowing the pool for a given scope, during which
//! an arbitrary number of jobs can be executed. These jobs can access data of
//! any lifetime outside of the pool's scope, which allows working with
//! non-`'static` references in parallel.
//!
//! For safety reasons, a panic inside a worker thread will not be isolated,
//! but rather propagate to the outside of the pool.
//!
//! # Examples:
//!
//! ```rust
//! extern crate scoped_threadpool;
//! use scoped_threadpool::Pool;
//!
//! fn main() {
//!     // Create a threadpool holding 4 threads
//!     let mut pool = Pool::new(4);
//!
//!     let mut vec = vec![0, 1, 2, 3, 4, 5, 6, 7];
//!
//!     // Use the threads as scoped threads that can
//!     // reference anything outside this closure
//!     pool.scoped(|scope| {
//!         // Create references to each element in the vector ...
//!         for e in &mut vec {
//!             // ... and add 1 to it in a seperate thread
//!             scope.execute(move || {
//!                 *e += 1;
//!             });
//!         }
//!     });
//!
//!     assert_eq!(vec, vec![1, 2, 3, 4, 5, 6, 7, 8]);
//! }
//! ```

#![cfg_attr(all(feature="nightly", test), feature(test))]
#![cfg_attr(feature="nightly", feature(const_fn))]
#![warn(missing_docs)]

#[macro_use]
#[cfg(test)]
extern crate lazy_static;

use std::thread::{self, JoinHandle};
use std::sync::mpsc::{channel, Sender, Receiver, SyncSender, sync_channel};
use std::sync::{Arc, Mutex};
use std::marker::PhantomData;
use std::mem;

enum Message {
    NewJob(Thunk<'static>),
    Join,
}

trait FnBox {
    fn call_box(self: Box<Self>);
}

impl<F: FnOnce()> FnBox for F {
    fn call_box(self: Box<F>) {
        (*self)()
    }
}

type Thunk<'a> = Box<FnBox + Send + 'a>;

impl Drop for Pool {
    fn drop(&mut self) {
        mem::replace(&mut self.job_sender, None);
    }
}

/// A threadpool that acts as a handle to a number
/// of threads spawned at construction.
pub struct Pool {
    threads: Vec<ThreadData>,
    job_sender: Option<Sender<Message>>
}

struct ThreadData {
    _thread_join_handle: JoinHandle<()>,
    pool_sync_rx: Receiver<()>,
    thread_sync_tx: SyncSender<()>,
}

impl Pool {
    /// Construct a threadpool with the given number of threads.
    /// Minimum value is `1`.
    pub fn new(n: u32) -> Pool {
        assert!(n >= 1);

        let (job_sender, job_receiver) = channel();
        let job_receiver = Arc::new(Mutex::new(job_receiver));

        let mut threads = Vec::with_capacity(n as usize);

        // spawn n threads, put them in waiting mode
        for _ in 0..n {
            let job_receiver = job_receiver.clone();

            let (pool_sync_tx, pool_sync_rx) =
                sync_channel::<()>(0);
            let (thread_sync_tx, thread_sync_rx) =
                sync_channel::<()>(0);

            let thread = thread::spawn(move || {
                loop {
                    let message = {
                        // Only lock jobs for the time it takes
                        // to get a job, not run it.
                        let lock = job_receiver.lock().unwrap();
                        lock.recv()
                    };

                    match message {
                        Ok(Message::NewJob(job)) => {
                            job.call_box();
                        }
                        Ok(Message::Join) => {
                            // Syncronize/Join with pool.
                            // This has to be a two step
                            // process to ensure that all threads
                            // finished their work before the pool
                            // can continue

                            // Wait until the pool started syncing with threads
                            if pool_sync_tx.send(()).is_err() {
                                // The pool was dropped.
                                break;
                            }

                            // Wait until the pool finished syncing with threads
                            if thread_sync_rx.recv().is_err() {
                                // The pool was dropped.
                                break;
                            }
                        }
                        Err(..) => {
                            // The pool was dropped.
                            break
                        }
                    }
                }
            });

            threads.push(ThreadData {
                _thread_join_handle: thread,
                pool_sync_rx: pool_sync_rx,
                thread_sync_tx: thread_sync_tx,
            });
        }

        Pool {
            threads: threads,
            job_sender: Some(job_sender),
        }
    }

    /// Borrows the pool and allows executing jobs on other
    /// threads during that scope via the argument of the closure.
    ///
    /// This method will block until the closure and all its jobs have
    /// run to completion.
    pub fn scoped<'pool, 'scope, F, R>(&'pool mut self, f: F) -> R
        where F: FnOnce(&Scope<'pool, 'scope>) -> R
    {
        let scope = Scope {
            pool: self,
            _marker: PhantomData,
        };
        f(&scope)
    }

    /// Returns the number of threads inside this pool.
    pub fn thread_count(&self) -> u32 {
        self.threads.len() as u32
    }
}

/////////////////////////////////////////////////////////////////////////////

/// Handle to the scope during which the threadpool is borrowed.
pub struct Scope<'pool, 'scope> {
    pool: &'pool mut Pool,
    _marker: PhantomData<&'scope mut ()>,
}

impl<'pool, 'scope> Scope<'pool, 'scope> {
    /// Execute a job on the threadpool.
    ///
    /// The body of the closure will be send to one of the
    /// internal threads, and this method itself will not wait
    /// for its completion.
    pub fn execute<F>(&self, f: F)
        where F: FnOnce() + Send + 'scope
    {
        let b = unsafe {
            mem::transmute::<Thunk<'scope>, Thunk<'static>>(Box::new(f))
        };
        self.pool.job_sender.as_ref().unwrap().send(Message::NewJob(b)).unwrap();
    }
}

impl<'pool, 'scope> Drop for Scope<'pool, 'scope> {
    fn drop(&mut self) {
        for _ in 0..self.pool.threads.len() {
            self.pool.job_sender.as_ref().unwrap().send(Message::Join).unwrap();
        }

        // Syncronize/Join with threads
        // This has to be a two step process
        // to make sure _all_ threads received _one_ Join message each.

        // This loop will block on every thread until it
        // received and reacted to its Join message.
        for thread_data in &self.pool.threads {
            thread_data.pool_sync_rx.recv().unwrap();
        }

        // Once all threads joined the jobs, send them a continue message
        for thread_data in &self.pool.threads {
            thread_data.thread_sync_tx.send(()).unwrap();
        }
    }
}

/////////////////////////////////////////////////////////////////////////////

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

    #[test]
    fn smoketest() {
        let mut pool = Pool::new(4);

        for i in 1..7 {
            let mut vec = vec![0, 1, 2, 3, 4];
            pool.scoped(|s| {
                for e in vec.iter_mut() {
                    s.execute(move || {
                        *e += i;
                    });
                }
            });

            let mut vec2 = vec![0, 1, 2, 3, 4];
            for e in vec2.iter_mut() {
                *e += i;
            }

            assert_eq!(vec, vec2);
        }
    }

    #[test]
    #[should_panic]
    fn thread_panic() {
        let mut pool = Pool::new(4);
        pool.scoped(|scoped| {
            scoped.execute(move || {
                panic!()
            });
        });
    }

    #[test]
    #[should_panic]
    fn scope_panic() {
        let mut pool = Pool::new(4);
        pool.scoped(|_scoped| {
            panic!()
        });
    }

    #[test]
    #[should_panic]
    fn pool_panic() {
        let _pool = Pool::new(4);
        panic!()
    }
}

#[cfg(all(test, feature="nightly"))]
mod benches {
    extern crate test;

    use self::test::{Bencher, black_box};
    use super::Pool;
    use std::sync::Mutex;

    // const MS_SLEEP_PER_OP: u32 = 1;

    lazy_static! {
        static ref POOL_1: Mutex<Pool> = Mutex::new(Pool::new(1));
        static ref POOL_2: Mutex<Pool> = Mutex::new(Pool::new(2));
        static ref POOL_3: Mutex<Pool> = Mutex::new(Pool::new(3));
        static ref POOL_4: Mutex<Pool> = Mutex::new(Pool::new(4));
        static ref POOL_5: Mutex<Pool> = Mutex::new(Pool::new(5));
        static ref POOL_8: Mutex<Pool> = Mutex::new(Pool::new(8));
    }

    fn fib(n: u64) -> u64 {
        let mut prev_prev: u64 = 1;
        let mut prev = 1;
        let mut current = 1;
        for _ in 2..(n+1) {
            current = prev_prev.wrapping_add(prev);
            prev_prev = prev;
            prev = current;
        }
        current
    }

    fn threads_interleaved_n(pool: &mut Pool)  {
        let size = 1024; // 1kiB

        let mut data = vec![1u8; size];
        pool.scoped(|s| {
            for e in data.iter_mut() {
                s.execute(move || {
                    *e += fib(black_box(1000 * (*e as u64))) as u8;
                    for i in 0..10000 { black_box(i); }
                    //thread::sleep_ms(MS_SLEEP_PER_OP);
                });
            }
        });
    }

    #[bench]
    fn threads_interleaved_1(b: &mut Bencher) {
        b.iter(|| threads_interleaved_n(&mut POOL_1.lock().unwrap()))
    }

    #[bench]
    fn threads_interleaved_2(b: &mut Bencher) {
        b.iter(|| threads_interleaved_n(&mut POOL_2.lock().unwrap()))
    }

    #[bench]
    fn threads_interleaved_4(b: &mut Bencher) {
        b.iter(|| threads_interleaved_n(&mut POOL_4.lock().unwrap()))
    }

    #[bench]
    fn threads_interleaved_8(b: &mut Bencher) {
        b.iter(|| threads_interleaved_n(&mut POOL_8.lock().unwrap()))
    }

    fn threads_chunked_n(pool: &mut Pool) {
        // Set this to 1GB and 40 to get good but slooow results
        let size = 1024 * 1024 * 10 / 4; // 10MiB
        let bb_repeat = 50;

        let n = pool.thread_count();
        let mut data = vec![0u32; size];
        pool.scoped(|s| {
            let l = (data.len() - 1) / n as usize + 1;
            for es in data.chunks_mut(l) {
                s.execute(move || {
                    if es.len() > 1 {
                        es[0] = 1;
                        es[1] = 1;
                        for i in 2..es.len() {
                            // Fibonnaci gets big fast,
                            // so just wrap around all the time
                            es[i] = black_box(es[i-1].wrapping_add(es[i-2]));
                            for i in 0..bb_repeat { black_box(i); }
                        }
                    }
                    //thread::sleep_ms(MS_SLEEP_PER_OP);
                });
            }
        });
    }

    #[bench]
    fn threads_chunked_1(b: &mut Bencher) {
        b.iter(|| threads_chunked_n(&mut POOL_1.lock().unwrap()))
    }

    #[bench]
    fn threads_chunked_2(b: &mut Bencher) {
        b.iter(|| threads_chunked_n(&mut POOL_2.lock().unwrap()))
    }

    #[bench]
    fn threads_chunked_3(b: &mut Bencher) {
        b.iter(|| threads_chunked_n(&mut POOL_3.lock().unwrap()))
    }

    #[bench]
    fn threads_chunked_4(b: &mut Bencher) {
        b.iter(|| threads_chunked_n(&mut POOL_4.lock().unwrap()))
    }

    #[bench]
    fn threads_chunked_5(b: &mut Bencher) {
        b.iter(|| threads_chunked_n(&mut POOL_5.lock().unwrap()))
    }

    #[bench]
    fn threads_chunked_8(b: &mut Bencher) {
        b.iter(|| threads_chunked_n(&mut POOL_8.lock().unwrap()))
    }
}
<commit_msg>doc fixes<commit_after>//! This crate provides a stable, safe and scoped threadpool.
//!
//! It can be used to execute a number of short-lived jobs in parallel
//! without the need to respawn the underlying threads.
//!
//! Jobs are runnable by borrowing the pool for a given scope, during which
//! an arbitrary number of them can be executed. These jobs can access data of
//! any lifetime outside of the pools scope, which allows working on
//! non-`'static` references in parallel.
//!
//! For safety reasons, a panic inside a worker thread will not be isolated,
//! but rather propagate to the outside of the pool.
//!
//! # Examples:
//!
//! ```rust
//! extern crate scoped_threadpool;
//! use scoped_threadpool::Pool;
//!
//! fn main() {
//!     // Create a threadpool holding 4 threads
//!     let mut pool = Pool::new(4);
//!
//!     let mut vec = vec![0, 1, 2, 3, 4, 5, 6, 7];
//!
//!     // Use the threads as scoped threads that can
//!     // reference anything outside this closure
//!     pool.scoped(|scope| {
//!         // Create references to each element in the vector ...
//!         for e in &mut vec {
//!             // ... and add 1 to it in a seperate thread
//!             scope.execute(move || {
//!                 *e += 1;
//!             });
//!         }
//!     });
//!
//!     assert_eq!(vec, vec![1, 2, 3, 4, 5, 6, 7, 8]);
//! }
//! ```

#![cfg_attr(all(feature="nightly", test), feature(test))]
#![cfg_attr(feature="nightly", feature(const_fn))]
#![warn(missing_docs)]

#[macro_use]
#[cfg(test)]
extern crate lazy_static;

use std::thread::{self, JoinHandle};
use std::sync::mpsc::{channel, Sender, Receiver, SyncSender, sync_channel};
use std::sync::{Arc, Mutex};
use std::marker::PhantomData;
use std::mem;

enum Message {
    NewJob(Thunk<'static>),
    Join,
}

trait FnBox {
    fn call_box(self: Box<Self>);
}

impl<F: FnOnce()> FnBox for F {
    fn call_box(self: Box<F>) {
        (*self)()
    }
}

type Thunk<'a> = Box<FnBox + Send + 'a>;

impl Drop for Pool {
    fn drop(&mut self) {
        mem::replace(&mut self.job_sender, None);
    }
}

/// A threadpool that acts as a handle to a number
/// of threads spawned at construction.
pub struct Pool {
    threads: Vec<ThreadData>,
    job_sender: Option<Sender<Message>>
}

struct ThreadData {
    _thread_join_handle: JoinHandle<()>,
    pool_sync_rx: Receiver<()>,
    thread_sync_tx: SyncSender<()>,
}

impl Pool {
    /// Construct a threadpool with the given number of threads.
    /// Minimum value is `1`.
    pub fn new(n: u32) -> Pool {
        assert!(n >= 1);

        let (job_sender, job_receiver) = channel();
        let job_receiver = Arc::new(Mutex::new(job_receiver));

        let mut threads = Vec::with_capacity(n as usize);

        // spawn n threads, put them in waiting mode
        for _ in 0..n {
            let job_receiver = job_receiver.clone();

            let (pool_sync_tx, pool_sync_rx) =
                sync_channel::<()>(0);
            let (thread_sync_tx, thread_sync_rx) =
                sync_channel::<()>(0);

            let thread = thread::spawn(move || {
                loop {
                    let message = {
                        // Only lock jobs for the time it takes
                        // to get a job, not run it.
                        let lock = job_receiver.lock().unwrap();
                        lock.recv()
                    };

                    match message {
                        Ok(Message::NewJob(job)) => {
                            job.call_box();
                        }
                        Ok(Message::Join) => {
                            // Syncronize/Join with pool.
                            // This has to be a two step
                            // process to ensure that all threads
                            // finished their work before the pool
                            // can continue

                            // Wait until the pool started syncing with threads
                            if pool_sync_tx.send(()).is_err() {
                                // The pool was dropped.
                                break;
                            }

                            // Wait until the pool finished syncing with threads
                            if thread_sync_rx.recv().is_err() {
                                // The pool was dropped.
                                break;
                            }
                        }
                        Err(..) => {
                            // The pool was dropped.
                            break
                        }
                    }
                }
            });

            threads.push(ThreadData {
                _thread_join_handle: thread,
                pool_sync_rx: pool_sync_rx,
                thread_sync_tx: thread_sync_tx,
            });
        }

        Pool {
            threads: threads,
            job_sender: Some(job_sender),
        }
    }

    /// Borrows the pool and allows executing jobs on other
    /// threads during that scope via the argument of the closure.
    ///
    /// This method will block until the closure and all its jobs have
    /// run to completion.
    pub fn scoped<'pool, 'scope, F, R>(&'pool mut self, f: F) -> R
        where F: FnOnce(&Scope<'pool, 'scope>) -> R
    {
        let scope = Scope {
            pool: self,
            _marker: PhantomData,
        };
        f(&scope)
    }

    /// Returns the number of threads inside this pool.
    pub fn thread_count(&self) -> u32 {
        self.threads.len() as u32
    }
}

/////////////////////////////////////////////////////////////////////////////

/// Handle to the scope during which the threadpool is borrowed.
pub struct Scope<'pool, 'scope> {
    pool: &'pool mut Pool,
    _marker: PhantomData<&'scope mut ()>,
}

impl<'pool, 'scope> Scope<'pool, 'scope> {
    /// Execute a job on the threadpool.
    ///
    /// The body of the closure will be send to one of the
    /// internal threads, and this method itself will not wait
    /// for its completion.
    pub fn execute<F>(&self, f: F)
        where F: FnOnce() + Send + 'scope
    {
        let b = unsafe {
            mem::transmute::<Thunk<'scope>, Thunk<'static>>(Box::new(f))
        };
        self.pool.job_sender.as_ref().unwrap().send(Message::NewJob(b)).unwrap();
    }
}

impl<'pool, 'scope> Drop for Scope<'pool, 'scope> {
    fn drop(&mut self) {
        for _ in 0..self.pool.threads.len() {
            self.pool.job_sender.as_ref().unwrap().send(Message::Join).unwrap();
        }

        // Syncronize/Join with threads
        // This has to be a two step process
        // to make sure _all_ threads received _one_ Join message each.

        // This loop will block on every thread until it
        // received and reacted to its Join message.
        for thread_data in &self.pool.threads {
            thread_data.pool_sync_rx.recv().unwrap();
        }

        // Once all threads joined the jobs, send them a continue message
        for thread_data in &self.pool.threads {
            thread_data.thread_sync_tx.send(()).unwrap();
        }
    }
}

/////////////////////////////////////////////////////////////////////////////

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

    #[test]
    fn smoketest() {
        let mut pool = Pool::new(4);

        for i in 1..7 {
            let mut vec = vec![0, 1, 2, 3, 4];
            pool.scoped(|s| {
                for e in vec.iter_mut() {
                    s.execute(move || {
                        *e += i;
                    });
                }
            });

            let mut vec2 = vec![0, 1, 2, 3, 4];
            for e in vec2.iter_mut() {
                *e += i;
            }

            assert_eq!(vec, vec2);
        }
    }

    #[test]
    #[should_panic]
    fn thread_panic() {
        let mut pool = Pool::new(4);
        pool.scoped(|scoped| {
            scoped.execute(move || {
                panic!()
            });
        });
    }

    #[test]
    #[should_panic]
    fn scope_panic() {
        let mut pool = Pool::new(4);
        pool.scoped(|_scoped| {
            panic!()
        });
    }

    #[test]
    #[should_panic]
    fn pool_panic() {
        let _pool = Pool::new(4);
        panic!()
    }
}

#[cfg(all(test, feature="nightly"))]
mod benches {
    extern crate test;

    use self::test::{Bencher, black_box};
    use super::Pool;
    use std::sync::Mutex;

    // const MS_SLEEP_PER_OP: u32 = 1;

    lazy_static! {
        static ref POOL_1: Mutex<Pool> = Mutex::new(Pool::new(1));
        static ref POOL_2: Mutex<Pool> = Mutex::new(Pool::new(2));
        static ref POOL_3: Mutex<Pool> = Mutex::new(Pool::new(3));
        static ref POOL_4: Mutex<Pool> = Mutex::new(Pool::new(4));
        static ref POOL_5: Mutex<Pool> = Mutex::new(Pool::new(5));
        static ref POOL_8: Mutex<Pool> = Mutex::new(Pool::new(8));
    }

    fn fib(n: u64) -> u64 {
        let mut prev_prev: u64 = 1;
        let mut prev = 1;
        let mut current = 1;
        for _ in 2..(n+1) {
            current = prev_prev.wrapping_add(prev);
            prev_prev = prev;
            prev = current;
        }
        current
    }

    fn threads_interleaved_n(pool: &mut Pool)  {
        let size = 1024; // 1kiB

        let mut data = vec![1u8; size];
        pool.scoped(|s| {
            for e in data.iter_mut() {
                s.execute(move || {
                    *e += fib(black_box(1000 * (*e as u64))) as u8;
                    for i in 0..10000 { black_box(i); }
                    //thread::sleep_ms(MS_SLEEP_PER_OP);
                });
            }
        });
    }

    #[bench]
    fn threads_interleaved_1(b: &mut Bencher) {
        b.iter(|| threads_interleaved_n(&mut POOL_1.lock().unwrap()))
    }

    #[bench]
    fn threads_interleaved_2(b: &mut Bencher) {
        b.iter(|| threads_interleaved_n(&mut POOL_2.lock().unwrap()))
    }

    #[bench]
    fn threads_interleaved_4(b: &mut Bencher) {
        b.iter(|| threads_interleaved_n(&mut POOL_4.lock().unwrap()))
    }

    #[bench]
    fn threads_interleaved_8(b: &mut Bencher) {
        b.iter(|| threads_interleaved_n(&mut POOL_8.lock().unwrap()))
    }

    fn threads_chunked_n(pool: &mut Pool) {
        // Set this to 1GB and 40 to get good but slooow results
        let size = 1024 * 1024 * 10 / 4; // 10MiB
        let bb_repeat = 50;

        let n = pool.thread_count();
        let mut data = vec![0u32; size];
        pool.scoped(|s| {
            let l = (data.len() - 1) / n as usize + 1;
            for es in data.chunks_mut(l) {
                s.execute(move || {
                    if es.len() > 1 {
                        es[0] = 1;
                        es[1] = 1;
                        for i in 2..es.len() {
                            // Fibonnaci gets big fast,
                            // so just wrap around all the time
                            es[i] = black_box(es[i-1].wrapping_add(es[i-2]));
                            for i in 0..bb_repeat { black_box(i); }
                        }
                    }
                    //thread::sleep_ms(MS_SLEEP_PER_OP);
                });
            }
        });
    }

    #[bench]
    fn threads_chunked_1(b: &mut Bencher) {
        b.iter(|| threads_chunked_n(&mut POOL_1.lock().unwrap()))
    }

    #[bench]
    fn threads_chunked_2(b: &mut Bencher) {
        b.iter(|| threads_chunked_n(&mut POOL_2.lock().unwrap()))
    }

    #[bench]
    fn threads_chunked_3(b: &mut Bencher) {
        b.iter(|| threads_chunked_n(&mut POOL_3.lock().unwrap()))
    }

    #[bench]
    fn threads_chunked_4(b: &mut Bencher) {
        b.iter(|| threads_chunked_n(&mut POOL_4.lock().unwrap()))
    }

    #[bench]
    fn threads_chunked_5(b: &mut Bencher) {
        b.iter(|| threads_chunked_n(&mut POOL_5.lock().unwrap()))
    }

    #[bench]
    fn threads_chunked_8(b: &mut Bencher) {
        b.iter(|| threads_chunked_n(&mut POOL_8.lock().unwrap()))
    }
}
<|endoftext|>


<commit_before>mod recommendation {
    use std::collections::HashMap;
    use std::collections::HashSet;
    use std::hash::{Hash, Hasher};
    use std::iter::FromIterator;

    #[derive(Debug,Default,Clone)]
    pub struct ItemSet {
        pub items: HashSet<u64>,
        pub support: f64,
        pub count: u64,
    }

    impl Hash for ItemSet {
        fn hash<H: Hasher>(&self, state: &mut H) {
            let mut a: Vec<u64> = self.items.iter().cloned().collect();
            a.sort();
            for s in a.iter() {
                state.write_u64(*s);
            }
        }
    }
    impl PartialEq for ItemSet {
        fn eq(&self, other: &ItemSet) -> bool {
            self.items == other.items
        }
    }
    impl Eq for ItemSet {}

    fn first_pass(sets: &Vec<Vec<u64>>, min_support: f64) -> (HashSet<ItemSet>, u64) {
        let mut sets_count = 0;
        let large = sets.iter()
            .inspect(|_| sets_count += 1)
            .flat_map(|x| x.iter())
            .fold(HashMap::new(), |mut acc, x| {
                *acc.entry(x).or_insert(0) += 1;
                acc
            })
            .iter()
            .filter(|&(_, &v)| v as f64 / sets_count as f64 > min_support)
            .fold(HashSet::new(), |mut acc, (&k, &v)| {
                acc.insert(ItemSet {
                    items: vec![*k].into_iter().collect::<HashSet<u64>>(),
                    support: v as f64 / sets_count as f64,
                    count: v,
                    ..Default::default()
                });

                acc
            });

        (large, sets_count)
    }

    fn generate_subsets(large: HashSet<ItemSet>) -> HashSet<ItemSet> {
        large.iter().fold(HashSet::new(), |mut acc, rit| {
            for lit in large.iter() {
                for i1 in rit.items.difference(&lit.items).cloned() {
                    let mut candidate = lit.items.clone();

                    candidate.insert(i1);
                    acc.insert(ItemSet { items: candidate, ..Default::default() });
                }
            }

            acc
        })
    }

    pub fn apriori(sets: Vec<Vec<u64>>, min_support: f64) -> HashSet<ItemSet> {
        let (mut large, sets_count) = first_pass(&sets, min_support);
        let mut output = HashSet::<ItemSet>::new();

        while !large.is_empty() {
            let mut candidates = generate_subsets(large);

            for set in sets.iter() {
                let hash_set = HashSet::from_iter(set.iter().cloned());

                candidates = candidates.into_iter()
                    .map(|mut x| {
                        if x.items.is_subset(&hash_set) {
                            x.count += 1;
                            x.support = x.count as f64 / sets_count as f64;
                        }

                        x
                    })
                    .collect();
            }

            large =
                HashSet::from_iter(candidates.iter().cloned().filter(|x| x.support > min_support));

            output.extend(large.clone());
        }

        output
    }
}
#[cfg(test)]

mod tests {
    use recommendation::*;

    #[test]
    fn it_works() {
        let sets = vec![vec![1, 2, 3, 4],
                        vec![1, 2, 4],
                        vec![1, 2],
                        vec![2, 3, 4],
                        vec![2, 3],
                        vec![3, 4],
                        vec![2, 4]];

        let output = apriori(sets, 0.42);

        assert_eq!(output.len(), 4);

        let set1output =
            output.get(&ItemSet { items: vec![4, 2].into_iter().collect(), ..Default::default() });
        let set2output =
            output.get(&ItemSet { items: vec![3, 2].into_iter().collect(), ..Default::default() });
        let set3output =
            output.get(&ItemSet { items: vec![1, 2].into_iter().collect(), ..Default::default() });
        let set4output =
            output.get(&ItemSet { items: vec![3, 4].into_iter().collect(), ..Default::default() });

        assert!(set1output.is_some());
        assert!(set2output.is_some());
        assert!(set3output.is_some());
        assert!(set4output.is_some());

        println!("Output: {:?}", output);
    }
}
<commit_msg>Set pruning<commit_after>mod recommendation {
    use std::collections::HashMap;
    use std::collections::HashSet;
    use std::hash::{Hash, Hasher};
    use std::iter::FromIterator;

    #[derive(Debug,Default,Clone)]
    pub struct ItemSet {
        pub items: HashSet<u64>,
        pub support: f64,
        pub count: u64,
    }

    impl Hash for ItemSet {
        fn hash<H: Hasher>(&self, state: &mut H) {
            let mut a: Vec<u64> = self.items.iter().cloned().collect();
            a.sort();
            for s in a.iter() {
                state.write_u64(*s);
            }
        }
    }
    impl PartialEq for ItemSet {
        fn eq(&self, other: &ItemSet) -> bool {
            self.items == other.items
        }
    }
    impl Eq for ItemSet {}

    fn first_pass(sets: &Vec<Vec<u64>>, min_support: f64) -> (HashSet<ItemSet>, u64) {
        let mut sets_count = 0;
        let large = sets.iter()
            .inspect(|_| sets_count += 1)
            .flat_map(|x| x.iter())
            .fold(HashMap::new(), |mut acc, x| {
                *acc.entry(x).or_insert(0) += 1;
                acc
            })
            .iter()
            .filter(|&(_, &v)| v as f64 / sets_count as f64 > min_support)
            .fold(HashSet::new(), |mut acc, (&k, &v)| {
                acc.insert(ItemSet {
                    items: vec![*k].into_iter().collect::<HashSet<u64>>(),
                    support: v as f64 / sets_count as f64,
                    count: v,
                    ..Default::default()
                });

                acc
            });

        (large, sets_count)
    }

    fn generate_subsets(large: HashSet<ItemSet>) -> HashSet<ItemSet> {
        large.iter().fold(HashSet::new(), |mut acc, rit| {
            for lit in large.iter() {
                for i1 in rit.items.difference(&lit.items).cloned() {
                    let mut candidate = lit.items.clone();
                    candidate.insert(i1);

                    if candidate.iter().fold(true, |acc, elem| {
                        let subset = candidate.difference(&vec![*elem].into_iter().collect::<HashSet<u64>>()).cloned().collect();
                        acc && large.contains(&ItemSet { items: subset, ..Default::default() })
                    }) {
                        acc.insert(ItemSet { items: candidate, ..Default::default() });
                    }
                }
            }

            acc
        })
    }

    pub fn apriori(sets: Vec<Vec<u64>>, min_support: f64) -> HashSet<ItemSet> {
        let (mut large, sets_count) = first_pass(&sets, min_support);
        let mut output = HashSet::<ItemSet>::new();

        while !large.is_empty() {
            let mut candidates = generate_subsets(large);

            for set in sets.iter() {
                let hash_set = HashSet::from_iter(set.iter().cloned());

                candidates = candidates.into_iter()
                    .map(|mut x| {
                        if x.items.is_subset(&hash_set) {
                            x.count += 1;
                            x.support = x.count as f64 / sets_count as f64;
                        }

                        x
                    })
                    .collect();
            }

            large =
                HashSet::from_iter(candidates.iter().cloned().filter(|x| x.support > min_support));

            output.extend(large.clone());
        }

        output
    }
}
#[cfg(test)]

mod tests {
    use recommendation::*;

    #[test]
    fn it_works() {
        let sets = vec![vec![1, 2, 3, 4],
                        vec![1, 2, 4],
                        vec![1, 2],
                        vec![2, 3, 4],
                        vec![2, 3],
                        vec![3, 4],
                        vec![2, 4]];

        let output = apriori(sets, 0.42);

        assert_eq!(output.len(), 4);

        let set1output =
            output.get(&ItemSet { items: vec![4, 2].into_iter().collect(), ..Default::default() });
        let set2output =
            output.get(&ItemSet { items: vec![3, 2].into_iter().collect(), ..Default::default() });
        let set3output =
            output.get(&ItemSet { items: vec![1, 2].into_iter().collect(), ..Default::default() });
        let set4output =
            output.get(&ItemSet { items: vec![3, 4].into_iter().collect(), ..Default::default() });

        assert!(set1output.is_some());
        assert!(set2output.is_some());
        assert!(set3output.is_some());
        assert!(set4output.is_some());

        println!("Output: {:?}", output);
    }
}
<|endoftext|>