<commit_before>//! Unix-specific extensions to primitives in the `std::process` module.

#![stable(feature = "rust1", since = "1.0.0")]

use crate::ffi::OsStr;
use crate::io;
use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
use crate::process;
use crate::sys;
use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner};

/// Unix-specific extensions to the [`process::Command`] builder.
#[stable(feature = "rust1", since = "1.0.0")]
pub trait CommandExt {
    /// Sets the child process's user ID. This translates to a
    /// `setuid` call in the child process. Failure in the `setuid`
    /// call will cause the spawn to fail.
    #[stable(feature = "rust1", since = "1.0.0")]
    fn uid(
        &mut self,
        #[cfg(not(target_os = "vxworks"))] id: u32,
        #[cfg(target_os = "vxworks")] id: u16,
    ) -> &mut process::Command;

    /// Similar to `uid`, but sets the group ID of the child process. This has
    /// the same semantics as the `uid` field.
    #[stable(feature = "rust1", since = "1.0.0")]
    fn gid(
        &mut self,
        #[cfg(not(target_os = "vxworks"))] id: u32,
        #[cfg(target_os = "vxworks")] id: u16,
    ) -> &mut process::Command;

    /// Schedules a closure to be run just before the `exec` function is
    /// invoked.
    ///
    /// The closure is allowed to return an I/O error whose OS error code will
    /// be communicated back to the parent and returned as an error from when
    /// the spawn was requested.
    ///
    /// Multiple closures can be registered and they will be called in order of
    /// their registration. If a closure returns `Err` then no further closures
    /// will be called and the spawn operation will immediately return with a
    /// failure.
    ///
    /// # Notes and Safety
    ///
    /// This closure will be run in the context of the child process after a
    /// `fork`. This primarily means that any modifications made to memory on
    /// behalf of this closure will **not** be visible to the parent process.
    /// This is often a very constrained environment where normal operations
    /// like `malloc` or acquiring a mutex are not guaranteed to work (due to
    /// other threads perhaps still running when the `fork` was run).
    ///
    /// This also means that all resources such as file descriptors and
    /// memory-mapped regions got duplicated. It is your responsibility to make
    /// sure that the closure does not violate library invariants by making
    /// invalid use of these duplicates.
    ///
    /// When this closure is run, aspects such as the stdio file descriptors and
    /// working directory have successfully been changed, so output to these
    /// locations may not appear where intended.
    #[stable(feature = "process_pre_exec", since = "1.34.0")]
    unsafe fn pre_exec<F>(&mut self, f: F) -> &mut process::Command
    where
        F: FnMut() -> io::Result<()> + Send + Sync + 'static;

    /// Schedules a closure to be run just before the `exec` function is
    /// invoked.
    ///
    /// This method is stable and usable, but it should be unsafe. To fix
    /// that, it got deprecated in favor of the unsafe [`pre_exec`].
    ///
    /// [`pre_exec`]: CommandExt::pre_exec
    #[stable(feature = "process_exec", since = "1.15.0")]
    #[rustc_deprecated(since = "1.37.0", reason = "should be unsafe, use `pre_exec` instead")]
    fn before_exec<F>(&mut self, f: F) -> &mut process::Command
    where
        F: FnMut() -> io::Result<()> + Send + Sync + 'static,
    {
        unsafe { self.pre_exec(f) }
    }

    /// Performs all the required setup by this `Command`, followed by calling
    /// the `execvp` syscall.
    ///
    /// On success this function will not return, and otherwise it will return
    /// an error indicating why the exec (or another part of the setup of the
    /// `Command`) failed.
    ///
    /// `exec` not returning has the same implications as calling
    /// [`process::exit`] – no destructors on the current stack or any other
    /// thread’s stack will be run. Therefore, it is recommended to only call
    /// `exec` at a point where it is fine to not run any destructors. Note,
    /// that the `execvp` syscall independently guarantees that all memory is
    /// freed and all file descriptors with the `CLOEXEC` option (set by default
    /// on all file descriptors opened by the standard library) are closed.
    ///
    /// This function, unlike `spawn`, will **not** `fork` the process to create
    /// a new child. Like spawn, however, the default behavior for the stdio
    /// descriptors will be to inherited from the current process.
    ///
    /// # Notes
    ///
    /// The process may be in a "broken state" if this function returns in
    /// error. For example the working directory, environment variables, signal
    /// handling settings, various user/group information, or aspects of stdio
    /// file descriptors may have changed. If a "transactional spawn" is
    /// required to gracefully handle errors it is recommended to use the
    /// cross-platform `spawn` instead.
    #[stable(feature = "process_exec2", since = "1.9.0")]
    fn exec(&mut self) -> io::Error;

    /// Set executable argument
    ///
    /// Set the first process argument, `argv[0]`, to something other than the
    /// default executable path.
    #[stable(feature = "process_set_argv0", since = "1.45.0")]
    fn arg0<S>(&mut self, arg: S) -> &mut process::Command
    where
        S: AsRef<OsStr>;
}

#[stable(feature = "rust1", since = "1.0.0")]
impl CommandExt for process::Command {
    fn uid(
        &mut self,
        #[cfg(not(target_os = "vxworks"))] id: u32,
        #[cfg(target_os = "vxworks")] id: u16,
    ) -> &mut process::Command {
        self.as_inner_mut().uid(id);
        self
    }

    fn gid(
        &mut self,
        #[cfg(not(target_os = "vxworks"))] id: u32,
        #[cfg(target_os = "vxworks")] id: u16,
    ) -> &mut process::Command {
        self.as_inner_mut().gid(id);
        self
    }

    unsafe fn pre_exec<F>(&mut self, f: F) -> &mut process::Command
    where
        F: FnMut() -> io::Result<()> + Send + Sync + 'static,
    {
        self.as_inner_mut().pre_exec(Box::new(f));
        self
    }

    fn exec(&mut self) -> io::Error {
        self.as_inner_mut().exec(sys::process::Stdio::Inherit)
    }

    fn arg0<S>(&mut self, arg: S) -> &mut process::Command
    where
        S: AsRef<OsStr>,
    {
        self.as_inner_mut().set_arg_0(arg.as_ref());
        self
    }
}

/// Unix-specific extensions to [`process::ExitStatus`].
#[stable(feature = "rust1", since = "1.0.0")]
pub trait ExitStatusExt {
    /// Creates a new `ExitStatus` from the raw underlying `i32` return value of
    /// a process.
    #[stable(feature = "exit_status_from", since = "1.12.0")]
    fn from_raw(raw: i32) -> Self;

    /// If the process was terminated by a signal, returns that signal.
    ///
    /// Ie, if `WIFSIGNALED`, this returns `WTERMSIG`.
    #[stable(feature = "rust1", since = "1.0.0")]
    fn signal(&self) -> Option<i32>;

    /// If the process was terminated by a signal, says whether it dumped core.
    #[unstable(feature = "unix_process_wait_more", issue = "none")]
    fn core_dumped(&self) -> bool;

    /// If the process was stopped by a signal, returns that signal.
    ///
    /// Ie, if `WIFSTOPPED`, this returns `WSTOPSIG`.  This is only possible if the status came from
    /// a `wait` system call which was passed `WUNTRACED`, was then converted into an `ExitStatus`.
    #[unstable(feature = "unix_process_wait_more", issue = "none")]
    fn stopped_signal(&self) -> Option<i32>;

    /// Whether the process was continued from a stopped status.
    ///
    /// Ie, `WIFCONTINUED`.  This is only possible if the status came from a `wait` system call
    /// which was passed `WCONTINUED`, was then converted into an `ExitStatus`.
    #[unstable(feature = "unix_process_wait_more", issue = "none")]
    fn continued(&self) -> bool;

    /// Returns the underlying raw `wait` status.
    #[unstable(feature = "unix_process_wait_more", issue = "none")]
    fn into_raw(self) -> i32;
}

#[stable(feature = "rust1", since = "1.0.0")]
impl ExitStatusExt for process::ExitStatus {
    fn from_raw(raw: i32) -> Self {
        process::ExitStatus::from_inner(From::from(raw))
    }

    fn signal(&self) -> Option<i32> {
        self.as_inner().signal()
    }

    fn core_dumped(&self) -> bool {
        self.as_inner().core_dumped()
    }

    fn stopped_signal(&self) -> Option<i32> {
        self.as_inner().stopped_signal()
    }

    fn continued(&self) -> bool {
        self.as_inner().continued()
    }

    fn into_raw(self) -> i32 {
        self.as_inner().into_raw().into()
    }
}

#[stable(feature = "process_extensions", since = "1.2.0")]
impl FromRawFd for process::Stdio {
    unsafe fn from_raw_fd(fd: RawFd) -> process::Stdio {
        let fd = sys::fd::FileDesc::new(fd);
        let io = sys::process::Stdio::Fd(fd);
        process::Stdio::from_inner(io)
    }
}

#[stable(feature = "process_extensions", since = "1.2.0")]
impl AsRawFd for process::ChildStdin {
    fn as_raw_fd(&self) -> RawFd {
        self.as_inner().fd().raw()
    }
}

#[stable(feature = "process_extensions", since = "1.2.0")]
impl AsRawFd for process::ChildStdout {
    fn as_raw_fd(&self) -> RawFd {
        self.as_inner().fd().raw()
    }
}

#[stable(feature = "process_extensions", since = "1.2.0")]
impl AsRawFd for process::ChildStderr {
    fn as_raw_fd(&self) -> RawFd {
        self.as_inner().fd().raw()
    }
}

#[stable(feature = "into_raw_os", since = "1.4.0")]
impl IntoRawFd for process::ChildStdin {
    fn into_raw_fd(self) -> RawFd {
        self.into_inner().into_fd().into_raw()
    }
}

#[stable(feature = "into_raw_os", since = "1.4.0")]
impl IntoRawFd for process::ChildStdout {
    fn into_raw_fd(self) -> RawFd {
        self.into_inner().into_fd().into_raw()
    }
}

#[stable(feature = "into_raw_os", since = "1.4.0")]
impl IntoRawFd for process::ChildStderr {
    fn into_raw_fd(self) -> RawFd {
        self.into_inner().into_fd().into_raw()
    }
}

/// Returns the OS-assigned process identifier associated with this process's parent.
#[stable(feature = "unix_ppid", since = "1.27.0")]
pub fn parent_id() -> u32 {
    crate::sys::os::getppid()
}
<commit_msg>Replace `Ie` with `In other words`<commit_after>//! Unix-specific extensions to primitives in the `std::process` module.

#![stable(feature = "rust1", since = "1.0.0")]

use crate::ffi::OsStr;
use crate::io;
use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
use crate::process;
use crate::sys;
use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner};

/// Unix-specific extensions to the [`process::Command`] builder.
#[stable(feature = "rust1", since = "1.0.0")]
pub trait CommandExt {
    /// Sets the child process's user ID. This translates to a
    /// `setuid` call in the child process. Failure in the `setuid`
    /// call will cause the spawn to fail.
    #[stable(feature = "rust1", since = "1.0.0")]
    fn uid(
        &mut self,
        #[cfg(not(target_os = "vxworks"))] id: u32,
        #[cfg(target_os = "vxworks")] id: u16,
    ) -> &mut process::Command;

    /// Similar to `uid`, but sets the group ID of the child process. This has
    /// the same semantics as the `uid` field.
    #[stable(feature = "rust1", since = "1.0.0")]
    fn gid(
        &mut self,
        #[cfg(not(target_os = "vxworks"))] id: u32,
        #[cfg(target_os = "vxworks")] id: u16,
    ) -> &mut process::Command;

    /// Schedules a closure to be run just before the `exec` function is
    /// invoked.
    ///
    /// The closure is allowed to return an I/O error whose OS error code will
    /// be communicated back to the parent and returned as an error from when
    /// the spawn was requested.
    ///
    /// Multiple closures can be registered and they will be called in order of
    /// their registration. If a closure returns `Err` then no further closures
    /// will be called and the spawn operation will immediately return with a
    /// failure.
    ///
    /// # Notes and Safety
    ///
    /// This closure will be run in the context of the child process after a
    /// `fork`. This primarily means that any modifications made to memory on
    /// behalf of this closure will **not** be visible to the parent process.
    /// This is often a very constrained environment where normal operations
    /// like `malloc` or acquiring a mutex are not guaranteed to work (due to
    /// other threads perhaps still running when the `fork` was run).
    ///
    /// This also means that all resources such as file descriptors and
    /// memory-mapped regions got duplicated. It is your responsibility to make
    /// sure that the closure does not violate library invariants by making
    /// invalid use of these duplicates.
    ///
    /// When this closure is run, aspects such as the stdio file descriptors and
    /// working directory have successfully been changed, so output to these
    /// locations may not appear where intended.
    #[stable(feature = "process_pre_exec", since = "1.34.0")]
    unsafe fn pre_exec<F>(&mut self, f: F) -> &mut process::Command
    where
        F: FnMut() -> io::Result<()> + Send + Sync + 'static;

    /// Schedules a closure to be run just before the `exec` function is
    /// invoked.
    ///
    /// This method is stable and usable, but it should be unsafe. To fix
    /// that, it got deprecated in favor of the unsafe [`pre_exec`].
    ///
    /// [`pre_exec`]: CommandExt::pre_exec
    #[stable(feature = "process_exec", since = "1.15.0")]
    #[rustc_deprecated(since = "1.37.0", reason = "should be unsafe, use `pre_exec` instead")]
    fn before_exec<F>(&mut self, f: F) -> &mut process::Command
    where
        F: FnMut() -> io::Result<()> + Send + Sync + 'static,
    {
        unsafe { self.pre_exec(f) }
    }

    /// Performs all the required setup by this `Command`, followed by calling
    /// the `execvp` syscall.
    ///
    /// On success this function will not return, and otherwise it will return
    /// an error indicating why the exec (or another part of the setup of the
    /// `Command`) failed.
    ///
    /// `exec` not returning has the same implications as calling
    /// [`process::exit`] – no destructors on the current stack or any other
    /// thread’s stack will be run. Therefore, it is recommended to only call
    /// `exec` at a point where it is fine to not run any destructors. Note,
    /// that the `execvp` syscall independently guarantees that all memory is
    /// freed and all file descriptors with the `CLOEXEC` option (set by default
    /// on all file descriptors opened by the standard library) are closed.
    ///
    /// This function, unlike `spawn`, will **not** `fork` the process to create
    /// a new child. Like spawn, however, the default behavior for the stdio
    /// descriptors will be to inherited from the current process.
    ///
    /// # Notes
    ///
    /// The process may be in a "broken state" if this function returns in
    /// error. For example the working directory, environment variables, signal
    /// handling settings, various user/group information, or aspects of stdio
    /// file descriptors may have changed. If a "transactional spawn" is
    /// required to gracefully handle errors it is recommended to use the
    /// cross-platform `spawn` instead.
    #[stable(feature = "process_exec2", since = "1.9.0")]
    fn exec(&mut self) -> io::Error;

    /// Set executable argument
    ///
    /// Set the first process argument, `argv[0]`, to something other than the
    /// default executable path.
    #[stable(feature = "process_set_argv0", since = "1.45.0")]
    fn arg0<S>(&mut self, arg: S) -> &mut process::Command
    where
        S: AsRef<OsStr>;
}

#[stable(feature = "rust1", since = "1.0.0")]
impl CommandExt for process::Command {
    fn uid(
        &mut self,
        #[cfg(not(target_os = "vxworks"))] id: u32,
        #[cfg(target_os = "vxworks")] id: u16,
    ) -> &mut process::Command {
        self.as_inner_mut().uid(id);
        self
    }

    fn gid(
        &mut self,
        #[cfg(not(target_os = "vxworks"))] id: u32,
        #[cfg(target_os = "vxworks")] id: u16,
    ) -> &mut process::Command {
        self.as_inner_mut().gid(id);
        self
    }

    unsafe fn pre_exec<F>(&mut self, f: F) -> &mut process::Command
    where
        F: FnMut() -> io::Result<()> + Send + Sync + 'static,
    {
        self.as_inner_mut().pre_exec(Box::new(f));
        self
    }

    fn exec(&mut self) -> io::Error {
        self.as_inner_mut().exec(sys::process::Stdio::Inherit)
    }

    fn arg0<S>(&mut self, arg: S) -> &mut process::Command
    where
        S: AsRef<OsStr>,
    {
        self.as_inner_mut().set_arg_0(arg.as_ref());
        self
    }
}

/// Unix-specific extensions to [`process::ExitStatus`].
#[stable(feature = "rust1", since = "1.0.0")]
pub trait ExitStatusExt {
    /// Creates a new `ExitStatus` from the raw underlying `i32` return value of
    /// a process.
    #[stable(feature = "exit_status_from", since = "1.12.0")]
    fn from_raw(raw: i32) -> Self;

    /// If the process was terminated by a signal, returns that signal.
    ///
    /// Ie, if `WIFSIGNALED`, this returns `WTERMSIG`.
    #[stable(feature = "rust1", since = "1.0.0")]
    fn signal(&self) -> Option<i32>;

    /// If the process was terminated by a signal, says whether it dumped core.
    #[unstable(feature = "unix_process_wait_more", issue = "none")]
    fn core_dumped(&self) -> bool;

    /// If the process was stopped by a signal, returns that signal.
    ///
    /// In other words, if `WIFSTOPPED`, this returns `WSTOPSIG`.  This is only possible if the status came from
    /// a `wait` system call which was passed `WUNTRACED`, was then converted into an `ExitStatus`.
    #[unstable(feature = "unix_process_wait_more", issue = "none")]
    fn stopped_signal(&self) -> Option<i32>;

    /// Whether the process was continued from a stopped status.
    ///
    /// Ie, `WIFCONTINUED`.  This is only possible if the status came from a `wait` system call
    /// which was passed `WCONTINUED`, was then converted into an `ExitStatus`.
    #[unstable(feature = "unix_process_wait_more", issue = "none")]
    fn continued(&self) -> bool;

    /// Returns the underlying raw `wait` status.
    #[unstable(feature = "unix_process_wait_more", issue = "none")]
    fn into_raw(self) -> i32;
}

#[stable(feature = "rust1", since = "1.0.0")]
impl ExitStatusExt for process::ExitStatus {
    fn from_raw(raw: i32) -> Self {
        process::ExitStatus::from_inner(From::from(raw))
    }

    fn signal(&self) -> Option<i32> {
        self.as_inner().signal()
    }

    fn core_dumped(&self) -> bool {
        self.as_inner().core_dumped()
    }

    fn stopped_signal(&self) -> Option<i32> {
        self.as_inner().stopped_signal()
    }

    fn continued(&self) -> bool {
        self.as_inner().continued()
    }

    fn into_raw(self) -> i32 {
        self.as_inner().into_raw().into()
    }
}

#[stable(feature = "process_extensions", since = "1.2.0")]
impl FromRawFd for process::Stdio {
    unsafe fn from_raw_fd(fd: RawFd) -> process::Stdio {
        let fd = sys::fd::FileDesc::new(fd);
        let io = sys::process::Stdio::Fd(fd);
        process::Stdio::from_inner(io)
    }
}

#[stable(feature = "process_extensions", since = "1.2.0")]
impl AsRawFd for process::ChildStdin {
    fn as_raw_fd(&self) -> RawFd {
        self.as_inner().fd().raw()
    }
}

#[stable(feature = "process_extensions", since = "1.2.0")]
impl AsRawFd for process::ChildStdout {
    fn as_raw_fd(&self) -> RawFd {
        self.as_inner().fd().raw()
    }
}

#[stable(feature = "process_extensions", since = "1.2.0")]
impl AsRawFd for process::ChildStderr {
    fn as_raw_fd(&self) -> RawFd {
        self.as_inner().fd().raw()
    }
}

#[stable(feature = "into_raw_os", since = "1.4.0")]
impl IntoRawFd for process::ChildStdin {
    fn into_raw_fd(self) -> RawFd {
        self.into_inner().into_fd().into_raw()
    }
}

#[stable(feature = "into_raw_os", since = "1.4.0")]
impl IntoRawFd for process::ChildStdout {
    fn into_raw_fd(self) -> RawFd {
        self.into_inner().into_fd().into_raw()
    }
}

#[stable(feature = "into_raw_os", since = "1.4.0")]
impl IntoRawFd for process::ChildStderr {
    fn into_raw_fd(self) -> RawFd {
        self.into_inner().into_fd().into_raw()
    }
}

/// Returns the OS-assigned process identifier associated with this process's parent.
#[stable(feature = "unix_ppid", since = "1.27.0")]
pub fn parent_id() -> u32 {
    crate::sys::os::getppid()
}
<|endoftext|>


<commit_before>use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
use std::time::{SystemTime, UNIX_EPOCH};

use domain::wallet::Tags;
use errors::prelude::*;
use services::wallet::{WalletService, WalletRecord};
use api::{WalletHandle, PoolHandle, CommandHandle};
use commands::{Command, CommandExecutor};
use commands::ledger::LedgerCommand;

use api::next_command_handle;

const CRED_DEF_CACHE: &str = "cred_def_cache";
const SCHEMA_CACHE: &str = "schema_cache";

pub enum CacheCommand {
    GetSchema(PoolHandle,
              WalletHandle,
              String, // submitter_did
              String, // id
              String, // options_json
              Box<Fn(IndyResult<String>) + Send>),
    GetSchemaContinue(
        WalletHandle,
        IndyResult<(String, String)>, // ledger_response
        GetCacheOptions,              // options
        CommandHandle,                          // cb_id
    ),
    GetCredDef(PoolHandle,
               WalletHandle,
               String, // submitter_did
               String, // id
               String, // options_json
               Box<Fn(IndyResult<String>) + Send>),
    GetCredDefContinue(
        WalletHandle,
        IndyResult<(String, String)>, // ledger_response
        GetCacheOptions,              // options
        CommandHandle,                          // cb_id
    ),
    PurgeSchemaCache(WalletHandle,
                     String, // options json
                     Box<Fn(IndyResult<()>) + Send>),
    PurgeCredDefCache(WalletHandle,
                      String, // options json
                      Box<Fn(IndyResult<()>) + Send>),
}

pub struct CacheCommandExecutor {
    wallet_service: Rc<WalletService>,

    pending_callbacks: RefCell<HashMap<CommandHandle, Box<Fn(IndyResult<String>)>>>,
}

macro_rules! check_cache {
    ($cache: ident, $options: ident, $cb: ident) => {
    if let Some(cache) = $cache {
            let min_fresh = $options.min_fresh.unwrap_or(-1);
            if min_fresh >= 0 {
                let ts = match SystemTime::now().duration_since(UNIX_EPOCH) {
                    Ok(ts) => ts.as_secs() as i32,
                    Err(err) => {
                        error!("Cannot get time: {:?}", err);
                        return $cb(Err(IndyError::from_msg(IndyErrorKind::InvalidState, format!("Cannot get time: {:?}", err))))
                    }
                };
                if ts - min_fresh <= cache.get_tags().unwrap_or(&Tags::new()).get("timestamp").unwrap_or(&"-1".to_string()).parse().unwrap_or(-1) {
                    return $cb(Ok(cache.get_value().unwrap_or("").to_string()))
                }
            } else {
                return $cb(Ok(cache.get_value().unwrap_or("").to_string()))
            }
        }
    };
}

impl CacheCommandExecutor {
    pub fn new(wallet_service: Rc<WalletService>) -> CacheCommandExecutor {
        CacheCommandExecutor {
            wallet_service,
            pending_callbacks: RefCell::new(HashMap::new()),
        }
    }

    pub fn execute(&self, command: CacheCommand) {
        match command {
            CacheCommand::GetSchema(pool_handle, wallet_handle, submitter_did, id, options_json, cb) => {
                info!(target: "non_secrets_command_executor", "GetSchema command received");
                self.get_schema(pool_handle, wallet_handle, &submitter_did, &id, &options_json, cb);
            }
            CacheCommand::GetSchemaContinue(wallet_handle, ledger_response, options, cb_id) => {
                info!(target: "non_secrets_command_executor", "GetSchemaContinue command received");
                self._get_schema_continue(wallet_handle, ledger_response, options, cb_id);
            }
            CacheCommand::GetCredDef(pool_handle, wallet_handle, submitter_did, id, options_json, cb) => {
                info!(target: "non_secrets_command_executor", "GetCredDef command received");
                self.get_cred_def(pool_handle, wallet_handle, &submitter_did, &id, &options_json, cb);
            }
            CacheCommand::GetCredDefContinue(wallet_handle, ledger_response, options, cb_id) => {
                info!(target: "non_secrets_command_executor", "GetCredDefContinue command received");
                self._get_cred_def_continue(wallet_handle, ledger_response, options, cb_id);
            }
            CacheCommand::PurgeSchemaCache(wallet_handle, options_json, cb) => {
                info!(target: "non_secrets_command_executor", "PurgeSchemaCache command received");
                cb(self.purge_schema_cache(wallet_handle, &options_json));
            }
            CacheCommand::PurgeCredDefCache(wallet_handle, options_json, cb) => {
                info!(target: "non_secrets_command_executor", "PurgeCredDefCache command received");
                cb(self.purge_cred_def_cache(wallet_handle, &options_json));
            }
        }
    }

    fn get_schema(&self,
                  pool_handle: PoolHandle,
                  wallet_handle: WalletHandle,
                  submitter_did: &str,
                  id: &str,
                  options_json: &str,
                  cb: Box<Fn(IndyResult<String>) + Send>) {
        trace!("get_schema >>> pool_handle: {:?}, wallet_handle: {:?}, submitter_did: {:?}, id: {:?}, options_json: {:?}",
               pool_handle, wallet_handle, submitter_did, id, options_json);

        let options = try_cb!(serde_json::from_str::<GetCacheOptions>(options_json).to_indy(IndyErrorKind::InvalidStructure, "Cannot deserialize options"), cb);

        let cache = self.get_record_from_cache(wallet_handle, id, &options, SCHEMA_CACHE);
        let cache = try_cb!(cache, cb);

        check_cache!(cache, options, cb);

        if options.no_update.unwrap_or(false) {
            return cb(Err(IndyError::from(IndyErrorKind::LedgerItemNotFound)));
        }

        let cb_id = next_command_handle();
        self.pending_callbacks.borrow_mut().insert(cb_id, cb);

        CommandExecutor::instance().send(
            Command::Ledger(
                LedgerCommand::GetSchema(
                    pool_handle,
                    Some(submitter_did.to_string()),
                    id.to_string(),
                    Box::new(move |ledger_response| {
                        CommandExecutor::instance().send(
                            Command::Cache(
                                CacheCommand::GetSchemaContinue(
                                    wallet_handle,
                                    ledger_response,
                                    options.clone(),
                                    cb_id,
                                )
                            )
                        ).unwrap();
                    })
                )
            )
        ).unwrap();
    }

    fn _delete_and_add_record(&self,
                              wallet_handle: WalletHandle,
                              options: GetCacheOptions,
                              schema_id: &str,
                              schema_json: &str,
                              which_cache: &str) -> IndyResult<()>
    {
        if !options.no_store.unwrap_or(false) {
            let mut tags = Tags::new();
            let ts = match SystemTime::now().duration_since(UNIX_EPOCH) {
                Ok(ts) => ts.as_secs() as i32,
                Err(err) => {
                    warn!("Cannot get time: {:?}", err);
                    0
                }
            };
            tags.insert("timestamp".to_string(), ts.to_string());
            self.wallet_service.delete_record(wallet_handle, which_cache, &schema_id)?;
            self.wallet_service.add_record(wallet_handle, which_cache, &schema_id, &schema_json, &tags)?
        }
        Ok(())
    }

    fn _get_schema_continue(&self,
                            wallet_handle: WalletHandle,
                            ledger_response: IndyResult<(String, String)>,
                            options: GetCacheOptions, cb_id: CommandHandle) {
        let cb = self.pending_callbacks.borrow_mut().remove(&cb_id).expect("FIXME INVALID STATE");

        let (schema_id, schema_json) = try_cb!(ledger_response, cb);

        match self._delete_and_add_record(wallet_handle, options, &schema_id, &schema_json, SCHEMA_CACHE) {
            Ok(_) => cb(Ok(schema_json)),
            Err(err) => cb(Err(IndyError::from_msg(IndyErrorKind::InvalidState, format!("get_schema_continue failed: {:?}", err))))
        }
    }

    fn get_cred_def(&self,
                    pool_handle: PoolHandle,
                    wallet_handle: WalletHandle,
                    submitter_did: &str,
                    id: &str,
                    options_json: &str,
                    cb: Box<Fn(IndyResult<String>) + Send>) {
        trace!("get_cred_def >>> pool_handle: {:?}, wallet_handle: {:?}, submitter_did: {:?}, id: {:?}, options_json: {:?}",
               pool_handle, wallet_handle, submitter_did, id, options_json);

        let options = try_cb!(serde_json::from_str::<GetCacheOptions>(options_json).to_indy(IndyErrorKind::InvalidStructure, "Cannot deserialize options"), cb);

        let cache = self.get_record_from_cache(wallet_handle, id, &options, CRED_DEF_CACHE);
        let cache = try_cb!(cache, cb);

        check_cache!(cache, options, cb);

        if options.no_update.unwrap_or(false) {
            return cb(Err(IndyError::from(IndyErrorKind::LedgerItemNotFound)));
        }

        let cb_id = next_command_handle();
        self.pending_callbacks.borrow_mut().insert(cb_id, cb);

        CommandExecutor::instance().send(
            Command::Ledger(
                LedgerCommand::GetCredDef(
                    pool_handle,
                    Some(submitter_did.to_string()),
                    id.to_string(),
                    Box::new(move |ledger_response| {
                        CommandExecutor::instance().send(
                            Command::Cache(
                                CacheCommand::GetCredDefContinue(
                                    wallet_handle,
                                    ledger_response,
                                    options.clone(),
                                    cb_id,
                                )
                            )
                        ).unwrap();
                    })
                )
            )
        ).unwrap();
    }

    fn get_record_from_cache(&self, wallet_handle: WalletHandle, id: &str, options: &GetCacheOptions, which_cache: &str) -> Result<Option<WalletRecord>, IndyError> {
        if !options.no_cache.unwrap_or(false) {
            let options_json = json!({
                "retrieveType": false,
                "retrieveValue": true,
                "retrieveTags": true,
            }).to_string();
            match self.wallet_service.get_record(wallet_handle, which_cache, id, &options_json) {
                Ok(record) => Ok(Some(record)),
                Err(err) => if err.kind() == IndyErrorKind::WalletItemNotFound { Ok(None) } else { Err(err) }
            }
        } else { Ok(None) }
    }

    fn _get_cred_def_continue(&self, wallet_handle: WalletHandle, ledger_response: IndyResult<(String, String)>, options: GetCacheOptions, cb_id: CommandHandle) {
        let cb = self.pending_callbacks.borrow_mut().remove(&cb_id).expect("FIXME INVALID STATE");

        let (cred_def_id, cred_def_json) = try_cb!(ledger_response, cb);

        match self._delete_and_add_record(wallet_handle, options, &cred_def_id, &cred_def_json, CRED_DEF_CACHE) {
            Ok(_) => cb(Ok(cred_def_json)),
            Err(err) => cb(Err(IndyError::from_msg(IndyErrorKind::InvalidState, format!("get_cred_def_continue failed: {:?}", err))))
        }
    }

    fn purge_schema_cache(&self,
                          wallet_handle: WalletHandle,
                          options_json: &str) -> IndyResult<()> {
        trace!("purge_schema_cache >>> wallet_handle: {:?}, options_json: {:?}", wallet_handle, options_json);

        let options = serde_json::from_str::<PurgeOptions>(options_json)
            .to_indy(IndyErrorKind::InvalidStructure, "Cannot deserialize options")?;

        let max_age = options.max_age.unwrap_or(-1);
        let query_json = if max_age >= 0 {
            let ts = match SystemTime::now().duration_since(UNIX_EPOCH) {
                Ok(ts) => ts.as_secs() as i32,
                Err(err) => {
                    error!("Cannot get time: {:?}", err);
                    return Err(IndyError::from_msg(IndyErrorKind::InvalidState, format!("Cannot get time: {:?}", err)))
                }
            };
            json!({"timestamp": {"$lt": ts - max_age}}).to_string()
        } else {
            "{}".to_string()
        };

        let options_json = json!({
            "retrieveType": false,
            "retrieveValue": false,
            "retrieveTags": false,
        }).to_string();

        let mut search = self.wallet_service.search_records(
            wallet_handle,
            SCHEMA_CACHE,
            &query_json,
            &options_json,
        )?;

        while let Some(record) = search.fetch_next_record()? {
            self.wallet_service.delete_record(wallet_handle, SCHEMA_CACHE, record.get_id())?;
        }

        trace!("purge_schema_cache <<< res: ()");

        Ok(())
    }

    fn purge_cred_def_cache(&self,
                            wallet_handle: WalletHandle,
                            options_json: &str) -> IndyResult<()> {
        trace!("purge_cred_def_cache >>> wallet_handle: {:?}, options_json: {:?}", wallet_handle, options_json);

        let options = serde_json::from_str::<PurgeOptions>(options_json)
            .to_indy(IndyErrorKind::InvalidStructure, "Cannot deserialize options")?;

        let max_age = options.max_age.unwrap_or(-1);
        let query_json = if max_age >= 0 {
            let ts = match SystemTime::now().duration_since(UNIX_EPOCH) {
                Ok(ts) => ts.as_secs() as i32,
                Err(err) => {
                    error!("Cannot get time: {:?}", err);
                    return Err(IndyError::from_msg(IndyErrorKind::InvalidState, format!("Cannot get time: {:?}", err)))
                }
            };
            json!({"timestamp": {"$lt": ts - max_age}}).to_string()
        } else {
            "{}".to_string()
        };

        let options_json = json!({
            "retrieveType": false,
            "retrieveValue": false,
            "retrieveTags": false,
        }).to_string();

        let mut search = self.wallet_service.search_records(
            wallet_handle,
            CRED_DEF_CACHE,
            &query_json,
            &options_json,
        )?;

        while let Some(record) = search.fetch_next_record()? {
            self.wallet_service.delete_record(wallet_handle, CRED_DEF_CACHE, record.get_id())?;
        }

        trace!("purge_cred_def_cache <<< res: ()");

        Ok(())
    }
}

#[serde(rename_all = "camelCase")]
#[derive(Debug, Deserialize, Serialize)]
struct PurgeOptions {
    pub max_age: Option<i32>,
}

#[serde(rename_all = "camelCase")]
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct GetCacheOptions {
    pub no_cache: Option<bool>,     // Skip usage of cache,
    pub no_update: Option<bool>,    // Use only cached data, do not try to update.
    pub no_store: Option<bool>,     // Skip storing fresh data if updated
    pub min_fresh: Option<i32>,     // Return cached data if not older than this many seconds. -1 means do not check age.
}
<commit_msg>remove duplicate code build_query_json<commit_after>use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
use std::time::{SystemTime, UNIX_EPOCH};

use domain::wallet::Tags;
use errors::prelude::*;
use services::wallet::{WalletService, WalletRecord};
use api::{WalletHandle, PoolHandle, CommandHandle};
use commands::{Command, CommandExecutor};
use commands::ledger::LedgerCommand;

use api::next_command_handle;

const CRED_DEF_CACHE: &str = "cred_def_cache";
const SCHEMA_CACHE: &str = "schema_cache";

pub enum CacheCommand {
    GetSchema(PoolHandle,
              WalletHandle,
              String, // submitter_did
              String, // id
              String, // options_json
              Box<Fn(IndyResult<String>) + Send>),
    GetSchemaContinue(
        WalletHandle,
        IndyResult<(String, String)>, // ledger_response
        GetCacheOptions,              // options
        CommandHandle,                          // cb_id
    ),
    GetCredDef(PoolHandle,
               WalletHandle,
               String, // submitter_did
               String, // id
               String, // options_json
               Box<Fn(IndyResult<String>) + Send>),
    GetCredDefContinue(
        WalletHandle,
        IndyResult<(String, String)>, // ledger_response
        GetCacheOptions,              // options
        CommandHandle,                          // cb_id
    ),
    PurgeSchemaCache(WalletHandle,
                     String, // options json
                     Box<Fn(IndyResult<()>) + Send>),
    PurgeCredDefCache(WalletHandle,
                      String, // options json
                      Box<Fn(IndyResult<()>) + Send>),
}

pub struct CacheCommandExecutor {
    wallet_service: Rc<WalletService>,

    pending_callbacks: RefCell<HashMap<CommandHandle, Box<Fn(IndyResult<String>)>>>,
}

macro_rules! check_cache {
    ($cache: ident, $options: ident, $cb: ident) => {
    if let Some(cache) = $cache {
            let min_fresh = $options.min_fresh.unwrap_or(-1);
            if min_fresh >= 0 {
                let ts = match SystemTime::now().duration_since(UNIX_EPOCH) {
                    Ok(ts) => ts.as_secs() as i32,
                    Err(err) => {
                        error!("Cannot get time: {:?}", err);
                        return $cb(Err(IndyError::from_msg(IndyErrorKind::InvalidState, format!("Cannot get time: {:?}", err))))
                    }
                };
                if ts - min_fresh <= cache.get_tags().unwrap_or(&Tags::new()).get("timestamp").unwrap_or(&"-1".to_string()).parse().unwrap_or(-1) {
                    return $cb(Ok(cache.get_value().unwrap_or("").to_string()))
                }
            } else {
                return $cb(Ok(cache.get_value().unwrap_or("").to_string()))
            }
        }
    };
}

impl CacheCommandExecutor {
    pub fn new(wallet_service: Rc<WalletService>) -> CacheCommandExecutor {
        CacheCommandExecutor {
            wallet_service,
            pending_callbacks: RefCell::new(HashMap::new()),
        }
    }

    pub fn execute(&self, command: CacheCommand) {
        match command {
            CacheCommand::GetSchema(pool_handle, wallet_handle, submitter_did, id, options_json, cb) => {
                info!(target: "non_secrets_command_executor", "GetSchema command received");
                self.get_schema(pool_handle, wallet_handle, &submitter_did, &id, &options_json, cb);
            }
            CacheCommand::GetSchemaContinue(wallet_handle, ledger_response, options, cb_id) => {
                info!(target: "non_secrets_command_executor", "GetSchemaContinue command received");
                self._get_schema_continue(wallet_handle, ledger_response, options, cb_id);
            }
            CacheCommand::GetCredDef(pool_handle, wallet_handle, submitter_did, id, options_json, cb) => {
                info!(target: "non_secrets_command_executor", "GetCredDef command received");
                self.get_cred_def(pool_handle, wallet_handle, &submitter_did, &id, &options_json, cb);
            }
            CacheCommand::GetCredDefContinue(wallet_handle, ledger_response, options, cb_id) => {
                info!(target: "non_secrets_command_executor", "GetCredDefContinue command received");
                self._get_cred_def_continue(wallet_handle, ledger_response, options, cb_id);
            }
            CacheCommand::PurgeSchemaCache(wallet_handle, options_json, cb) => {
                info!(target: "non_secrets_command_executor", "PurgeSchemaCache command received");
                cb(self.purge_schema_cache(wallet_handle, &options_json));
            }
            CacheCommand::PurgeCredDefCache(wallet_handle, options_json, cb) => {
                info!(target: "non_secrets_command_executor", "PurgeCredDefCache command received");
                cb(self.purge_cred_def_cache(wallet_handle, &options_json));
            }
        }
    }

    fn get_schema(&self,
                  pool_handle: PoolHandle,
                  wallet_handle: WalletHandle,
                  submitter_did: &str,
                  id: &str,
                  options_json: &str,
                  cb: Box<Fn(IndyResult<String>) + Send>) {
        trace!("get_schema >>> pool_handle: {:?}, wallet_handle: {:?}, submitter_did: {:?}, id: {:?}, options_json: {:?}",
               pool_handle, wallet_handle, submitter_did, id, options_json);

        let options = try_cb!(serde_json::from_str::<GetCacheOptions>(options_json).to_indy(IndyErrorKind::InvalidStructure, "Cannot deserialize options"), cb);

        let cache = self.get_record_from_cache(wallet_handle, id, &options, SCHEMA_CACHE);
        let cache = try_cb!(cache, cb);

        check_cache!(cache, options, cb);

        if options.no_update.unwrap_or(false) {
            return cb(Err(IndyError::from(IndyErrorKind::LedgerItemNotFound)));
        }

        let cb_id = next_command_handle();
        self.pending_callbacks.borrow_mut().insert(cb_id, cb);

        CommandExecutor::instance().send(
            Command::Ledger(
                LedgerCommand::GetSchema(
                    pool_handle,
                    Some(submitter_did.to_string()),
                    id.to_string(),
                    Box::new(move |ledger_response| {
                        CommandExecutor::instance().send(
                            Command::Cache(
                                CacheCommand::GetSchemaContinue(
                                    wallet_handle,
                                    ledger_response,
                                    options.clone(),
                                    cb_id,
                                )
                            )
                        ).unwrap();
                    })
                )
            )
        ).unwrap();
    }

    fn _delete_and_add_record(&self,
                              wallet_handle: WalletHandle,
                              options: GetCacheOptions,
                              schema_id: &str,
                              schema_json: &str,
                              which_cache: &str) -> IndyResult<()>
    {
        if !options.no_store.unwrap_or(false) {
            let mut tags = Tags::new();
            let ts = match SystemTime::now().duration_since(UNIX_EPOCH) {
                Ok(ts) => ts.as_secs() as i32,
                Err(err) => {
                    warn!("Cannot get time: {:?}", err);
                    0
                }
            };
            tags.insert("timestamp".to_string(), ts.to_string());
            self.wallet_service.delete_record(wallet_handle, which_cache, &schema_id)?;
            self.wallet_service.add_record(wallet_handle, which_cache, &schema_id, &schema_json, &tags)?
        }
        Ok(())
    }

    fn _get_schema_continue(&self,
                            wallet_handle: WalletHandle,
                            ledger_response: IndyResult<(String, String)>,
                            options: GetCacheOptions, cb_id: CommandHandle) {
        let cb = self.pending_callbacks.borrow_mut().remove(&cb_id).expect("FIXME INVALID STATE");

        let (schema_id, schema_json) = try_cb!(ledger_response, cb);

        match self._delete_and_add_record(wallet_handle, options, &schema_id, &schema_json, SCHEMA_CACHE) {
            Ok(_) => cb(Ok(schema_json)),
            Err(err) => cb(Err(IndyError::from_msg(IndyErrorKind::InvalidState, format!("get_schema_continue failed: {:?}", err))))
        }
    }

    fn get_cred_def(&self,
                    pool_handle: PoolHandle,
                    wallet_handle: WalletHandle,
                    submitter_did: &str,
                    id: &str,
                    options_json: &str,
                    cb: Box<Fn(IndyResult<String>) + Send>) {
        trace!("get_cred_def >>> pool_handle: {:?}, wallet_handle: {:?}, submitter_did: {:?}, id: {:?}, options_json: {:?}",
               pool_handle, wallet_handle, submitter_did, id, options_json);

        let options = try_cb!(serde_json::from_str::<GetCacheOptions>(options_json).to_indy(IndyErrorKind::InvalidStructure, "Cannot deserialize options"), cb);

        let cache = self.get_record_from_cache(wallet_handle, id, &options, CRED_DEF_CACHE);
        let cache = try_cb!(cache, cb);

        check_cache!(cache, options, cb);

        if options.no_update.unwrap_or(false) {
            return cb(Err(IndyError::from(IndyErrorKind::LedgerItemNotFound)));
        }

        let cb_id = next_command_handle();
        self.pending_callbacks.borrow_mut().insert(cb_id, cb);

        CommandExecutor::instance().send(
            Command::Ledger(
                LedgerCommand::GetCredDef(
                    pool_handle,
                    Some(submitter_did.to_string()),
                    id.to_string(),
                    Box::new(move |ledger_response| {
                        CommandExecutor::instance().send(
                            Command::Cache(
                                CacheCommand::GetCredDefContinue(
                                    wallet_handle,
                                    ledger_response,
                                    options.clone(),
                                    cb_id,
                                )
                            )
                        ).unwrap();
                    })
                )
            )
        ).unwrap();
    }

    fn get_record_from_cache(&self, wallet_handle: WalletHandle, id: &str, options: &GetCacheOptions, which_cache: &str) -> Result<Option<WalletRecord>, IndyError> {
        if !options.no_cache.unwrap_or(false) {
            let options_json = json!({
                "retrieveType": false,
                "retrieveValue": true,
                "retrieveTags": true,
            }).to_string();
            match self.wallet_service.get_record(wallet_handle, which_cache, id, &options_json) {
                Ok(record) => Ok(Some(record)),
                Err(err) => if err.kind() == IndyErrorKind::WalletItemNotFound { Ok(None) } else { Err(err) }
            }
        } else { Ok(None) }
    }

    fn _get_cred_def_continue(&self, wallet_handle: WalletHandle, ledger_response: IndyResult<(String, String)>, options: GetCacheOptions, cb_id: CommandHandle) {
        let cb = self.pending_callbacks.borrow_mut().remove(&cb_id).expect("FIXME INVALID STATE");

        let (cred_def_id, cred_def_json) = try_cb!(ledger_response, cb);

        match self._delete_and_add_record(wallet_handle, options, &cred_def_id, &cred_def_json, CRED_DEF_CACHE) {
            Ok(_) => cb(Ok(cred_def_json)),
            Err(err) => cb(Err(IndyError::from_msg(IndyErrorKind::InvalidState, format!("get_cred_def_continue failed: {:?}", err))))
        }
    }

    fn get_seconds_since_epoch() -> Result<i32, IndyError> {
        match SystemTime::now().duration_since(UNIX_EPOCH) {
            Ok(ts) => Ok(ts.as_secs() as i32),
            Err(err) => {
                error!("Cannot get time: {:?}", err);
                Err(IndyError::from_msg(IndyErrorKind::InvalidState, format!("Cannot get time: {:?}", err)))
            }
        }
    }

    fn build_query_json(max_age: i32) -> Result<String, IndyError> {
        if max_age >= 0 {
            let ts = CacheCommandExecutor::get_seconds_since_epoch()?;
            Ok(json!({"timestamp": {"$lt": ts - max_age}}).to_string())
        } else {
            Ok("{}".to_string())
        }
    }

    fn purge_schema_cache(&self,
                          wallet_handle: WalletHandle,
                          options_json: &str) -> IndyResult<()> {
        trace!("purge_schema_cache >>> wallet_handle: {:?}, options_json: {:?}", wallet_handle, options_json);

        let options = serde_json::from_str::<PurgeOptions>(options_json)
            .to_indy(IndyErrorKind::InvalidStructure, "Cannot deserialize options")?;

        let max_age = options.max_age.unwrap_or(-1);
        let query_json = CacheCommandExecutor::build_query_json(max_age)?;

        let options_json = json!({
            "retrieveType": false,
            "retrieveValue": false,
            "retrieveTags": false,
        }).to_string();

        let mut search = self.wallet_service.search_records(
            wallet_handle,
            SCHEMA_CACHE,
            &query_json,
            &options_json,
        )?;

        while let Some(record) = search.fetch_next_record()? {
            self.wallet_service.delete_record(wallet_handle, SCHEMA_CACHE, record.get_id())?;
        }

        trace!("purge_schema_cache <<< res: ()");

        Ok(())
    }

    fn purge_cred_def_cache(&self,
                            wallet_handle: WalletHandle,
                            options_json: &str) -> IndyResult<()> {
        trace!("purge_cred_def_cache >>> wallet_handle: {:?}, options_json: {:?}", wallet_handle, options_json);

        let options = serde_json::from_str::<PurgeOptions>(options_json)
            .to_indy(IndyErrorKind::InvalidStructure, "Cannot deserialize options")?;

        let max_age = options.max_age.unwrap_or(-1);
        let query_json = CacheCommandExecutor::build_query_json(max_age)?;

        let options_json = json!({
            "retrieveType": false,
            "retrieveValue": false,
            "retrieveTags": false,
        }).to_string();

        let mut search = self.wallet_service.search_records(
            wallet_handle,
            CRED_DEF_CACHE,
            &query_json,
            &options_json,
        )?;

        while let Some(record) = search.fetch_next_record()? {
            self.wallet_service.delete_record(wallet_handle, CRED_DEF_CACHE, record.get_id())?;
        }

        trace!("purge_cred_def_cache <<< res: ()");

        Ok(())
    }
}

#[serde(rename_all = "camelCase")]
#[derive(Debug, Deserialize, Serialize)]
struct PurgeOptions {
    pub max_age: Option<i32>,
}

#[serde(rename_all = "camelCase")]
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct GetCacheOptions {
    pub no_cache: Option<bool>,     // Skip usage of cache,
    pub no_update: Option<bool>,    // Use only cached data, do not try to update.
    pub no_store: Option<bool>,     // Skip storing fresh data if updated
    pub min_fresh: Option<i32>,     // Return cached data if not older than this many seconds. -1 means do not check age.
}
<|endoftext|>


<commit_before>// TODO: COPYRIGHT, USE & AUTHORS

use super::ga_core::GASolution;

use std::cmp::Ordering;
use std::iter::FromIterator;

// Better name than 'Basis'?
pub enum GAPopulationSortBasis
{
    Raw,
    Scaled,
}

// Isn't a boolean enough?
pub enum GAPopulationSortOrder
{
    LowIsBest,
    HighIsBest,
}

/// Genetic Algorithm Population
// TODO: RUST DOCS!
pub struct GAPopulation<T: GASolution>
{
    population: Vec<T>,

    sort_order: GAPopulationSortOrder,

    // 'population' ordered by Raw score.
    population_order_raw: Vec<usize>,
    // Is 'population_order_raw' sorted?
    is_raw_sorted: bool,

    // 'population' ordered by Scaled score.
    population_order_scaled: Vec<usize>,
    // Is 'population_scaled' sorted?
    is_scaled_sorted: bool,

    // We keep 2 lists of indexes to the population vector.
    // One sorted by Raw Score and one by Scaled Score (Fitness).
}
impl<T: GASolution> GAPopulation<T>
{
    // Need best() and sort() for Selectors.

    // TODO: New should use some parameters, maybe a Config
    pub fn new(p: Vec<T>, order: GAPopulationSortOrder) -> GAPopulation<T>
    {
        let gap = GAPopulation
                  {
                      population: p,
                      sort_order: order,
                      population_order_raw: vec![],
                      is_raw_sorted: false,
                      population_order_scaled: vec![],
                      is_scaled_sorted: false
                  };

        gap
    }

    pub fn population(&mut self) -> &mut Vec<T>
    {
        return &mut self.population
    }

    pub fn evaluate(&mut self)
    {
        for ref mut ind in &mut self.population
        {
            ind.evaluate();
        }
    }

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

    //TODO: this is a temporary implementation
    pub fn select(&self) -> &T
    {
        self.individual(0, GAPopulationSortBasis::Scaled)
    }

    //TODO: This is a temporary implementation
    pub fn best(&self) -> &T
    {
        self.individual(0, GAPopulationSortBasis::Scaled)
    }

    //TODO: This is a temporary implementation
    pub fn worst(&self) -> &T
    {
        self.individual(self.size()-1, GAPopulationSortBasis::Scaled)
    }

    // Q: Seems like this could be ith_best() instead of individual().
    //    indvidual() could be used to iterate through the population in no
    //    particular order of score.
    pub fn individual(&self, i : usize, sort_basis : GAPopulationSortBasis) -> &T
    {
        // TODO: Check that i makes sense
        match sort_basis
        {
            GAPopulationSortBasis::Raw
            => { &self.population[self.population_order_raw[i]] },
            GAPopulationSortBasis::Scaled
            => { &self.population[self.population_order_scaled[i]] },
        }
    }

    pub fn sort(&mut self)
    {
        self.sort_int(false, GAPopulationSortBasis::Scaled);
        self.sort_int(false, GAPopulationSortBasis::Raw);

        // Q: Selector needs to be updated after sorting?
    }

    //TODO: I hate this name
    pub fn sort_int(&mut self, force_sort: bool, sort_basis: GAPopulationSortBasis)
    {
        let mut ordered : Vec<usize> = Vec::from_iter(0..self.size());
        match sort_basis
        {
            GAPopulationSortBasis::Raw
            =>  if (!self.is_raw_sorted) || force_sort
                {
                    match self.sort_order
                    {
                        GAPopulationSortOrder::LowIsBest =>
                        {
                            ordered.sort_by(|s1: &usize, s2: &usize|
                                            self.population[*s1].score()
                                                .partial_cmp(&self.population[*s2].score()).unwrap_or(Ordering::Equal));

                        },
                        GAPopulationSortOrder::HighIsBest =>
                        {
                            ordered.sort_by(|s1: &usize, s2: &usize|
                                            self.population[*s2].score()
                                                .partial_cmp(&self.population[*s1].score()).unwrap_or(Ordering::Equal));

                        },
                    };
                    self.population_order_raw = ordered;
                    self.is_raw_sorted = true;
                },

            GAPopulationSortBasis::Scaled
            =>  if (!self.is_scaled_sorted) || force_sort
                {
                    match self.sort_order
                    {
                        GAPopulationSortOrder::LowIsBest =>
                        {
                            ordered.sort_by(|s1: &usize, s2: &usize|
                                            self.population[*s1].fitness()
                                                .partial_cmp(&self.population[*s2].fitness()).unwrap_or(Ordering::Equal));
                        },

                        GAPopulationSortOrder::HighIsBest =>
                        {
                            ordered.sort_by(|s1: &usize, s2: &usize|
                                            self.population[*s2].fitness()
                                                .partial_cmp(&self.population[*s1].fitness()).unwrap_or(Ordering::Equal));
                        }
                    };
                    self.population_order_scaled = ordered;
                    self.is_scaled_sorted = true;
                },
        };
    }
}

#[cfg(test)]
mod test
{
}
<commit_msg>Remove last push's comments.<commit_after>// TODO: COPYRIGHT, USE & AUTHORS

use super::ga_core::GASolution;

use std::cmp::Ordering;
use std::iter::FromIterator;

// Better name than 'Basis'?
pub enum GAPopulationSortBasis
{
    Raw,
    Scaled,
}

// Isn't a boolean enough?
pub enum GAPopulationSortOrder
{
    LowIsBest,
    HighIsBest,
}

/// Genetic Algorithm Population
// TODO: RUST DOCS!
pub struct GAPopulation<T: GASolution>
{
    population: Vec<T>,

    sort_order: GAPopulationSortOrder,

    // 'population' ordered by Raw score.
    population_order_raw: Vec<usize>,
    // Is 'population_order_raw' sorted?
    is_raw_sorted: bool,

    // 'population' ordered by Scaled score.
    population_order_scaled: Vec<usize>,
    // Is 'population_scaled' sorted?
    is_scaled_sorted: bool,

    // We keep 2 lists of indexes to the population vector.
    // One sorted by Raw Score and one by Scaled Score (Fitness).
}
impl<T: GASolution> GAPopulation<T>
{
    // Need best() and sort() for Selectors.

    // TODO: New should use some parameters, maybe a Config
    pub fn new(p: Vec<T>, order: GAPopulationSortOrder) -> GAPopulation<T>
    {
        let gap = GAPopulation
                  {
                      population: p,
                      sort_order: order,
                      population_order_raw: vec![],
                      is_raw_sorted: false,
                      population_order_scaled: vec![],
                      is_scaled_sorted: false
                  };

        gap
    }

    pub fn population(&mut self) -> &mut Vec<T>
    {
        return &mut self.population
    }

    pub fn evaluate(&mut self)
    {
        for ref mut ind in &mut self.population
        {
            ind.evaluate();
        }
    }

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

    //TODO: this is a temporary implementation
    pub fn select(&self) -> &T
    {
        self.individual(0, GAPopulationSortBasis::Scaled)
    }

    //TODO: This is a temporary implementation
    pub fn best(&self) -> &T
    {
        // TODO: Call GAPopulation.scale().

        self.individual(0, GAPopulationSortBasis::Scaled)
    }

    //TODO: This is a temporary implementation
    pub fn worst(&self) -> &T
    {
        self.individual(self.size()-1, GAPopulationSortBasis::Scaled)
    }

    pub fn individual(&self, i : usize, sort_basis : GAPopulationSortBasis) -> &T
    {
        // TODO: Check that i makes sense
        match sort_basis
        {
            GAPopulationSortBasis::Raw
            => { &self.population[self.population_order_raw[i]] },
            GAPopulationSortBasis::Scaled
            => { &self.population[self.population_order_scaled[i]] },
        }
    }

    pub fn sort(&mut self)
    {
        self.sort_int(false, GAPopulationSortBasis::Scaled);
        self.sort_int(false, GAPopulationSortBasis::Raw);
    }

    //TODO: I hate this name
    pub fn sort_int(&mut self, force_sort: bool, sort_basis: GAPopulationSortBasis)
    {
        let mut ordered : Vec<usize> = Vec::from_iter(0..self.size());
        match sort_basis
        {
            GAPopulationSortBasis::Raw
            =>  if (!self.is_raw_sorted) || force_sort
                {
                    match self.sort_order
                    {
                        GAPopulationSortOrder::LowIsBest =>
                        {
                            ordered.sort_by(|s1: &usize, s2: &usize|
                                            self.population[*s1].score()
                                                .partial_cmp(&self.population[*s2].score()).unwrap_or(Ordering::Equal));

                        },
                        GAPopulationSortOrder::HighIsBest =>
                        {
                            ordered.sort_by(|s1: &usize, s2: &usize|
                                            self.population[*s2].score()
                                                .partial_cmp(&self.population[*s1].score()).unwrap_or(Ordering::Equal));

                        },
                    };
                    self.population_order_raw = ordered;
                    self.is_raw_sorted = true;
                },

            GAPopulationSortBasis::Scaled
            =>  if (!self.is_scaled_sorted) || force_sort
                {
                    match self.sort_order
                    {
                        GAPopulationSortOrder::LowIsBest =>
                        {
                            ordered.sort_by(|s1: &usize, s2: &usize|
                                            self.population[*s1].fitness()
                                                .partial_cmp(&self.population[*s2].fitness()).unwrap_or(Ordering::Equal));
                        },

                        GAPopulationSortOrder::HighIsBest =>
                        {
                            ordered.sort_by(|s1: &usize, s2: &usize|
                                            self.population[*s2].fitness()
                                                .partial_cmp(&self.population[*s1].fitness()).unwrap_or(Ordering::Equal));
                        }
                    };
                    self.population_order_scaled = ordered;
                    self.is_scaled_sorted = true;
                },
        };
    }
}

#[cfg(test)]
mod test
{
}
<|endoftext|>


<commit_before>/// Create a reply markup.
///
/// # Examples
/// ```
/// # #[macro_use] extern crate telegram_bot;
/// # fn main() {
/// let reply_keyboard = reply_markup!(reply_keyboard, selective, one_time, resize,
///     ["button", "button"],
///     ["button" contact], // Request contact
///     ["button" location] // Request location
/// );
///
/// let remove_keyboard = reply_markup!(remove_keyboard);
/// let selective_remove_keyboard = reply_markup!(remove_keyboard, selective);
///
/// let force_reply = reply_markup!(force_reply);
/// let selective_force_reply = reply_markup!(force_reply, selective);
///
/// let inline_keyboard = reply_markup!(inline_keyboard,
///     ["button" callback "0,0", "button" callback "0,1"],
///     ["button" callback "1,0", "button" callback "1,1", "button" callback "1,2"]
/// );
///
/// # drop(inline_keyboard);
/// # drop(selective_force_reply);
/// # drop(force_reply);
/// # drop(selective_remove_keyboard);
/// # drop(remove_keyboard);
/// # drop(reply_keyboard);
/// # }
/// ```
#[macro_export]
macro_rules! reply_markup {
    (remove_keyboard) => ({
          $crate::ReplyKeyboardRemove::new()
    });

    (remove_keyboard, selective) => ({
          let mut keyboard = reply_markup!(remove_keyboard);
          keyboard.selective();
          keyboard
    });

    (force_reply) => ({
          $crate::ForceReply::new()
    });

    (force_reply, selective) => ({
          let mut keyboard = reply_markup!(force_reply);
          keyboard.selective();
          keyboard
    });

    (reply_keyboard, $($content:tt)*) => ({
        reply_markup!(_reply_keyboard, $($content)*)
    });

    (_reply_keyboard, resize, $($content:tt)*) => ({
        let mut keyboard = reply_markup!(_reply_keyboard, $($content)*);
        keyboard.resize_keyboard();
        keyboard
    });

    (_reply_keyboard, one_time, $($content:tt)*) => ({
        let mut keyboard = reply_markup!(_reply_keyboard, $($content)*);
        keyboard.one_time_keyboard();
        keyboard
    });

    (_reply_keyboard, selective, $($content:tt)*) => ({
        let mut keyboard = reply_markup!(_reply_keyboard, $($content)*);
        keyboard.selective();
        keyboard
    });

    (_reply_keyboard, $([$($content:tt)*]), *) => (
        $crate::ReplyKeyboardMarkup::from(vec![$(reply_markup![_reply_keyboard_row, $($content)*]), *])
    );

    (_reply_keyboard_row, ($($acc:tt)*); $value:expr) => (vec![$($acc)* reply_markup!(_reply_keyboard_button, $value)]);
    (_reply_keyboard_row, ($($acc:tt)*); $value:expr, $($remaining:tt)*) => (
    reply_markup!(_reply_keyboard_row, ($($acc)* reply_markup!(_reply_keyboard_button,  $value),); $($remaining)*)

    );

    (_reply_keyboard_row, ($($acc:tt)*); $value:tt $request:tt) => (vec![$($acc)* reply_markup!(_reply_keyboard_button,  $value, $request)]);
    (_reply_keyboard_row, ($($acc:tt)*); $value:tt $request:tt, $($remaining: tt)*) => (
        reply_markup!(_reply_keyboard_row, ($($acc)* reply_markup!(_reply_keyboard_button, $value, $request),); $($remaining)*)
    );

    (_reply_keyboard_row, $($content:expr), *) => (vec![$(reply_markup!(_reply_keyboard_button,  $content)), *]);
    (_reply_keyboard_row, $($content:tt)*) => (reply_markup!(_reply_keyboard_row, (); $($content)*));

    (_reply_keyboard_button, $value:expr, contact) => ({
        let mut button: $crate::KeyboardButton = reply_markup!(_reply_keyboard_button, $value);
        button.request_contact();
        button
    });

    (_reply_keyboard_button, $value:expr, location) => ({
        let mut button: $crate::KeyboardButton = reply_markup!(_reply_keyboard_button, $value);
        button.request_location();
        button
    });
    (_reply_keyboard_button, $value:expr) => ($value.into());

    (inline_keyboard, $([$($content:tt)*]), *) => (
        $crate::InlineKeyboardMarkup::from(vec![$(reply_markup![_inline_keyboard_row, $($content)*]), *])
    );

    (_inline_keyboard_row, ($($acc:tt)*); $text:tt callback $callback:tt) => (
        vec![$($acc)* reply_markup!(_inline_keyboard_button_callback,  $text, $callback)]
    );
    (_inline_keyboard_row, $($text:tt callback $callback:tt), *) => (
        vec![$(reply_markup!(_inline_keyboard_button_callback, $text, $callback)), *]
    );
    (_inline_keyboard_row, $($content:tt)*) => (reply_markup!(_inline_keyboard_row, (); $($content)*));

    (_inline_keyboard_button_callback, $text:expr, $callback:expr) => (
        $crate::InlineKeyboardButton::callback($text, $callback)
    );
}

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

    #[test]
    fn test_simple() {
        let mut remove_keyboard = ReplyKeyboardRemove::new();
        assert_eq!(remove_keyboard, reply_markup!(remove_keyboard));

        remove_keyboard.selective();
        assert_eq!(remove_keyboard, reply_markup!(remove_keyboard, selective));

        let mut force_reply = ForceReply::new();
        assert_eq!(force_reply, reply_markup!(force_reply));

        force_reply.selective();
        assert_eq!(force_reply, reply_markup!(force_reply, selective));
    }

    #[test]
    fn test_reply_keyboard() {
        let mut keyboard = ReplyKeyboardMarkup::new();
        assert_eq!(keyboard, reply_markup!(reply_keyboard,));

        keyboard.add_empty_row();
        assert_eq!(keyboard, reply_markup!(reply_keyboard, []));

        {
            let row = keyboard.add_empty_row();
            row.push(KeyboardButton::new("foo"));
            row.push(KeyboardButton::new("bar"));
        }
        assert_eq!(keyboard, reply_markup!(reply_keyboard, [], ["foo", "bar"]));

        {
            let row = keyboard.add_empty_row();
            row.push(KeyboardButton::new("baz"));
        }
        assert_eq!(keyboard, reply_markup!(reply_keyboard, [], ["foo", "bar"], ["baz"]));

        {
            let row = keyboard.add_empty_row();

            let mut contact_button = KeyboardButton::new("contact");
            contact_button.request_contact();
            row.push(contact_button);

            let mut location_button = KeyboardButton::new("location");
            location_button.request_location();
            row.push(location_button)
        }
        assert_eq!(keyboard, reply_markup!(
            reply_keyboard, [], ["foo", "bar"], ["baz"],
            ["contact" contact, "location" location]
        ));

        {
            let row = keyboard.add_empty_row();
            row.push(KeyboardButton::new("spam"));
        }
        assert_eq!(keyboard, reply_markup!(
            reply_keyboard, [], ["foo", "bar"], ["baz"],
            ["contact" contact, "location" location],
            ["spam"]
        ));

        keyboard.selective();
        assert_eq!(keyboard, reply_markup!(
            reply_keyboard, selective, [], ["foo", "bar"], ["baz"],
            ["contact" contact, "location" location],
            ["spam"]
        ));

        keyboard.resize_keyboard();
        assert_eq!(keyboard, reply_markup!(
            reply_keyboard, resize, selective, [], ["foo", "bar"], ["baz"],
            ["contact" contact, "location" location],
            ["spam"]
        ));

        keyboard.one_time_keyboard();
        assert_eq!(keyboard, reply_markup!(
            reply_keyboard, resize, selective, one_time, [], ["foo", "bar"], ["baz"],
            ["contact" contact, "location" location],
            ["spam"]
        ));
    }

    #[test]
    fn test_inline_keyboard() {
        let mut markup = InlineKeyboardMarkup::new();
        assert_eq!(markup, reply_markup!(inline_keyboard,));

        markup.add_empty_row();
        assert_eq!(markup, reply_markup!(inline_keyboard, []));

        {
            let row = markup.add_empty_row();
            row.push(InlineKeyboardButton::callback("foo", "bar"));
            row.push(InlineKeyboardButton::callback("baz", "quux"));
        }
        assert_eq!(markup, reply_markup!(inline_keyboard, [], ["foo" callback "bar", "baz" callback "quux"]));
    }
}
<commit_msg>refactor: change macro for inline keyboard<commit_after>/// Create a reply markup.
///
/// # Examples
/// ```
/// # #[macro_use] extern crate telegram_bot;
/// # fn main() {
/// let reply_keyboard = reply_markup!(reply_keyboard, selective, one_time, resize,
///     ["button", "button"],
///     ["button" contact], // Request contact
///     ["button" location] // Request location
/// );
///
/// let remove_keyboard = reply_markup!(remove_keyboard);
/// let selective_remove_keyboard = reply_markup!(remove_keyboard, selective);
///
/// let force_reply = reply_markup!(force_reply);
/// let selective_force_reply = reply_markup!(force_reply, selective);
///
/// let inline_keyboard = reply_markup!(inline_keyboard,
///     ["button" callback "0,0", "button" callback "0,1"],
///     ["button" callback "1,0", "button" callback "1,1", "button" callback "1,2"]
/// );
///
/// # drop(inline_keyboard);
/// # drop(selective_force_reply);
/// # drop(force_reply);
/// # drop(selective_remove_keyboard);
/// # drop(remove_keyboard);
/// # drop(reply_keyboard);
/// # }
/// ```
#[macro_export]
macro_rules! reply_markup {
    (remove_keyboard) => ({
          $crate::ReplyKeyboardRemove::new()
    });

    (remove_keyboard, selective) => ({
          let mut keyboard = reply_markup!(remove_keyboard);
          keyboard.selective();
          keyboard
    });

    (force_reply) => ({
          $crate::ForceReply::new()
    });

    (force_reply, selective) => ({
          let mut keyboard = reply_markup!(force_reply);
          keyboard.selective();
          keyboard
    });

    (reply_keyboard, $($content:tt)*) => ({
        reply_markup!(_reply_keyboard, $($content)*)
    });

    (_reply_keyboard, resize, $($content:tt)*) => ({
        let mut keyboard = reply_markup!(_reply_keyboard, $($content)*);
        keyboard.resize_keyboard();
        keyboard
    });

    (_reply_keyboard, one_time, $($content:tt)*) => ({
        let mut keyboard = reply_markup!(_reply_keyboard, $($content)*);
        keyboard.one_time_keyboard();
        keyboard
    });

    (_reply_keyboard, selective, $($content:tt)*) => ({
        let mut keyboard = reply_markup!(_reply_keyboard, $($content)*);
        keyboard.selective();
        keyboard
    });

    (_reply_keyboard, $([$($content:tt)*]), *) => (
        $crate::ReplyKeyboardMarkup::from(vec![$(reply_markup![_reply_keyboard_row, $($content)*]), *])
    );

    (_reply_keyboard_row, ($($acc:tt)*); $value:expr) => (vec![$($acc)* reply_markup!(_reply_keyboard_button, $value)]);
    (_reply_keyboard_row, ($($acc:tt)*); $value:expr, $($remaining:tt)*) => (
    reply_markup!(_reply_keyboard_row, ($($acc)* reply_markup!(_reply_keyboard_button,  $value),); $($remaining)*)

    );

    (_reply_keyboard_row, ($($acc:tt)*); $value:tt $request:tt) => (vec![$($acc)* reply_markup!(_reply_keyboard_button,  $value, $request)]);
    (_reply_keyboard_row, ($($acc:tt)*); $value:tt $request:tt, $($remaining: tt)*) => (
        reply_markup!(_reply_keyboard_row, ($($acc)* reply_markup!(_reply_keyboard_button, $value, $request),); $($remaining)*)
    );

    (_reply_keyboard_row, $($content:expr), *) => (vec![$(reply_markup!(_reply_keyboard_button,  $content)), *]);
    (_reply_keyboard_row, $($content:tt)*) => (reply_markup!(_reply_keyboard_row, (); $($content)*));

    (_reply_keyboard_button, $value:expr, contact) => ({
        let mut button: $crate::KeyboardButton = reply_markup!(_reply_keyboard_button, $value);
        button.request_contact();
        button
    });

    (_reply_keyboard_button, $value:expr, location) => ({
        let mut button: $crate::KeyboardButton = reply_markup!(_reply_keyboard_button, $value);
        button.request_location();
        button
    });
    (_reply_keyboard_button, $value:expr) => ($value.into());

    (inline_keyboard, $([$($content:tt)*]), *) => (
        $crate::InlineKeyboardMarkup::from(vec![$(reply_markup![_inline_keyboard_row, $($content)*]), *])
    );

    (_inline_keyboard_row, ($($acc:tt)*); $text:tt $request:tt $callback:tt) => (
        vec![$($acc)* reply_markup!(_inline_keyboard_button, $request,  $text, $callback)]
    );
    (_inline_keyboard_row, $($text:tt $request:tt $callback:tt), *) => (
        vec![$(reply_markup!(_inline_keyboard_button, $request, $text, $callback)), *]
    );
    (_inline_keyboard_row, $($content:tt)*) => (reply_markup!(_inline_keyboard_row, (); $($content)*));

    (_inline_keyboard_button, callback, $text:expr, $callback:expr) => (
        $crate::InlineKeyboardButton::callback($text, $callback)
    );
}

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

    #[test]
    fn test_simple() {
        let mut remove_keyboard = ReplyKeyboardRemove::new();
        assert_eq!(remove_keyboard, reply_markup!(remove_keyboard));

        remove_keyboard.selective();
        assert_eq!(remove_keyboard, reply_markup!(remove_keyboard, selective));

        let mut force_reply = ForceReply::new();
        assert_eq!(force_reply, reply_markup!(force_reply));

        force_reply.selective();
        assert_eq!(force_reply, reply_markup!(force_reply, selective));
    }

    #[test]
    fn test_reply_keyboard() {
        let mut keyboard = ReplyKeyboardMarkup::new();
        assert_eq!(keyboard, reply_markup!(reply_keyboard,));

        keyboard.add_empty_row();
        assert_eq!(keyboard, reply_markup!(reply_keyboard, []));

        {
            let row = keyboard.add_empty_row();
            row.push(KeyboardButton::new("foo"));
            row.push(KeyboardButton::new("bar"));
        }
        assert_eq!(keyboard, reply_markup!(reply_keyboard, [], ["foo", "bar"]));

        {
            let row = keyboard.add_empty_row();
            row.push(KeyboardButton::new("baz"));
        }
        assert_eq!(keyboard, reply_markup!(reply_keyboard, [], ["foo", "bar"], ["baz"]));

        {
            let row = keyboard.add_empty_row();

            let mut contact_button = KeyboardButton::new("contact");
            contact_button.request_contact();
            row.push(contact_button);

            let mut location_button = KeyboardButton::new("location");
            location_button.request_location();
            row.push(location_button)
        }
        assert_eq!(keyboard, reply_markup!(
            reply_keyboard, [], ["foo", "bar"], ["baz"],
            ["contact" contact, "location" location]
        ));

        {
            let row = keyboard.add_empty_row();
            row.push(KeyboardButton::new("spam"));
        }
        assert_eq!(keyboard, reply_markup!(
            reply_keyboard, [], ["foo", "bar"], ["baz"],
            ["contact" contact, "location" location],
            ["spam"]
        ));

        keyboard.selective();
        assert_eq!(keyboard, reply_markup!(
            reply_keyboard, selective, [], ["foo", "bar"], ["baz"],
            ["contact" contact, "location" location],
            ["spam"]
        ));

        keyboard.resize_keyboard();
        assert_eq!(keyboard, reply_markup!(
            reply_keyboard, resize, selective, [], ["foo", "bar"], ["baz"],
            ["contact" contact, "location" location],
            ["spam"]
        ));

        keyboard.one_time_keyboard();
        assert_eq!(keyboard, reply_markup!(
            reply_keyboard, resize, selective, one_time, [], ["foo", "bar"], ["baz"],
            ["contact" contact, "location" location],
            ["spam"]
        ));
    }

    #[test]
    fn test_inline_keyboard() {
        let mut markup = InlineKeyboardMarkup::new();
        assert_eq!(markup, reply_markup!(inline_keyboard,));

        markup.add_empty_row();
        assert_eq!(markup, reply_markup!(inline_keyboard, []));

        {
            let row = markup.add_empty_row();
            row.push(InlineKeyboardButton::callback("foo", "bar"));
            row.push(InlineKeyboardButton::callback("baz", "quux"));
        }
        assert_eq!(markup, reply_markup!(inline_keyboard, [], ["foo" callback "bar", "baz" callback "quux"]));
    }
}
<|endoftext|>


<commit_before>#![deny(missing_docs, warnings)]
#![feature(macro_rules)]

//! The `zip!` macro

/// This macro emulates an "any-arity" free function that zips iterators
///
/// **Note** This macro calls `into_iter()` on the inputs. For this reason, this macro won't work
/// until `IntoIterator` lands in stdlib, or unless you provide your own `IntoIterator` trait.
///
/// # Examples
///
/// ```
/// # #![feature(phase)]
/// # extern crate zip;
/// # #[phase(plugin)]
/// # extern crate zip_macros;
/// # use std::slice;
/// # trait IntoIterator<T, I> where I: Iterator<T> {
/// #     fn into_iter(self) -> I;
/// # }
/// # impl<T, I> IntoIterator<T, I> for I where I: Iterator<T> {
/// #     fn into_iter(self) -> I { self }
/// # }
/// # impl<'a, T> IntoIterator<&'a T, slice::Items<'a, T>> for &'a [T] {
/// #     fn into_iter(self) -> slice::Items<'a, T> { self.iter() }
/// # }
/// # impl<'a, T> IntoIterator<&'a mut T, slice::MutItems<'a, T>> for &'a mut [T] {
/// #     fn into_iter(self) -> slice::MutItems<'a, T> { self.iter_mut() }
/// # }
/// # fn main() {
/// let chars = &['a', 'b', 'c'];
/// let mut v = vec![0u, 1, 2];
///
/// for (&c, i, &mut j) in zip!(chars, range(0u, 5), &mut *v) {
///     assert!(i < 3);
///     assert_eq!(i, j);
/// }
/// # }
/// ```
#[macro_export]
macro_rules! zip {
    ($a:expr, $b:expr) => {
        ::zip::Zip2(($a).into_iter(), ($b).into_iter())
    };
    ($a:expr, $b:expr, $c:expr) => {
        ::zip::Zip3(($a).into_iter(), ($b).into_iter(), ($c).into_iter())
    };
    ($a:expr, $b:expr, $c:expr, $d:expr) => {
        ::zip::Zip4(($a).into_iter(), ($b).into_iter(), ($c).into_iter(), ($d).into_iter())
    };
    ($a:expr, $b:expr, $c:expr, $d:expr, $e:expr) => {
        ::zip::Zip5(
            ($a).into_iter(),
            ($b).into_iter(),
            ($c).into_iter(),
            ($d).into_iter(),
            ($e).into_iter(),
        )
    };
    ($($x:expr),+,) => { zip!($($x),+) }
}
<commit_msg>fix(doctest): `slice::[Mut]Items` -> `slice::Iter[Mut]`<commit_after>#![deny(missing_docs, warnings)]
#![feature(macro_rules)]

//! The `zip!` macro

/// This macro emulates an "any-arity" free function that zips iterators
///
/// **Note** This macro calls `into_iter()` on the inputs. For this reason, this macro won't work
/// until `IntoIterator` lands in stdlib, or unless you provide your own `IntoIterator` trait.
///
/// # Examples
///
/// ```
/// # #![feature(phase)]
/// # extern crate zip;
/// # #[phase(plugin)]
/// # extern crate zip_macros;
/// # use std::slice;
/// # trait IntoIterator<T, I> where I: Iterator<T> {
/// #     fn into_iter(self) -> I;
/// # }
/// # impl<T, I> IntoIterator<T, I> for I where I: Iterator<T> {
/// #     fn into_iter(self) -> I { self }
/// # }
/// # impl<'a, T> IntoIterator<&'a T, slice::Iter<'a, T>> for &'a [T] {
/// #     fn into_iter(self) -> slice::Iter<'a, T> { self.iter() }
/// # }
/// # impl<'a, T> IntoIterator<&'a mut T, slice::IterMut<'a, T>> for &'a mut [T] {
/// #     fn into_iter(self) -> slice::IterMut<'a, T> { self.iter_mut() }
/// # }
/// # fn main() {
/// let chars = &['a', 'b', 'c'];
/// let mut v = vec![0u, 1, 2];
///
/// for (&c, i, &mut j) in zip!(chars, range(0u, 5), &mut *v) {
///     assert!(i < 3);
///     assert_eq!(i, j);
/// }
/// # }
/// ```
#[macro_export]
macro_rules! zip {
    ($a:expr, $b:expr) => {
        ::zip::Zip2(($a).into_iter(), ($b).into_iter())
    };
    ($a:expr, $b:expr, $c:expr) => {
        ::zip::Zip3(($a).into_iter(), ($b).into_iter(), ($c).into_iter())
    };
    ($a:expr, $b:expr, $c:expr, $d:expr) => {
        ::zip::Zip4(($a).into_iter(), ($b).into_iter(), ($c).into_iter(), ($d).into_iter())
    };
    ($a:expr, $b:expr, $c:expr, $d:expr, $e:expr) => {
        ::zip::Zip5(
            ($a).into_iter(),
            ($b).into_iter(),
            ($c).into_iter(),
            ($d).into_iter(),
            ($e).into_iter(),
        )
    };
    ($($x:expr),+,) => { zip!($($x),+) }
}
<|endoftext|>


<commit_before>// `cargo test --benches` and `#[feature(test)]` work only in nightly
#![cfg(rustc_nightly)]

#![feature(test)]

use std::sync::Arc;

extern crate test;
extern crate httpbis;
extern crate futures;
extern crate bytes;

use httpbis::*;

use futures::stream;
use futures::stream::Stream;
use futures::future::Future;

use bytes::Bytes;

use test::Bencher;

struct Megabyte;

impl Service for Megabyte {
    fn start_request(&self, _headers: Headers, _req: HttpStreamAfterHeaders) -> Response {
        Response::headers_and_bytes_stream(
            Headers::ok_200(),
            stream::iter_ok((0..1024).map(|i| Bytes::from(vec![(i % 0xff) as u8; 1024]))))
    }
}

#[bench]
fn bench(b: &mut Bencher) {
    b.iter(|| {
        let mut server = ServerBuilder::new_plain();
        server.set_port(0);
        server.service.set_service("/", Arc::new(Megabyte));
        let server = server.build().expect("server");

        let client = Client::new_plain(
            "127.0.0.1",
            server.local_addr().port().unwrap(),
            Default::default())
                .expect("client");

        let (header, body) = client.start_get("/any", "localhost").0.wait().expect("headers");
        assert_eq!(200, header.status());

        let mut s = 0;
        for p in body.wait() {
            match p.expect("part") {
                DataOrTrailers::Data(d, ..) => s += d.len(),
                _ => panic!("unexpected headers"),
            }
        }

        assert_eq!(1024 * 1024, s);
    })
}
<commit_msg>Better benches<commit_after>// `cargo test --benches` and `#[feature(test)]` work only in nightly
#![cfg(rustc_nightly)]

#![feature(test)]

use std::sync::Arc;

extern crate test;
extern crate httpbis;
extern crate futures;
extern crate bytes;

use httpbis::*;

use futures::stream;
use futures::stream::Stream;
use futures::future::Future;

use bytes::Bytes;

use test::Bencher;

#[bench]
fn download_megabyte_in_kb_chunks(b: &mut Bencher) {
    b.iter(|| {
        struct Megabyte;

        impl Service for Megabyte {
            fn start_request(&self, _headers: Headers, _req: HttpStreamAfterHeaders) -> Response {
                Response::headers_and_bytes_stream(
                    Headers::ok_200(),
                    stream::iter_ok((0..1024).map(|i| Bytes::from(vec![(i % 0xff) as u8; 1024]))))
            }
        }

        let mut server = ServerBuilder::new_plain();
        server.set_port(0);
        server.service.set_service("/", Arc::new(Megabyte));
        let server = server.build().expect("server");

        let client = Client::new_plain(
            "127.0.0.1",
            server.local_addr().port().unwrap(),
            Default::default())
                .expect("client");

        let (header, body) = client.start_get("/any", "localhost").0.wait().expect("headers");
        assert_eq!(200, header.status());

        let mut s = 0;
        for p in body.wait() {
            match p.expect("part") {
                DataOrTrailers::Data(d, ..) => s += d.len(),
                _ => panic!("unexpected headers"),
            }
        }

        assert_eq!(1024 * 1024, s);
    })
}

#[bench]
fn thousand_small_requests(b: &mut Bencher) {
    b.iter(|| {
        struct My;

        impl Service for My {
            fn start_request(&self, _headers: Headers, _req: HttpStreamAfterHeaders) -> Response {
                Response::found_200_plain_text("hello there")
            }
        }

        let mut server = ServerBuilder::new_plain();
        server.set_port(0);
        server.service.set_service("/", Arc::new(My));
        let server = server.build().expect("server");

        let client = Client::new_plain(
            "127.0.0.1",
            server.local_addr().port().unwrap(),
            Default::default())
            .expect("client");

        for _i in 0..1000 {
            let (header, body) = client.start_get("/any", "localhost").0.wait().expect("headers");
            assert_eq!(200, header.status());

            // TODO: check content
            body.collect().wait().expect("body");
        }

    })
}
<|endoftext|>