<commit_before>use mio::Token;
use futures::prelude::*;
use futures::executor::{Notify, Spawn, spawn};
use futures::task::{self, Task};
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use slab::Slab;
use std::collections::{HashSet, VecDeque};
use sozu_command::proxy::{ProxyRequest, ProxyResponse, ProxyResponseStatus};
use sozu_command::command::CommandResponse;
use super::FrontToken;

lazy_static! {
  static ref EXECUTOR: Arc<Executor> = {
    Arc::new(Executor {
      to_notify: Mutex::new(HashMap::new()),
      inner: Mutex::new(Runner::new()),
      messages: Mutex::new(HashMap::new()),
      worker_queue: Mutex::new(VecDeque::new()),
      client_queue: Mutex::new(VecDeque::new()),
      state_queue: Mutex::new(VecDeque::new()),
    })
  };
}

pub struct Executor {
  pub inner: Mutex<Runner>,
  pub to_notify: Mutex<HashMap<(Token, String, MessageStatus), Task>>,
  pub messages: Mutex<HashMap<(Token, String, MessageStatus), ProxyResponse>>,
  pub worker_queue: Mutex<VecDeque<(Token, ProxyRequest)>>,
  pub client_queue: Mutex<VecDeque<(FrontToken, CommandResponse)>>,
  pub state_queue: Mutex<VecDeque<StateChange>>,
}

pub struct Runner {
  pub ready: HashSet<usize>,
  pub tasks: Slab<Spawn<Box<Future<Item = (), Error = ()> + Send>>>,
}

#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub enum MessageStatus {
  Processing,
  Other,
}

#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub enum StateChange {
  StopWorker(Token),
  StopMain,
}

impl Runner {
  pub fn new() -> Runner {
    Runner {
      ready: HashSet::new(),
      tasks: Slab::with_capacity(10000),
    }
  }

  pub fn run(&mut self) {
    for id in self.ready.drain() {
      //println!("run({})", id);
      if self.tasks.contains(id) {
        //println!("tasks has id: {}", id);
        match self.tasks[id].poll_future_notify(&(EXECUTOR.clone()), id) {
          Err(e) => {
            error!("error executing future[{}]: {:?}", id, e);
            self.tasks.remove(id);
          },
          Ok(Async::Ready(())) => {
            trace!("finished executing future[{}]", id);
            self.tasks.remove(id);
          },
          Ok(Async::NotReady) => {
            //println!("not ready");
          },
        }
      }
    }

  }
}

impl Executor {
  pub fn register(worker: Token, message_id: &str, status: MessageStatus, task: Task) {
    //println!("register({:?}, {}, {:?})", worker, message_id, task);
    let mut to_notify = EXECUTOR.to_notify.lock().unwrap();
    to_notify.insert((worker, message_id.to_string(), status), task);
  }

  pub fn send_worker(worker: Token, message: ProxyRequest) {
    let mut queue = EXECUTOR.worker_queue.lock().unwrap();
    queue.push_back((worker, message));
  }

  pub fn get_worker_message() -> Option<(Token, ProxyRequest)> {
    let mut queue = EXECUTOR.worker_queue.lock().unwrap();
    queue.pop_front()
  }

  pub fn send_client(client: FrontToken, message: CommandResponse) {
    let mut queue = EXECUTOR.client_queue.lock().unwrap();
    queue.push_back((client, message));
  }

  pub fn get_client_message() -> Option<(FrontToken, CommandResponse)> {
    let mut queue = EXECUTOR.client_queue.lock().unwrap();
    queue.pop_front()
  }

  pub fn stop_worker(worker: Token) {
    let mut queue = EXECUTOR.state_queue.lock().unwrap();
    queue.push_back(StateChange::StopWorker(worker));
  }

  pub fn stop_main() {
    let mut queue = EXECUTOR.state_queue.lock().unwrap();
    queue.push_back(StateChange::StopMain);
  }

  pub fn get_state_change() -> Option<StateChange> {
    let mut queue = EXECUTOR.state_queue.lock().unwrap();
    queue.pop_front()
  }

  pub fn handle_message(worker: Token, message: ProxyResponse) {
    trace!("executor handle_message({:?}, {}, {:?})", worker, message.id, message);

    let status = match message.status {
      ProxyResponseStatus::Processing => MessageStatus::Processing,
      _ => MessageStatus::Other
    };

    let task = {
      let mut to_notify = EXECUTOR.to_notify.lock().unwrap();

      match to_notify.remove(&(worker, message.id.to_string(), status)) {
        None => {
          trace!("no task waiting for {}", message.id);
          None
        },
        Some(task) => {
          Some(task.clone())
        }
      }
    };

    {
      let mut messages = EXECUTOR.messages.lock().unwrap();
      trace!("inserting message({:?}, {}", worker, message.id);
      messages.insert((worker, message.id.to_string(), status), message);
    }

    if let Some(t) = task {
      t.notify();
    }
  }

  pub fn execute(s: impl Future<Item = (), Error = ()> + Send + 'static) {
    let mut inner = EXECUTOR.inner.lock().unwrap();
    if let Ok(id) = inner.tasks.insert(spawn(Box::new(s))) {
      inner.ready.insert(id);
    }
  }

  pub fn run() {
    let mut inner = EXECUTOR.inner.lock().unwrap();
    inner.run();
  }

  pub fn get_message(worker: Token, message_id: &str, status: MessageStatus) -> Option<ProxyResponse> {
    {
      let mut messages = EXECUTOR.messages.lock().unwrap();
      messages.remove(&(worker, message_id.to_string(), status))
    }
  }

  pub fn peek_message(worker: Token, message_id: &str, status: MessageStatus) -> bool {
    {
      let messages = EXECUTOR.messages.lock().unwrap();
      messages.contains_key(&(worker, message_id.to_string(), status))
    }
  }
}

impl Notify for Executor {
  fn notify(&self, id: usize) {
    let mut inner = self.inner.lock().unwrap();
    inner.ready.insert(id);
  }
}

pub struct FutureAnswer {
  worker_id: Token,
  message_id: String,
}

impl Future for FutureAnswer {
  type Item  = ProxyResponse;
  type Error = String;
  fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
    //FIXME: handle workers disconnected
    match Executor::get_message(self.worker_id, &self.message_id, MessageStatus::Other) {
      Some(message) => {
        match message.status {
          ProxyResponseStatus::Ok => Ok(Async::Ready(message)),
          ProxyResponseStatus::Error(_) => Ok(Async::Ready(message)),
          _ => panic!(),
        }
      },
      None => {
        Executor::register(self.worker_id, &self.message_id, MessageStatus::Other, task::current());
        Ok(Async::NotReady)
      }
    }
  }
}

pub struct FutureProcessing {
  worker_id: Token,
  message_id: String,
}

impl Stream for FutureProcessing {
  type Item  = ProxyResponse;
  type Error = ();
  fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
    //FIXME: handle workers disconnected
    if Executor::peek_message(self.worker_id, &self.message_id, MessageStatus::Other) {
      return Ok(Async::Ready(None));
    } else {
      Executor::register(self.worker_id, &self.message_id, MessageStatus::Other, task::current());
    }

    match Executor::get_message(self.worker_id, &self.message_id, MessageStatus::Processing) {
      Some(message) => {
        match message.status {
          ProxyResponseStatus::Processing => Ok(Async::Ready(Some(message))),
          _ => panic!(),
        }
      },
      None => {
        Executor::register(self.worker_id, &self.message_id, MessageStatus::Processing, task::current());
        Ok(Async::NotReady)
      }
    }
  }
}

pub fn send_processing(worker_id: Token, message: ProxyRequest) -> (FutureProcessing, FutureAnswer) {
  let message_id = message.id.to_string();
  Executor::send_worker(worker_id, message);
  (FutureProcessing {
    worker_id,
    message_id: message_id.clone()
  },

  FutureAnswer {
    worker_id,
    message_id
  })
}

pub fn send(worker_id: Token, message: ProxyRequest) -> FutureAnswer {
  let message_id = message.id.to_string();
  Executor::send_worker(worker_id, message);
  FutureAnswer {
    worker_id,
    message_id,
  }
}

#[cfg(test)]
mod tests {
  use mio::Token;
  use super::*;
  use futures::executor::spawn;
  use futures::task;
  use futures::future::{lazy, result};
  use sozu_command::proxy::{ProxyRequestData,ProxyResponseStatus};
  use sozu_command::command::CommandStatus;

  #[test]
  fn executor() {
    Executor::execute(lazy(||{
      let (processing, msg_future) = send_processing(Token(0), ProxyRequest { id: "test".to_string(), order: ProxyRequestData::Status });
      processing.for_each(|msg| {
        println!("TEST: got processing message: {:?}", msg);
        Ok(())
      }).join(msg_future.map(|msg| {
          println!("TEST: future got msg: {:?}", msg);
          Executor::send_client(FrontToken(1), CommandResponse::new(
            "test".to_string(),
            CommandStatus::Ok,
            "ok".to_string(),
            None
          ));
        }).map_err(|e| {
          println!("TEST: got error: {:?}", e);
        })
      ).map(|_| ())
    }));
    Executor::run();

    Executor::handle_message(Token(0), ProxyResponse{
      id: "test".to_string(),
      status: ProxyResponseStatus::Processing,
      data: None
    });

    Executor::run();

    Executor::handle_message(Token(0), ProxyResponse{
      id: "test".to_string(),
      status: ProxyResponseStatus::Ok,
      data: None
    });

    Executor::run();

    assert_eq!(
      Executor::get_client_message(),
      Some((FrontToken(1),
        CommandResponse::new(
          "test".to_string(),
          CommandStatus::Ok,
          "ok".to_string(),
          None
        )
      ))
    );
  }
}

<commit_msg>do not register the future more than once<commit_after>use mio::Token;
use futures::prelude::*;
use futures::executor::{Notify, Spawn, spawn};
use futures::task::{self, Task};
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use slab::Slab;
use std::collections::{HashSet, VecDeque};
use sozu_command::proxy::{ProxyRequest, ProxyResponse, ProxyResponseStatus};
use sozu_command::command::CommandResponse;
use super::FrontToken;

lazy_static! {
  static ref EXECUTOR: Arc<Executor> = {
    Arc::new(Executor {
      to_notify: Mutex::new(HashMap::new()),
      inner: Mutex::new(Runner::new()),
      messages: Mutex::new(HashMap::new()),
      worker_queue: Mutex::new(VecDeque::new()),
      client_queue: Mutex::new(VecDeque::new()),
      state_queue: Mutex::new(VecDeque::new()),
    })
  };
}

pub struct Executor {
  pub inner: Mutex<Runner>,
  pub to_notify: Mutex<HashMap<(Token, String, MessageStatus), Task>>,
  pub messages: Mutex<HashMap<(Token, String, MessageStatus), ProxyResponse>>,
  pub worker_queue: Mutex<VecDeque<(Token, ProxyRequest)>>,
  pub client_queue: Mutex<VecDeque<(FrontToken, CommandResponse)>>,
  pub state_queue: Mutex<VecDeque<StateChange>>,
}

pub struct Runner {
  pub ready: HashSet<usize>,
  pub tasks: Slab<Spawn<Box<Future<Item = (), Error = ()> + Send>>>,
}

#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub enum MessageStatus {
  Processing,
  Other,
}

#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub enum StateChange {
  StopWorker(Token),
  StopMain,
}

impl Runner {
  pub fn new() -> Runner {
    Runner {
      ready: HashSet::new(),
      tasks: Slab::with_capacity(10000),
    }
  }

  pub fn run(&mut self) {
    for id in self.ready.drain() {
      //println!("run({})", id);
      if self.tasks.contains(id) {
        //println!("tasks has id: {}", id);
        match self.tasks[id].poll_future_notify(&(EXECUTOR.clone()), id) {
          Err(e) => {
            error!("error executing future[{}]: {:?}", id, e);
            self.tasks.remove(id);
          },
          Ok(Async::Ready(())) => {
            trace!("finished executing future[{}]", id);
            self.tasks.remove(id);
          },
          Ok(Async::NotReady) => {
            //println!("not ready");
          },
        }
      }
    }

  }
}

impl Executor {
  pub fn register(worker: Token, message_id: &str, status: MessageStatus, task: Task) {
    //println!("register({:?}, {}, {:?})", worker, message_id, task);
    let mut to_notify = EXECUTOR.to_notify.lock().unwrap();
    to_notify.insert((worker, message_id.to_string(), status), task);
  }

  pub fn send_worker(worker: Token, message: ProxyRequest) {
    let mut queue = EXECUTOR.worker_queue.lock().unwrap();
    queue.push_back((worker, message));
  }

  pub fn get_worker_message() -> Option<(Token, ProxyRequest)> {
    let mut queue = EXECUTOR.worker_queue.lock().unwrap();
    queue.pop_front()
  }

  pub fn send_client(client: FrontToken, message: CommandResponse) {
    let mut queue = EXECUTOR.client_queue.lock().unwrap();
    queue.push_back((client, message));
  }

  pub fn get_client_message() -> Option<(FrontToken, CommandResponse)> {
    let mut queue = EXECUTOR.client_queue.lock().unwrap();
    queue.pop_front()
  }

  pub fn stop_worker(worker: Token) {
    let mut queue = EXECUTOR.state_queue.lock().unwrap();
    queue.push_back(StateChange::StopWorker(worker));
  }

  pub fn stop_main() {
    let mut queue = EXECUTOR.state_queue.lock().unwrap();
    queue.push_back(StateChange::StopMain);
  }

  pub fn get_state_change() -> Option<StateChange> {
    let mut queue = EXECUTOR.state_queue.lock().unwrap();
    queue.pop_front()
  }

  pub fn handle_message(worker: Token, message: ProxyResponse) {
    trace!("executor handle_message({:?}, {}, {:?})", worker, message.id, message);

    let status = match message.status {
      ProxyResponseStatus::Processing => MessageStatus::Processing,
      _ => MessageStatus::Other
    };

    let task = {
      let mut to_notify = EXECUTOR.to_notify.lock().unwrap();

      match to_notify.remove(&(worker, message.id.to_string(), status)) {
        None => {
          trace!("no task waiting for {}", message.id);
          None
        },
        Some(task) => {
          Some(task.clone())
        }
      }
    };

    {
      let mut messages = EXECUTOR.messages.lock().unwrap();
      trace!("inserting message({:?}, {}", worker, message.id);
      messages.insert((worker, message.id.to_string(), status), message);
    }

    if let Some(t) = task {
      t.notify();
    }
  }

  pub fn execute(s: impl Future<Item = (), Error = ()> + Send + 'static) {
    let mut inner = EXECUTOR.inner.lock().unwrap();
    if let Ok(id) = inner.tasks.insert(spawn(Box::new(s))) {
      inner.ready.insert(id);
    }
  }

  pub fn run() {
    let mut inner = EXECUTOR.inner.lock().unwrap();
    inner.run();
  }

  pub fn get_message(worker: Token, message_id: &str, status: MessageStatus) -> Option<ProxyResponse> {
    {
      let mut messages = EXECUTOR.messages.lock().unwrap();
      messages.remove(&(worker, message_id.to_string(), status))
    }
  }

  pub fn peek_message(worker: Token, message_id: &str, status: MessageStatus) -> bool {
    {
      let messages = EXECUTOR.messages.lock().unwrap();
      messages.contains_key(&(worker, message_id.to_string(), status))
    }
  }
}

impl Notify for Executor {
  fn notify(&self, id: usize) {
    let mut inner = self.inner.lock().unwrap();
    inner.ready.insert(id);
  }
}

pub struct FutureAnswer {
  registered: bool,
  worker_id: Token,
  message_id: String,
}

impl Future for FutureAnswer {
  type Item  = ProxyResponse;
  type Error = String;
  fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
    //FIXME: handle workers disconnected
    match Executor::get_message(self.worker_id, &self.message_id, MessageStatus::Other) {
      Some(message) => {
        match message.status {
          ProxyResponseStatus::Ok => Ok(Async::Ready(message)),
          ProxyResponseStatus::Error(_) => Ok(Async::Ready(message)),
          _ => panic!(),
        }
      },
      None => {
        if !self.registered {
          Executor::register(self.worker_id, &self.message_id, MessageStatus::Other, task::current());
          self.registered = true;
        }

        Ok(Async::NotReady)
      }
    }
  }
}

pub struct FutureProcessing {
  worker_id: Token,
  message_id: String,
}

impl Stream for FutureProcessing {
  type Item  = ProxyResponse;
  type Error = ();
  fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
    //FIXME: handle workers disconnected
    if Executor::peek_message(self.worker_id, &self.message_id, MessageStatus::Other) {
      return Ok(Async::Ready(None));
    } else {
      Executor::register(self.worker_id, &self.message_id, MessageStatus::Other, task::current());
    }

    match Executor::get_message(self.worker_id, &self.message_id, MessageStatus::Processing) {
      Some(message) => {
        match message.status {
          ProxyResponseStatus::Processing => Ok(Async::Ready(Some(message))),
          _ => panic!(),
        }
      },
      None => {
        Executor::register(self.worker_id, &self.message_id, MessageStatus::Processing, task::current());
        Ok(Async::NotReady)
      }
    }
  }
}

pub fn send_processing(worker_id: Token, message: ProxyRequest) -> (FutureProcessing, FutureAnswer) {
  let message_id = message.id.to_string();
  Executor::send_worker(worker_id, message);
  (FutureProcessing {
    worker_id,
    message_id: message_id.clone()
  },

  FutureAnswer {
    registered: false,
    worker_id,
    message_id
  })
}

pub fn send(worker_id: Token, message: ProxyRequest) -> FutureAnswer {
  let message_id = message.id.to_string();
  Executor::send_worker(worker_id, message);
  FutureAnswer {
    registered: false,
    worker_id,
    message_id,
  }
}

#[cfg(test)]
mod tests {
  use mio::Token;
  use super::*;
  use futures::executor::spawn;
  use futures::task;
  use futures::future::{lazy, result};
  use sozu_command::proxy::{ProxyRequestData,ProxyResponseStatus};
  use sozu_command::command::CommandStatus;

  #[test]
  fn executor() {
    Executor::execute(lazy(||{
      let (processing, msg_future) = send_processing(Token(0), ProxyRequest { id: "test".to_string(), order: ProxyRequestData::Status });
      processing.for_each(|msg| {
        println!("TEST: got processing message: {:?}", msg);
        Ok(())
      }).join(msg_future.map(|msg| {
          println!("TEST: future got msg: {:?}", msg);
          Executor::send_client(FrontToken(1), CommandResponse::new(
            "test".to_string(),
            CommandStatus::Ok,
            "ok".to_string(),
            None
          ));
        }).map_err(|e| {
          println!("TEST: got error: {:?}", e);
        })
      ).map(|_| ())
    }));
    Executor::run();

    Executor::handle_message(Token(0), ProxyResponse{
      id: "test".to_string(),
      status: ProxyResponseStatus::Processing,
      data: None
    });

    Executor::run();

    Executor::handle_message(Token(0), ProxyResponse{
      id: "test".to_string(),
      status: ProxyResponseStatus::Ok,
      data: None
    });

    Executor::run();

    assert_eq!(
      Executor::get_client_message(),
      Some((FrontToken(1),
        CommandResponse::new(
          "test".to_string(),
          CommandStatus::Ok,
          "ok".to_string(),
          None
        )
      ))
    );
  }
}

<|endoftext|>


<commit_before>extern crate c_ares_sys;
extern crate libc;

use std::ffi::CString;
use std::mem;
use std::os::unix::io;
use std::ptr;

use callbacks::{
    socket_callback,
    query_a_callback,
    query_aaaa_callback,
};
use types::{
    Flag,
    AresError,
    AResult,
    AAAAResult,
    DnsClass,
    QueryType,
};
use utils::ares_error;

/// Used to configure the behaviour of the name resolver.
pub struct Options {
    ares_options: c_ares_sys::Struct_ares_options,
    optmask: libc::c_int,
    domains: Vec<CString>,
    lookups: Option<CString>,
}

impl Options {
    /// Returns a fresh `Options`, on which no values are set.
    pub fn new() -> Options {
        Options {
            ares_options: c_ares_sys::Struct_ares_options::default(),
            optmask: 0,
            domains: Vec::new(),
            lookups: None,
        }
    }

    /// Set flags controlling the behaviour of the resolver.
    pub fn set_flags(&mut self, flags: &[Flag]) -> &mut Self {
        let c_flags: libc::c_int = flags
            .iter()
            .fold(0, |acc, &flag| acc | (flag as libc::c_int));
        self.ares_options.flags = c_flags;
        self.optmask = self.optmask | c_ares_sys::ARES_OPT_FLAGS;
        self
    }

    /// Set the number of milliseconds each name server is given to respond to
    /// a query on the first try.  (After the first try, the timeout algorithm
    /// becomes more complicated, but scales linearly with the value of
    /// timeout.) The default is five seconds.
    pub fn set_timeout(&mut self, ms: u32) -> &mut Self {
        self.ares_options.timeout = ms as libc::c_int;
        self.optmask = self.optmask | c_ares_sys::ARES_OPT_TIMEOUTMS;
        self
    }

    /// Set the number of tries the resolver will try contacting each name
    /// server before giving up. The default is four tries.
    pub fn set_tries(&mut self, tries: u32) -> &mut Self {
        self.ares_options.tries = tries as libc::c_int;
        self.optmask = self.optmask | c_ares_sys::ARES_OPT_TRIES;
        self
    }

    /// Set the number of dots which must be present in a domain name for it to
    /// be queried for "as is" prior to querying for it with the default domain
    /// extensions appended. The default value is 1 unless set otherwise by
    /// resolv.conf or the RES_OPTIONS environment variable.
    pub fn set_ndots(&mut self, ndots: u32) -> &mut Self {
        self.ares_options.ndots = ndots as libc::c_int;
        self.optmask = self.optmask | c_ares_sys::ARES_OPT_NDOTS;
        self
    }

    /// Set the UDP port to use for queries. The default value is 53, the
    /// standard name service port.
    pub fn set_udp_port(&mut self, udp_port: u16) -> &mut Self {
        self.ares_options.udp_port = udp_port as libc::c_ushort;
        self.optmask = self.optmask | c_ares_sys::ARES_OPT_UDP_PORT;
        self
    }

    /// Set the TCP port to use for queries. The default value is 53, the
    /// standard name service port.
    pub fn set_tcp_port(&mut self, tcp_port: u16) -> &mut Self {
        self.ares_options.tcp_port = tcp_port as libc::c_ushort;
        self.optmask = self.optmask | c_ares_sys::ARES_OPT_TCP_PORT;
        self
    }

    /// Set the domains to search, instead of the domains specified in
    /// resolv.conf or the domain derived from the kernel hostname variable.
    pub fn set_domains(&mut self, domains: &[&str]) -> &mut Self {
        self.domains = domains
            .iter()
            .map(|&s| CString::new(s).unwrap())
            .collect();
        self.optmask = self.optmask | c_ares_sys::ARES_OPT_DOMAINS;
        self
    }

    /// Set the lookups to perform for host queries. lookups should be set to a
    /// string of the characters "b" or "f", where "b" indicates a DNS lookup
    /// and "f" indicates a lookup in the hosts file.
    pub fn set_lookups(&mut self, lookups: &str) -> &mut Self {
        let c_lookups = CString::new(lookups).unwrap();
        self.lookups = Some(c_lookups);
        self.optmask = self.optmask | c_ares_sys::ARES_OPT_LOOKUPS;
        self
    }

    /// Set the socket send buffer size.
    pub fn set_sock_send_buffer_size(&mut self, size: u32) -> &mut Self {
        self.ares_options.socket_send_buffer_size = size as libc::c_int;
        self.optmask = self.optmask | c_ares_sys::ARES_OPT_SOCK_SNDBUF;
        self
    }

    /// Set the socket receive buffer size.
    pub fn set_sock_receive_buffer_size(&mut self, size: u32) -> &mut Self {
        self.ares_options.socket_receive_buffer_size = size as libc::c_int;
        self.optmask = self.optmask | c_ares_sys::ARES_OPT_SOCK_RCVBUF;
        self
    }

    /// Set the EDNS packet size.
    pub fn set_ednspsz(&mut self, size: u32) -> &mut Self {
        self.ares_options.ednspsz = size as libc::c_int;
        self.optmask = self.optmask | c_ares_sys::ARES_OPT_EDNSPSZ;
        self
    }
}

/// A channel for name service lookups.
pub struct Channel {
    ares_channel: c_ares_sys::ares_channel,
}

impl Channel {
    /// Create a new channel for name service lookups, providing a callback
    /// for socket state changes.
    ///
    /// `callback(socket, read, write)` will be called when a socket changes
    /// state:
    ///
    /// -  `read` is set to true if the socket should listen for read events
    /// -  `write` is set to true if the socket should listen to write events.
    pub fn new<F>(
        callback: F,
        mut options: Options) -> Result<Channel, AresError>
        where F: FnMut(io::RawFd, bool, bool) + 'static {
        options.optmask = options.optmask | c_ares_sys::ARES_OPT_SOCK_STATE_CB;
        options.ares_options.sock_state_cb = Some(socket_callback::<F>);
        options.ares_options.sock_state_cb_data = unsafe {
            mem::transmute(Box::new(callback))
        };
        Self::create_channel(options)
    }

    fn create_channel(mut options: Options) -> Result<Channel, AresError> {
        // Initialize the library.
        let lib_rc = unsafe {
            c_ares_sys::ares_library_init(c_ares_sys::ARES_LIB_INIT_ALL)
        };
        if lib_rc != c_ares_sys::ARES_SUCCESS {
            return Err(ares_error(lib_rc))
        }

        // We deferred setting up domains in the options - do it now.
        let domains: Vec<_> = options.domains
            .iter()
            .map(|s| s.as_ptr())
            .collect();
        options.ares_options.domains =
            domains.as_ptr() as *mut *mut libc::c_char;
        options.ares_options.ndomains = domains.len() as libc::c_int;

        // Likewise for lookups.
        for c_lookup in options.lookups.iter() {
            options.ares_options.lookups =
                c_lookup.as_ptr() as *mut libc::c_char;
        }

        // Initialize the channel.
        let mut ares_channel = ptr::null_mut();
        let channel_rc = unsafe {
            c_ares_sys::ares_init_options(
                &mut ares_channel,
                &mut options.ares_options,
                options.optmask)
        };
        if channel_rc != c_ares_sys::ARES_SUCCESS {
            unsafe { c_ares_sys::ares_library_cleanup(); }
            return Err(ares_error(channel_rc))
        }

        let channel = Channel {
            ares_channel: ares_channel,
        };
        Ok(channel)
    }

    /// Handle input, output, and timeout events associated with the specified
    /// file descriptors (sockets).
    ///
    /// Providing a value for `read_fd` indicates that the identified socket
    /// is readable; likewise providing a value for `write_fd` indicates that
    /// the identified socket is writable.  Use `INVALID_FD` for "no action".
    pub fn process_fd(&mut self, read_fd: io::RawFd, write_fd: io::RawFd) {
        unsafe {
            c_ares_sys::ares_process_fd(
                self.ares_channel,
                read_fd as c_ares_sys::ares_socket_t,
                write_fd as c_ares_sys::ares_socket_t);
        }
    }

    /// Set the list of servers to contact, instead of the servers specified
    /// in resolv.conf or the local named.
    ///
    /// String format is `host[:port]`.  IPv6 addresses with ports require
    /// square brackets eg `[2001:4860:4860::8888]:53`.
    pub fn set_servers(&mut self, servers: &[&str]) -> Result<(), AresError> {
        let servers_csv = servers.connect(",");
        let c_servers = CString::new(servers_csv).unwrap();
        let ares_rc = unsafe {
            c_ares_sys::ares_set_servers_csv(
                self.ares_channel,
                c_servers.as_ptr())
        };
        if ares_rc != c_ares_sys::ARES_SUCCESS {
            Err(ares_error(ares_rc))
        } else {
            Ok(())
        }
    }

    /// Look up the A record associated with `name`.
    ///
    /// On completion, `handler` is called with the result.
    pub fn query_a<F>(&mut self, name: &str, handler: F)
        where F: FnOnce(Result<AResult, AresError>) + 'static {
        let c_name = CString::new(name).unwrap();
        unsafe {
            let c_arg: *mut libc::c_void = mem::transmute(Box::new(handler));
            c_ares_sys::ares_query(
                self.ares_channel,
                c_name.as_ptr(),
                DnsClass::IN as libc::c_int,
                QueryType::A as libc::c_int,
                Some(query_a_callback::<F>),
                c_arg);
        }
    }

    /// Look up the AAAA record associated with `name`.
    ///
    /// On completion, `handler` is called with the result.
    pub fn query_aaaa<F>(&mut self, name: &str, handler: F)
        where F: FnOnce(Result<AAAAResult, AresError>) + 'static {
        let c_name = CString::new(name).unwrap();
        unsafe {
            let c_arg: *mut libc::c_void = mem::transmute(Box::new(handler));
            c_ares_sys::ares_query(
                self.ares_channel,
                c_name.as_ptr(),
                DnsClass::IN as libc::c_int,
                QueryType::AAAA as libc::c_int,
                Some(query_aaaa_callback::<F>),
                c_arg);
        }
    }
}

impl Drop for Channel {
    fn drop(&mut self) {
        unsafe {
            c_ares_sys::ares_destroy(self.ares_channel);
            c_ares_sys::ares_library_cleanup();
        }
    }
}

unsafe impl Send for Channel { }
<commit_msg>Docs formatting<commit_after>extern crate c_ares_sys;
extern crate libc;

use std::ffi::CString;
use std::mem;
use std::os::unix::io;
use std::ptr;

use callbacks::{
    socket_callback,
    query_a_callback,
    query_aaaa_callback,
};
use types::{
    Flag,
    AresError,
    AResult,
    AAAAResult,
    DnsClass,
    QueryType,
};
use utils::ares_error;

/// Used to configure the behaviour of the name resolver.
pub struct Options {
    ares_options: c_ares_sys::Struct_ares_options,
    optmask: libc::c_int,
    domains: Vec<CString>,
    lookups: Option<CString>,
}

impl Options {
    /// Returns a fresh `Options`, on which no values are set.
    pub fn new() -> Options {
        Options {
            ares_options: c_ares_sys::Struct_ares_options::default(),
            optmask: 0,
            domains: Vec::new(),
            lookups: None,
        }
    }

    /// Set flags controlling the behaviour of the resolver.
    pub fn set_flags(&mut self, flags: &[Flag]) -> &mut Self {
        let c_flags: libc::c_int = flags
            .iter()
            .fold(0, |acc, &flag| acc | (flag as libc::c_int));
        self.ares_options.flags = c_flags;
        self.optmask = self.optmask | c_ares_sys::ARES_OPT_FLAGS;
        self
    }

    /// Set the number of milliseconds each name server is given to respond to
    /// a query on the first try.  (After the first try, the timeout algorithm
    /// becomes more complicated, but scales linearly with the value of
    /// timeout.) The default is five seconds.
    pub fn set_timeout(&mut self, ms: u32) -> &mut Self {
        self.ares_options.timeout = ms as libc::c_int;
        self.optmask = self.optmask | c_ares_sys::ARES_OPT_TIMEOUTMS;
        self
    }

    /// Set the number of tries the resolver will try contacting each name
    /// server before giving up. The default is four tries.
    pub fn set_tries(&mut self, tries: u32) -> &mut Self {
        self.ares_options.tries = tries as libc::c_int;
        self.optmask = self.optmask | c_ares_sys::ARES_OPT_TRIES;
        self
    }

    /// Set the number of dots which must be present in a domain name for it to
    /// be queried for "as is" prior to querying for it with the default domain
    /// extensions appended. The default value is 1 unless set otherwise by
    /// resolv.conf or the RES_OPTIONS environment variable.
    pub fn set_ndots(&mut self, ndots: u32) -> &mut Self {
        self.ares_options.ndots = ndots as libc::c_int;
        self.optmask = self.optmask | c_ares_sys::ARES_OPT_NDOTS;
        self
    }

    /// Set the UDP port to use for queries. The default value is 53, the
    /// standard name service port.
    pub fn set_udp_port(&mut self, udp_port: u16) -> &mut Self {
        self.ares_options.udp_port = udp_port as libc::c_ushort;
        self.optmask = self.optmask | c_ares_sys::ARES_OPT_UDP_PORT;
        self
    }

    /// Set the TCP port to use for queries. The default value is 53, the
    /// standard name service port.
    pub fn set_tcp_port(&mut self, tcp_port: u16) -> &mut Self {
        self.ares_options.tcp_port = tcp_port as libc::c_ushort;
        self.optmask = self.optmask | c_ares_sys::ARES_OPT_TCP_PORT;
        self
    }

    /// Set the domains to search, instead of the domains specified in
    /// resolv.conf or the domain derived from the kernel hostname variable.
    pub fn set_domains(&mut self, domains: &[&str]) -> &mut Self {
        self.domains = domains
            .iter()
            .map(|&s| CString::new(s).unwrap())
            .collect();
        self.optmask = self.optmask | c_ares_sys::ARES_OPT_DOMAINS;
        self
    }

    /// Set the lookups to perform for host queries. `lookups` should be set to
    /// a string of the characters "b" or "f", where "b" indicates a DNS lookup
    /// and "f" indicates a lookup in the hosts file.
    pub fn set_lookups(&mut self, lookups: &str) -> &mut Self {
        let c_lookups = CString::new(lookups).unwrap();
        self.lookups = Some(c_lookups);
        self.optmask = self.optmask | c_ares_sys::ARES_OPT_LOOKUPS;
        self
    }

    /// Set the socket send buffer size.
    pub fn set_sock_send_buffer_size(&mut self, size: u32) -> &mut Self {
        self.ares_options.socket_send_buffer_size = size as libc::c_int;
        self.optmask = self.optmask | c_ares_sys::ARES_OPT_SOCK_SNDBUF;
        self
    }

    /// Set the socket receive buffer size.
    pub fn set_sock_receive_buffer_size(&mut self, size: u32) -> &mut Self {
        self.ares_options.socket_receive_buffer_size = size as libc::c_int;
        self.optmask = self.optmask | c_ares_sys::ARES_OPT_SOCK_RCVBUF;
        self
    }

    /// Set the EDNS packet size.
    pub fn set_ednspsz(&mut self, size: u32) -> &mut Self {
        self.ares_options.ednspsz = size as libc::c_int;
        self.optmask = self.optmask | c_ares_sys::ARES_OPT_EDNSPSZ;
        self
    }
}

/// A channel for name service lookups.
pub struct Channel {
    ares_channel: c_ares_sys::ares_channel,
}

impl Channel {
    /// Create a new channel for name service lookups, providing a callback
    /// for socket state changes.
    ///
    /// `callback(socket, read, write)` will be called when a socket changes
    /// state:
    ///
    /// -  `read` is set to true if the socket should listen for read events
    /// -  `write` is set to true if the socket should listen to write events.
    pub fn new<F>(
        callback: F,
        mut options: Options) -> Result<Channel, AresError>
        where F: FnMut(io::RawFd, bool, bool) + 'static {
        options.optmask = options.optmask | c_ares_sys::ARES_OPT_SOCK_STATE_CB;
        options.ares_options.sock_state_cb = Some(socket_callback::<F>);
        options.ares_options.sock_state_cb_data = unsafe {
            mem::transmute(Box::new(callback))
        };
        Self::create_channel(options)
    }

    fn create_channel(mut options: Options) -> Result<Channel, AresError> {
        // Initialize the library.
        let lib_rc = unsafe {
            c_ares_sys::ares_library_init(c_ares_sys::ARES_LIB_INIT_ALL)
        };
        if lib_rc != c_ares_sys::ARES_SUCCESS {
            return Err(ares_error(lib_rc))
        }

        // We deferred setting up domains in the options - do it now.
        let domains: Vec<_> = options.domains
            .iter()
            .map(|s| s.as_ptr())
            .collect();
        options.ares_options.domains =
            domains.as_ptr() as *mut *mut libc::c_char;
        options.ares_options.ndomains = domains.len() as libc::c_int;

        // Likewise for lookups.
        for c_lookup in options.lookups.iter() {
            options.ares_options.lookups =
                c_lookup.as_ptr() as *mut libc::c_char;
        }

        // Initialize the channel.
        let mut ares_channel = ptr::null_mut();
        let channel_rc = unsafe {
            c_ares_sys::ares_init_options(
                &mut ares_channel,
                &mut options.ares_options,
                options.optmask)
        };
        if channel_rc != c_ares_sys::ARES_SUCCESS {
            unsafe { c_ares_sys::ares_library_cleanup(); }
            return Err(ares_error(channel_rc))
        }

        let channel = Channel {
            ares_channel: ares_channel,
        };
        Ok(channel)
    }

    /// Handle input, output, and timeout events associated with the specified
    /// file descriptors (sockets).
    ///
    /// Providing a value for `read_fd` indicates that the identified socket
    /// is readable; likewise providing a value for `write_fd` indicates that
    /// the identified socket is writable.  Use `INVALID_FD` for "no action".
    pub fn process_fd(&mut self, read_fd: io::RawFd, write_fd: io::RawFd) {
        unsafe {
            c_ares_sys::ares_process_fd(
                self.ares_channel,
                read_fd as c_ares_sys::ares_socket_t,
                write_fd as c_ares_sys::ares_socket_t);
        }
    }

    /// Set the list of servers to contact, instead of the servers specified
    /// in resolv.conf or the local named.
    ///
    /// String format is `host[:port]`.  IPv6 addresses with ports require
    /// square brackets eg `[2001:4860:4860::8888]:53`.
    pub fn set_servers(&mut self, servers: &[&str]) -> Result<(), AresError> {
        let servers_csv = servers.connect(",");
        let c_servers = CString::new(servers_csv).unwrap();
        let ares_rc = unsafe {
            c_ares_sys::ares_set_servers_csv(
                self.ares_channel,
                c_servers.as_ptr())
        };
        if ares_rc != c_ares_sys::ARES_SUCCESS {
            Err(ares_error(ares_rc))
        } else {
            Ok(())
        }
    }

    /// Look up the A record associated with `name`.
    ///
    /// On completion, `handler` is called with the result.
    pub fn query_a<F>(&mut self, name: &str, handler: F)
        where F: FnOnce(Result<AResult, AresError>) + 'static {
        let c_name = CString::new(name).unwrap();
        unsafe {
            let c_arg: *mut libc::c_void = mem::transmute(Box::new(handler));
            c_ares_sys::ares_query(
                self.ares_channel,
                c_name.as_ptr(),
                DnsClass::IN as libc::c_int,
                QueryType::A as libc::c_int,
                Some(query_a_callback::<F>),
                c_arg);
        }
    }

    /// Look up the AAAA record associated with `name`.
    ///
    /// On completion, `handler` is called with the result.
    pub fn query_aaaa<F>(&mut self, name: &str, handler: F)
        where F: FnOnce(Result<AAAAResult, AresError>) + 'static {
        let c_name = CString::new(name).unwrap();
        unsafe {
            let c_arg: *mut libc::c_void = mem::transmute(Box::new(handler));
            c_ares_sys::ares_query(
                self.ares_channel,
                c_name.as_ptr(),
                DnsClass::IN as libc::c_int,
                QueryType::AAAA as libc::c_int,
                Some(query_aaaa_callback::<F>),
                c_arg);
        }
    }
}

impl Drop for Channel {
    fn drop(&mut self) {
        unsafe {
            c_ares_sys::ares_destroy(self.ares_channel);
            c_ares_sys::ares_library_cleanup();
        }
    }
}

unsafe impl Send for Channel { }
<|endoftext|>


<commit_before>use regex::Regex;
use std::sync::mpsc::channel;

use adapter::ChatAdapter;
use handler::MessageHandler;

/// The Chatbot is the central data structure of the chatbot platform. It contains a `run` method
/// which listens for messages from adapters and routes them to handlers. Any program which uses
/// chatbot will need to minimally create a Chatbot, add an adapter, add a handler, and call Chatbot
/// [`run`](chatbot/struct.Chatbot.html#method.run).
pub struct Chatbot {
    name: String,
    adapters: Vec<Box<ChatAdapter>>,
    handlers: Vec<Box<MessageHandler>>,
    addressed_handlers: Vec<Box<MessageHandler>>,
    addresser: Regex
}

impl Chatbot {
    /// Create a new chatbot instance.
    ///
    /// The name provided here will be used for message filtering and in handlers for whatever they
    /// want. You'll want to make your binding mutable so you can call `add_adapter` and
    /// `add_handler`.
    pub fn new(name: &str) -> Chatbot {
        let addresser_str = format!(r"^\s*@?{}[:,\s]\s*", name);
        let addresser = Regex::new(addresser_str.as_ref());

        Chatbot {
            name: name.to_owned(),
            adapters: Vec::new(),
            handlers: Vec::new(),
            addressed_handlers: Vec::new(),
            addresser: addresser.unwrap()
        }
    }

    /// Return the name provided in initialization
    pub fn get_name(&self) -> &str {
        self.name.as_ref()
    }

    /// Return the regular expression of what the bot will be addressed by
    pub fn get_addresser(&self) -> &Regex {
        &self.addresser
    }

    /// Add a ChatAdapter to the bot
    ///
    /// Add as many adapters as you like. The IncomingMessages sent by adapters are made available
    /// to all handlers regardless of how many adapters exist. The IncomingMessage.reply method
    /// makes sure the response is sent back to the adapter from whence the message came.
    pub fn add_adapter<T>(&mut self, adapter: T)
        where T: ChatAdapter + 'static
    {
        println!("Adding adapter {}", adapter.get_name());
        self.adapters.push(Box::new(adapter))
    }

    /// Add a MessageHandler to the bot
    ///
    /// The more handlers you have the more useful your bot becomes (for potentially loose
    /// definitions of useful :P). Check out the handler! macro for making simple handlers and also
    /// see the `MessageHandler` implementors section for a list of built in handlers.
    pub fn add_handler<T>(&mut self, handler: T)
        where T: MessageHandler + 'static
    {
        println!("Adding handler {}", handler.name());
        self.handlers.push(Box::new(handler))
    }

    /// Add a MessageHandler, that requires the bot to be addressed, to the bot
    pub fn add_addressed_handler<T>(&mut self, handler: T)
        where T: MessageHandler + 'static
    {
        println!("Adding handler {}", handler.name());
        self.addressed_handlers.push(Box::new(handler))
    }

    /// Start processing messages
    ///
    /// Call process_events on all of the adapters and `recv` on the `IncomingMessage` channel.
    /// Distribute IncomingMessages to list of handlers.
    pub fn run(&self) {

        assert!(self.adapters.len() > 0);
        assert!(self.handlers.len() > 0);

        println!("Chatbot: {} adapters", self.adapters.len());
        println!("Chatbot: {} handlers", self.handlers.len());

        let (incoming_tx, incoming_rx) = channel();

        for adapter in &self.adapters {
            adapter.process_events(self, incoming_tx.clone());
        }

        loop {
            // Get message from adapter
            let msg = match incoming_rx.recv() {
                Ok(msg) => msg,
                Err(_) => break
            };
            let msg_str = msg.get_contents();

            let handlers = if self.addresser.is_match(msg_str) {
                &self.addressed_handlers
            } else {
                &self.handlers
            };

            // Distribute to handlers
            for handler in handlers {
                if handler.can_handle(msg_str) {
                    match handler.handle(&msg) {
                        Err(e) => {
                            println!("Error in handler `{}`", handler.name());
                            println!("{:?}", e);
                            println!("The incoming message was {}", msg_str);

                            // TODO remove handler?
                        },
                        _ => ()
                    }
                }
            }
        }

        println!("chatbot shutting down");
    }
}

#[cfg(test)]
mod tests {
    use chatbot::Chatbot;
    use adapter::CliAdapter;

    #[test]
    fn test_create_chatbot() {
        let name = "testbot";
        let bot = Chatbot::new("testbot");
        assert_eq!(bot.get_name(), name);
    }

    #[test]
    fn test_chatbot_add_adapter() {
        let mut bot = Chatbot::new("testbot");
        let cli = CliAdapter::new();
        bot.add_adapter(cli);
    }

    #[test]
    fn test_chatbot_add_handler() {
        let mut bot = Chatbot::new("testbot");
        let echo = handler!("EchoHandler", r"echo .+", |_, msg| {
            Some(msg.to_owned())
        });
        bot.add_handler(echo);
    }

    #[test]
    fn test_chatbot_address_matches() {
        let bot = Chatbot::new("testbot");
        let addresser = bot.get_addresser();

        assert!(!addresser.is_match("testbotping"),
                "Shouldn't match bot name without a break (space, colon, comma)");
        assert!(!addresser.is_match("@testbotping"),
                "Shouldn't match bot name with '@' sign minus a break (space, colon, comma)");

        // Space separation
        assert!(addresser.is_match("testbot ping"), "Should match bot name with space");
        assert!(addresser.is_match("@testbot ping"),
                "Should match bot name with '@' sign plus '@' space");

        // Colon separation
        assert!(addresser.is_match("testbot:ping"), "Should match bot name with colon");
        assert!(addresser.is_match("testbot: ping"), "Should match bot name with colon and space");
        assert!(addresser.is_match("@testbot:ping"),
                "Should match bot name with '@' sign plus colon");
        assert!(addresser.is_match("@testbot: ping"),
                "Should match bot name with '@' sign plus colon and space");

        // Comma separation
        assert!(addresser.is_match("testbot,ping"), "Should match bot name with comma");
        assert!(addresser.is_match("testbot, ping"), "Should match bot name with comma and space");
        assert!(addresser.is_match("@testbot,ping"),
                "Should match bot name with '@' sign plus comma");
        assert!(addresser.is_match("@testbot, ping"),
                "Should match bot name with '@' sign plus comma and space");
    }
}
<commit_msg>Pull out dispatcher into a seperate function<commit_after>use regex::Regex;
use std::sync::mpsc::channel;

use adapter::ChatAdapter;
use handler::MessageHandler;
use message::IncomingMessage;

fn dispatch(handlers: &Vec<Box<MessageHandler>>, msg: &IncomingMessage) {
    let msg_str = msg.get_contents();

    for handler in handlers {
        if handler.can_handle(msg_str) {
            match handler.handle(msg) {
                Err(e) => {
                    println!("Error in handler `{}`", handler.name());
                    println!("{:?}", e);
                    println!("The incoming message was {}", msg_str);

                    // TODO remove handler?
                },
                _ => ()
            }
        }
    }
}

/// The Chatbot is the central data structure of the chatbot platform. It contains a `run` method
/// which listens for messages from adapters and routes them to handlers. Any program which uses
/// chatbot will need to minimally create a Chatbot, add an adapter, add a handler, and call Chatbot
/// [`run`](chatbot/struct.Chatbot.html#method.run).
pub struct Chatbot {
    name: String,
    adapters: Vec<Box<ChatAdapter>>,
    handlers: Vec<Box<MessageHandler>>,
    addressed_handlers: Vec<Box<MessageHandler>>,
    addresser: Regex
}

impl Chatbot {
    /// Create a new chatbot instance.
    ///
    /// The name provided here will be used for message filtering and in handlers for whatever they
    /// want. You'll want to make your binding mutable so you can call `add_adapter` and
    /// `add_handler`.
    pub fn new(name: &str) -> Chatbot {
        let addresser_str = format!(r"^\s*@?{}[:,\s]\s*", name);
        let addresser = Regex::new(addresser_str.as_ref());

        Chatbot {
            name: name.to_owned(),
            adapters: Vec::new(),
            handlers: Vec::new(),
            addressed_handlers: Vec::new(),
            addresser: addresser.unwrap()
        }
    }

    /// Return the name provided in initialization
    pub fn get_name(&self) -> &str {
        self.name.as_ref()
    }

    /// Return the regular expression of what the bot will be addressed by
    pub fn get_addresser(&self) -> &Regex {
        &self.addresser
    }

    /// Add a ChatAdapter to the bot
    ///
    /// Add as many adapters as you like. The IncomingMessages sent by adapters are made available
    /// to all handlers regardless of how many adapters exist. The IncomingMessage.reply method
    /// makes sure the response is sent back to the adapter from whence the message came.
    pub fn add_adapter<T>(&mut self, adapter: T)
        where T: ChatAdapter + 'static
    {
        println!("Adding adapter {}", adapter.get_name());
        self.adapters.push(Box::new(adapter))
    }

    /// Add a MessageHandler to the bot
    ///
    /// The more handlers you have the more useful your bot becomes (for potentially loose
    /// definitions of useful :P). Check out the handler! macro for making simple handlers and also
    /// see the `MessageHandler` implementors section for a list of built in handlers.
    pub fn add_handler<T>(&mut self, handler: T)
        where T: MessageHandler + 'static
    {
        println!("Adding handler {}", handler.name());
        self.handlers.push(Box::new(handler))
    }

    /// Add a MessageHandler, that requires the bot to be addressed, to the bot
    pub fn add_addressed_handler<T>(&mut self, handler: T)
        where T: MessageHandler + 'static
    {
        println!("Adding handler {}", handler.name());
        self.addressed_handlers.push(Box::new(handler))
    }

    /// Start processing messages
    ///
    /// Call process_events on all of the adapters and `recv` on the `IncomingMessage` channel.
    /// Distribute IncomingMessages to list of handlers.
    pub fn run(&self) {

        assert!(self.adapters.len() > 0);
        assert!(self.handlers.len() > 0);

        println!("Chatbot: {} adapters", self.adapters.len());
        println!("Chatbot: {} handlers", self.handlers.len());

        let (incoming_tx, incoming_rx) = channel();

        for adapter in &self.adapters {
            adapter.process_events(self, incoming_tx.clone());
        }

        loop {
            // Get message from adapter
            let msg = match incoming_rx.recv() {
                Ok(msg) => msg,
                Err(_) => break
            };

            // Only dispatch to addressed handlers when bot is addressed
            if self.addresser.is_match(msg.get_contents()) {
                dispatch(&self.addressed_handlers, &msg);
            }

            // Always dispatch to global handlers
            dispatch(&self.handlers, &msg);
        }

        println!("chatbot shutting down");
    }
}

#[cfg(test)]
mod tests {
    use chatbot::Chatbot;
    use adapter::CliAdapter;

    #[test]
    fn test_create_chatbot() {
        let name = "testbot";
        let bot = Chatbot::new("testbot");
        assert_eq!(bot.get_name(), name);
    }

    #[test]
    fn test_chatbot_add_adapter() {
        let mut bot = Chatbot::new("testbot");
        let cli = CliAdapter::new();
        bot.add_adapter(cli);
    }

    #[test]
    fn test_chatbot_add_handler() {
        let mut bot = Chatbot::new("testbot");
        let echo = handler!("EchoHandler", r"echo .+", |_, msg| {
            Some(msg.to_owned())
        });
        bot.add_handler(echo);
    }

    #[test]
    fn test_chatbot_address_matches() {
        let bot = Chatbot::new("testbot");
        let addresser = bot.get_addresser();

        assert!(!addresser.is_match("testbotping"),
                "Shouldn't match bot name without a break (space, colon, comma)");
        assert!(!addresser.is_match("@testbotping"),
                "Shouldn't match bot name with '@' sign minus a break (space, colon, comma)");

        // Space separation
        assert!(addresser.is_match("testbot ping"), "Should match bot name with space");
        assert!(addresser.is_match("@testbot ping"),
                "Should match bot name with '@' sign plus '@' space");

        // Colon separation
        assert!(addresser.is_match("testbot:ping"), "Should match bot name with colon");
        assert!(addresser.is_match("testbot: ping"), "Should match bot name with colon and space");
        assert!(addresser.is_match("@testbot:ping"),
                "Should match bot name with '@' sign plus colon");
        assert!(addresser.is_match("@testbot: ping"),
                "Should match bot name with '@' sign plus colon and space");

        // Comma separation
        assert!(addresser.is_match("testbot,ping"), "Should match bot name with comma");
        assert!(addresser.is_match("testbot, ping"), "Should match bot name with comma and space");
        assert!(addresser.is_match("@testbot,ping"),
                "Should match bot name with '@' sign plus comma");
        assert!(addresser.is_match("@testbot, ping"),
                "Should match bot name with '@' sign plus comma and space");
    }
}
<|endoftext|>


<commit_before>use std::{thread, time};

/// Chipate Module
/// Rust emulation of the Chip-8
/// http://www.multigesturearticles/how-to-write-an-emulator-chip-8-interpreter/
#[allow(dead_code)]
pub struct Chipate {
    // Opcodes
    opcode: u16,

    // 4K Memory
    memory: [u8; 4096],
    // The systems memory map:
    // 0x000-0x1FF - Chip 8 interpreter (contains font set in emu)
    // 0x050-0x0A0 - Used for the built in 4x5 pixel font set (0-F)
    // 0x200-0xFFF - Program ROM and work RAM

    // Registers
    v: [u8; 16],

    // Index
    i: u8,

    // Program Counter
    pc: u8,

    gfx: [u8; 64 * 32],

    // Timers
    delay_timer: u8,
    sound_timer: u8,

    // Stack
    stack: [u8; 16],
    sp: u8,

    // Keypad
    key: [u8; 16],

    program: &'static str,
}

impl Chipate {
    pub fn init(&mut self) {

        debug!("Initialize Chip");
    }

    pub fn load_program(&mut self, program: &str) {
        debug!("Loading program {}", program);
    }

    pub fn emulate_cycle(&mut self) {
        debug!("Cycle Begin");

        self.fetch_opcode();

        let one_second = time::Duration::from_secs(1);
        thread::sleep(one_second);

        debug!("Cycle End");
    }

    pub fn draw_screen(&mut self) {
        debug!("Drawing to Screen")
    }

    pub fn set_keys(&mut self) {
        debug!("Saving Key State")
    }

    pub fn setup_testing_memory(&mut self) {
        debug!("Setting test memory");

        self.memory[self.pc as usize] = 0xA2;
        self.memory[(self.pc + 1) as usize] = 0xF0;

        debug!("Location: 0x{:x} data: 0x{:x}",
               self.pc,
               self.memory[self.pc as usize]);
        debug!("Location: 0x{:x} data: 0x{:x}",
               self.pc + 1,
               self.memory[(self.pc + 1) as usize]);
    }

    pub fn fetch_opcode(&mut self) {
        let op_a = self.memory[self.pc as usize];
        self.opcode = op_a as u16;
        self.opcode = self.opcode << 8;
        // debug!("location: 0x{:x} data: 0x{:x}", self.pc, self.opcode);

        let op_b = self.memory[(self.pc + 1) as usize] as u16;
        // debug!("location: 0x{:x} data: 0x{:x}", self.pc, op_b);

        self.opcode = self.opcode | op_b;
        debug!("Opcode: 0x{:x}", self.opcode);
    }
}

pub fn new_chipate() -> Chipate {
    debug!("Creating New Chip");

    let chip = Chipate {
        opcode: 0,
        memory: [0; 4096],
        v: [0; 16],
        i: 0,
        pc: 0,
        gfx: [0; 64 * 32],
        delay_timer: 0,
        sound_timer: 0,
        stack: [0; 16],
        sp: 0,
        key: [0; 16],
        program: "",
    };

    return chip;
}
<commit_msg>Updated the init fn with real values<commit_after>use std::{thread, time};

/// Chipate Module
/// Rust emulation of the Chip-8
/// http://www.multigesturearticles/how-to-write-an-emulator-chip-8-interpreter/
#[allow(dead_code)]
pub struct Chipate {
    // Opcodes
    opcode: u16,

    // 4K Memory
    memory: [u8; 4096],
    // The systems memory map:
    // 0x000-0x1FF - Chip 8 interpreter (contains font set in emu)
    // 0x050-0x0A0 - Used for the built in 4x5 pixel font set (0-F)
    // 0x200-0xFFF - Program ROM and work RAM

    // Registers
    v: [u8; 16],

    // Index
    i: u16,

    // Program Counter
    pc: u16,

    gfx: [u8; 64 * 32],

    // Timers
    delay_timer: u8,
    sound_timer: u8,

    // Stack
    stack: [u8; 16],
    sp: u8,

    // Keypad
    key: [u8; 16],

    program: &'static str,
}

impl Chipate {
    pub fn init(&mut self) {
        debug!("Initialize Chip");
        // pc     = 0x200;  // Program counter starts at 0x200
        self.pc = 0x200;
        // opcode = 0;      // Reset current opcode
        self.opcode = 0x0;
        // I      = 0;      // Reset index register
        self.i = 0x0;
        // sp     = 0;      // Reset stack pointer
        self.sp = 0x0;

        // // Clear display
        // // Clear stack
        // // Clear registers V0-VF
        // // Clear memory

        // // Load fontset
        // for(int i = 0; i < 80; ++i)
        //     memory[i] = chip8_fontset[i];

        // // Reset timers
    }

    pub fn load_program(&mut self, program: &str) {
        debug!("Loading program {}", program);
    }

    pub fn emulate_cycle(&mut self) {
        debug!("Cycle Begin");

        self.fetch_opcode();

        let one_second = time::Duration::from_secs(1);
        thread::sleep(one_second);

        debug!("Cycle End");
    }

    pub fn draw_screen(&mut self) {
        debug!("Drawing to Screen")
    }

    pub fn set_keys(&mut self) {
        debug!("Saving Key State")
    }

    pub fn setup_testing_memory(&mut self) {
        debug!("Setting test memory");

        self.memory[self.pc as usize] = 0xA2;
        self.memory[(self.pc + 1) as usize] = 0xF0;

        debug!("Location: 0x{:x} data: 0x{:x}",
               self.pc,
               self.memory[self.pc as usize]);
        debug!("Location: 0x{:x} data: 0x{:x}",
               self.pc + 1,
               self.memory[(self.pc + 1) as usize]);
    }

    pub fn fetch_opcode(&mut self) {
        let op_a = self.memory[self.pc as usize];
        self.opcode = op_a as u16;
        self.opcode = self.opcode << 8;
        // debug!("location: 0x{:x} data: 0x{:x}", self.pc, self.opcode);

        let op_b = self.memory[(self.pc + 1) as usize] as u16;
        // debug!("location: 0x{:x} data: 0x{:x}", self.pc, op_b);

        self.opcode = self.opcode | op_b;
        debug!("Opcode: 0x{:x}", self.opcode);
    }
}

pub fn new_chipate() -> Chipate {
    debug!("Creating New Chip");

    let chip = Chipate {
        opcode: 0,
        memory: [0; 4096],
        v: [0; 16],
        i: 0,
        pc: 0,
        gfx: [0; 64 * 32],
        delay_timer: 0,
        sound_timer: 0,
        stack: [0; 16],
        sp: 0,
        key: [0; 16],
        program: "",
    };

    return chip;
}
<|endoftext|>


<commit_before>use std::io::Read;
use error::{Result, Error, ErrorKind};
use reader::{FbxEvent, PropertyValue};
use super::CommonState;

#[derive(Debug, Clone)]
pub struct BinaryParser {
    version: u32,
    end_offset_stack: Vec<u32>,
}

impl BinaryParser {
    pub fn new(version: u32) -> Self {
        BinaryParser {
            version: version,
            end_offset_stack: vec![],
        }
    }

    pub fn next<R: Read>(&mut self, reader: &mut R, common: &mut CommonState) -> Result<FbxEvent> {
        // Read a node record header.
        let node_record_header = try!(NodeRecordHeader::load(reader, &mut common.pos));
        if node_record_header.is_null_record() {
            // End of a node.
            return if let Some(expected_pos) = self.end_offset_stack.pop() {
                if common.pos == expected_pos as u64 {
                    Ok(FbxEvent::EndFbx)
                } else {
                    // Data is collapsed (the node doesn't end at expected position).
                    Err(Error::new(common.pos, ErrorKind::DataError("Node does not end at expected position".to_string())))
                }
            } else {
                // Data is collapsed (an extra node end marker found).
                Err(Error::new(common.pos, ErrorKind::DataError("An extra node end marker found".to_string())))
            };
        } else {
            // Start of a node.
            self.end_offset_stack.push(node_record_header.end_offset);
        }

        // Read a node name.
        let name = try_read_fixstr!(common.pos, reader, node_record_header.name_len);

        // Read properties.
        let mut properties = Vec::<PropertyValue>::with_capacity(node_record_header.num_properties as usize);
        for _ in 0..node_record_header.num_properties {
            let prop = try!(self.read_property(reader, common));
            properties.push(prop);
        }

        Ok(FbxEvent::StartNode {
            name: name,
            properties: properties,
        })
    }

    fn read_property<R: Read>(&mut self, reader: &mut R, common: &mut CommonState) -> Result<PropertyValue> {
        Err(Error::new(common.pos, ErrorKind::Unimplemented("Parser for FBX node property is not implemented yet".to_string())))
    }
}

#[derive(Debug, Copy, Clone)]
struct NodeRecordHeader {
    end_offset: u32,
    num_properties: u32,
    property_list_len: u32,
    name_len: u8,
}

impl NodeRecordHeader {
    pub fn load<R: Read>(reader: &mut R, pos: &mut u64) -> Result<Self> {
        let end_offset = try_read_le_u32!(*pos, reader);
        let num_properties = try_read_le_u32!(*pos, reader);
        let property_list_len = try_read_le_u32!(*pos, reader);
        let name_len = try_read_le_u8!(*pos, reader);
        Ok(NodeRecordHeader {
            end_offset: end_offset,
            num_properties: num_properties,
            property_list_len: property_list_len,
            name_len: name_len,
        })
    }

    /// Check whether the header indicates there are no more children.
    pub fn is_null_record(&self) -> bool {
        self.end_offset == 0
            && self.num_properties == 0
            && self.property_list_len == 0
            && self.name_len == 0
    }
}
<commit_msg>Fix code sending wrong event on end of nodes<commit_after>use std::io::Read;
use error::{Result, Error, ErrorKind};
use reader::{FbxEvent, PropertyValue};
use super::CommonState;

#[derive(Debug, Clone)]
pub struct BinaryParser {
    version: u32,
    end_offset_stack: Vec<u32>,
}

impl BinaryParser {
    pub fn new(version: u32) -> Self {
        BinaryParser {
            version: version,
            end_offset_stack: vec![],
        }
    }

    pub fn next<R: Read>(&mut self, reader: &mut R, common: &mut CommonState) -> Result<FbxEvent> {
        // Read a node record header.
        let node_record_header = try!(NodeRecordHeader::load(reader, &mut common.pos));
        if node_record_header.is_null_record() {
            // End of a node.
            return if let Some(expected_pos) = self.end_offset_stack.pop() {
                if common.pos == expected_pos as u64 {
                    Ok(FbxEvent::EndNode)
                } else {
                    // Data is collapsed (the node doesn't end at expected position).
                    Err(Error::new(common.pos, ErrorKind::DataError("Node does not end at expected position".to_string())))
                }
            } else {
                // Data is collapsed (an extra node end marker found).
                Err(Error::new(common.pos, ErrorKind::DataError("An extra node end marker found".to_string())))
            };
        } else {
            // Start of a node.
            self.end_offset_stack.push(node_record_header.end_offset);
        }

        // Read a node name.
        let name = try_read_fixstr!(common.pos, reader, node_record_header.name_len);

        // Read properties.
        let mut properties = Vec::<PropertyValue>::with_capacity(node_record_header.num_properties as usize);
        for _ in 0..node_record_header.num_properties {
            let prop = try!(self.read_property(reader, common));
            properties.push(prop);
        }

        Ok(FbxEvent::StartNode {
            name: name,
            properties: properties,
        })
    }

    fn read_property<R: Read>(&mut self, reader: &mut R, common: &mut CommonState) -> Result<PropertyValue> {
        Err(Error::new(common.pos, ErrorKind::Unimplemented("Parser for FBX node property is not implemented yet".to_string())))
    }
}

#[derive(Debug, Copy, Clone)]
struct NodeRecordHeader {
    end_offset: u32,
    num_properties: u32,
    property_list_len: u32,
    name_len: u8,
}

impl NodeRecordHeader {
    pub fn load<R: Read>(reader: &mut R, pos: &mut u64) -> Result<Self> {
        let end_offset = try_read_le_u32!(*pos, reader);
        let num_properties = try_read_le_u32!(*pos, reader);
        let property_list_len = try_read_le_u32!(*pos, reader);
        let name_len = try_read_le_u8!(*pos, reader);
        Ok(NodeRecordHeader {
            end_offset: end_offset,
            num_properties: num_properties,
            property_list_len: property_list_len,
            name_len: name_len,
        })
    }

    /// Check whether the header indicates there are no more children.
    pub fn is_null_record(&self) -> bool {
        self.end_offset == 0
            && self.num_properties == 0
            && self.property_list_len == 0
            && self.name_len == 0
    }
}
<|endoftext|>


<commit_before>extern crate find_folder;

use std::path::PathBuf;

pub struct Manager {
    assets_path: PathBuf,
}

impl Manager {
    pub fn new() -> Self {
        Manager { assets_path: find_folder::Search::KidsThenParents(3, 5).for_folder("assets").unwrap() }
    }

    pub fn get_asset(&self, name: &str) -> PathBuf {
        self.assets_path.join(name)
    }

    pub fn get_font(&self) -> PathBuf {
        self.get_asset("NotoSans-Regular.ttf")
    }
}

#[test]
fn new() {
    let manager = Manager::new();
    assert!(!manager.assets_path.to_string_lossy().is_empty());
    assert!(manager.assets_path.is_absolute());
    assert_eq!(None, manager.assets_path.extension());

//    assert_eq!("tts", manager.assets_path.extension().unwrap());
}

<commit_msg>get_font test<commit_after>extern crate find_folder;

use std::path::PathBuf;

pub struct Manager {
    assets_path: PathBuf,
}

impl Manager {
    pub fn new() -> Self {
        Manager { assets_path: find_folder::Search::KidsThenParents(3, 5).for_folder("assets").unwrap() }
    }

    pub fn get_asset(&self, name: &str) -> PathBuf {
        self.assets_path.join(name)
    }

    pub fn get_font(&self) -> PathBuf {
        self.get_asset("NotoSans-Regular.ttf")
    }
}

#[test]
fn new() {
    let manager = Manager::new();
    assert!(!manager.assets_path.to_string_lossy().is_empty());
    assert!(manager.assets_path.is_absolute());
    assert!(manager.assets_path.exists());
    assert!(manager.assets_path.is_dir());
}

#[test]
fn get_font() {
    let manager = Manager::new();
    let font_path = manager.get_font();
    assert!(font_path.to_string_lossy().len() > manager.assets_path.to_string_lossy().len());
    assert!(font_path.is_absolute());
    assert!(font_path.is_file());
    assert_eq!("ttf", font_path.extension().unwrap());
}
<|endoftext|>


<commit_before>use std::fs;
use std::ops::Drop;

pub enum Cleanup {
    File(String),
}

impl Drop for Cleanup {
    fn drop(&mut self) {
        match self {
            &mut Cleanup::File(ref path) => {
                match fs::remove_file(path) {
                    Ok(_) => {}
                    Err(e) => {
                        println!("warning: failed to remove file {}: {}", path, e);
                    }
                }
            }
        }
    }
}
<commit_msg>Add Empty and Dir cleanup types.<commit_after>use std;
use std::fs;
use std::ops::Drop;

pub enum Cleanup {
    #[allow(dead_code)]
    Empty,
    File(String),
    #[allow(dead_code)]
    Dir(String),
}

impl Drop for Cleanup {
    fn drop(&mut self) {
        match self {
            &mut Cleanup::Empty => {}
            &mut Cleanup::File(ref path) => {
                match fs::remove_file(path) {
                    Ok(_) => {}
                    Err(e) => {
                        println!("warning: failed to remove file {}: {}", path, e);
                    }
                }
            }
            &mut Cleanup::Dir(ref path) => {
                match std::fs::remove_dir_all(path) {
                    Ok(_) => {}
                    Err(e) => {
                        println!("warning: failed to remove file {}: {}", path, e);
                    }
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::io::Write;

use digest::Digest;
use mac::Mac;

pub struct Hmac<T: Digest + Default> {
    digest: T,
    key: Vec<u8>,
}

impl<T: Digest + Default> Hmac<T> {
    pub fn new<K: AsRef<[u8]>>(key: K) -> Self {
        Self::with_digest(key.as_ref(), Default::default())
    }

    pub fn with_digest(key: &[u8], mut digest: T) -> Self {
        let key = Self::expand_key(key);
        let ikey: Vec<_> = key.iter().map(|&b| b ^ 0x36).collect();

        digest.update(&ikey);

        Hmac {
            digest: digest,
            key: key,
        }
    }

    fn expand_key(key: &[u8]) -> Vec<u8> {
        let bs = T::block_size();
        let mut exp_key = vec![0; bs];

        if key.len() <= bs {
            exp_key.write(key).unwrap();
        } else {
            let mut digest = T::default();
            digest.update(key);
            digest.result(&mut exp_key[..bs]);
        }

        exp_key
    }

}

impl<T: Digest + Default> Mac for Hmac<T> {
    fn update<D: AsRef<[u8]>>(&mut self, data: D) {
        self.digest.update(data)
    }

    fn output_bits() -> usize { T::output_bits() }
    fn block_size() -> usize { T::block_size() }

    fn result<O: AsMut<[u8]>>(mut self, mut output: O) {
        self.digest.result(output.as_mut());
        self.digest = T::default();

        let okey: Vec<_> = self.key.iter().map(|&b| b ^ 0x5c).collect();

        self.digest.update(okey);
        self.digest.update(output.as_mut());
        self.digest.result(output.as_mut());
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use mac::Mac;
    use digest::Digest;
    use digest::md5::Md5;

    #[test]
    fn test_empty_string() {
        let mut hmac_md5 = Hmac::<Md5>::new("");
        hmac_md5.update("");

        let mut output = [0; 16];

        hmac_md5.result(&mut output);

        assert_eq!([0x74, 0xe6, 0xf7, 0x29, 0x8a, 0x9c, 0x2d, 0x16, 0x89, 0x35, 0xf5, 0x8c, 0x00, 0x1b, 0xad, 0x88], output);
    }
}
<commit_msg>feat(mac::hmac): Add more tests<commit_after>use digest::Digest;
use mac::Mac;

pub struct Hmac<T: Digest + Default> {
    digest: T,
    key: Vec<u8>,
}

impl<T: Digest + Default> Hmac<T> {
    pub fn new<K: AsRef<[u8]>>(key: K) -> Self {
        Self::with_digest(key.as_ref(), Default::default())
    }

    pub fn with_digest(key: &[u8], mut digest: T) -> Self {
        let key = Self::expand_key(key);
        let ikey: Vec<_> = key.iter().map(|&b| b ^ 0x36).collect();

        digest.update(&ikey);

        Hmac {
            digest: digest,
            key: key,
        }
    }

    fn expand_key(key: &[u8]) -> Vec<u8> {
        let bs = T::block_size();
        let mut exp_key = vec![0; bs];

        if key.len() <= bs {
            for i in 0..key.len() {
                exp_key[i] = key[i];
            }
        } else {
            let mut digest = T::default();
            digest.update(key);
            digest.result(&mut exp_key[..]);
        }

        exp_key
    }

}

impl<T: Digest + Default> Mac for Hmac<T> {
    fn update<D: AsRef<[u8]>>(&mut self, data: D) {
        self.digest.update(data)
    }

    fn output_bits() -> usize { T::output_bits() }
    fn block_size() -> usize { T::block_size() }

    fn result<O: AsMut<[u8]>>(mut self, mut output: O) {
        self.digest.result(output.as_mut());
        self.digest = T::default();

        let okey: Vec<_> = self.key.iter().map(|&b| b ^ 0x5c).collect();

        self.digest.update(okey);
        self.digest.update(output.as_mut());
        self.digest.result(output.as_mut());
    }
}

#[cfg(test)]
mod tests {
    mod md5 {
        use mac::Mac;
        use mac::hmac::Hmac;

        use digest::md5::Md5;

        #[test]
        fn rfc2104_test_vector_1() {
            let mut hmac_md5 = Hmac::<Md5>::new(&[0x0b; 16]);
            hmac_md5.update("Hi There");

            let mut output = [0; 16];

            hmac_md5.result(&mut output);

            assert_eq!([0x92, 0x94, 0x72, 0x7a, 0x36, 0x38, 0xbb, 0x1c, 0x13, 0xf4, 0x8e, 0xf8, 0x15, 0x8b, 0xfc, 0x9d, ], output);
        }

        #[test]
        fn rfc2104_test_vector_2() {
            let mut hmac_md5 = Hmac::<Md5>::new("Jefe");
            hmac_md5.update("what do ya want for nothing?");

            let mut output = [0; 16];

            hmac_md5.result(&mut output);

            assert_eq!([0x75, 0x0c, 0x78, 0x3e, 0x6a, 0xb0, 0xb5, 0x03, 0xea, 0xa8, 0x6e, 0x31, 0x0a, 0x5d, 0xb7, 0x38], output);
        }

        #[test]
        fn rfc2104_test_vector_3() {
            let mut hmac_md5 = Hmac::<Md5>::new(&[0xaa; 16]);
            for _ in 0..50 {
                hmac_md5.update(&[0xdd]);
            }

            let mut output = [0; 16];

            hmac_md5.result(&mut output);

            assert_eq!([0x56, 0xbe, 0x34, 0x52, 0x1d, 0x14, 0x4c, 0x88, 0xdb, 0xb8, 0xc7, 0x33, 0xf0, 0xe8, 0xb3, 0xf6, ], output);
        }
    }

    mod quickcheck {
        use quickcheck::quickcheck;

        use openssl::crypto::hash::Type;
        use openssl::crypto::hmac::hmac;

        use digest::*;

        use mac::Mac;
        use mac::hmac::Hmac;

        macro_rules! quickcheck {
            ($name:ident => $octavo:ty, $openssl:path) => {
                #[test]
                fn $name() {
                    fn prop(key:Vec<u8>, data: Vec<u8>) -> bool {
                        let octavo = {
                            let mut dig = Hmac::<$octavo>::new(&key);
                            let mut res = vec![0; <$octavo>::output_bytes()];

                            dig.update(&data);
                            dig.result(&mut res[..]);
                            res
                        };
                        let openssl = hmac($openssl, &key, &data);

                        octavo == openssl
                    }

                    quickcheck(prop as fn(Vec<u8>, Vec<u8>) -> bool)
                }
            }
        }

        quickcheck!(md5    => md5::Md5,     Type::MD5);
        quickcheck!(sha1   => sha1::Sha1,   Type::SHA1);
        quickcheck!(sha224 => sha2::Sha224, Type::SHA224);
        quickcheck!(sha256 => sha2::Sha256, Type::SHA256);
        quickcheck!(sha384 => sha2::Sha384, Type::SHA384);
        quickcheck!(sha512 => sha2::Sha512, Type::SHA512);
    }
}
<|endoftext|>


<commit_before>//! Tests for rustc plugins.

use cargo_test_support::{basic_manifest, project};
use cargo_test_support::{is_nightly, rustc_host};

#[cargo_test]
fn plugin_to_the_max() {
    if !is_nightly() {
        // plugins are unstable
        return;
    }

    let foo = project()
        .file(
            "Cargo.toml",
            r#"
            [package]
            name = "foo"
            version = "0.0.1"
            authors = []

            [lib]
            name = "foo_lib"

            [dependencies.bar]
            path = "../bar"
        "#,
        )
        .file(
            "src/main.rs",
            r#"
            #![feature(plugin)]
            #![plugin(bar)]
            extern crate foo_lib;

            fn main() { foo_lib::foo(); }
        "#,
        )
        .file(
            "src/foo_lib.rs",
            r#"
            #![feature(plugin)]
            #![plugin(bar)]

            pub fn foo() {}
        "#,
        )
        .build();
    let _bar = project()
        .at("bar")
        .file(
            "Cargo.toml",
            r#"
            [package]
            name = "bar"
            version = "0.0.1"
            authors = []

            [lib]
            name = "bar"
            plugin = true

            [dependencies.baz]
            path = "../baz"
        "#,
        )
        .file(
            "src/lib.rs",
            r#"
            #![feature(plugin_registrar, rustc_private)]

            extern crate baz;
            extern crate rustc_driver;

            use rustc_driver::plugin::Registry;

            #[plugin_registrar]
            pub fn foo(_reg: &mut Registry) {
                println!("{}", baz::baz());
            }
        "#,
        )
        .build();
    let _baz = project()
        .at("baz")
        .file(
            "Cargo.toml",
            r#"
            [package]
            name = "baz"
            version = "0.0.1"
            authors = []

            [lib]
            name = "baz"
            crate_type = ["dylib"]
        "#,
        )
        .file("src/lib.rs", "pub fn baz() -> i32 { 1 }")
        .build();

    foo.cargo("build").run();
    foo.cargo("doc").run();
}

#[cargo_test]
fn plugin_with_dynamic_native_dependency() {
    if !is_nightly() {
        // plugins are unstable
        return;
    }

    let build = project()
        .at("builder")
        .file(
            "Cargo.toml",
            r#"
            [package]
            name = "builder"
            version = "0.0.1"
            authors = []

            [lib]
            name = "builder"
            crate-type = ["dylib"]
        "#,
        )
        .file("src/lib.rs", "#[no_mangle] pub extern fn foo() {}")
        .build();

    let foo = project()
        .file(
            "Cargo.toml",
            r#"
            [package]
            name = "foo"
            version = "0.0.1"
            authors = []

            [dependencies.bar]
            path = "bar"
        "#,
        )
        .file(
            "src/main.rs",
            r#"
            #![feature(plugin)]
            #![plugin(bar)]

            fn main() {}
        "#,
        )
        .file(
            "bar/Cargo.toml",
            r#"
            [package]
            name = "bar"
            version = "0.0.1"
            authors = []
            build = 'build.rs'

            [lib]
            name = "bar"
            plugin = true
        "#,
        )
        .file(
            "bar/build.rs",
            r#"
            use std::env;
            use std::fs;
            use std::path::PathBuf;

            fn main() {
                let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
                let root = PathBuf::from(env::var("BUILDER_ROOT").unwrap());
                let file = format!("{}builder{}",
                    env::consts::DLL_PREFIX,
                    env::consts::DLL_SUFFIX);
                let src = root.join(&file);
                let dst = out_dir.join(&file);
                fs::copy(src, dst).unwrap();
                if cfg!(windows) {
                    fs::copy(root.join("builder.dll.lib"),
                             out_dir.join("builder.dll.lib")).unwrap();
                }
                println!("cargo:rustc-flags=-L {}", out_dir.display());
            }
        "#,
        )
        .file(
            "bar/src/lib.rs",
            r#"
            #![feature(plugin_registrar, rustc_private)]

            extern crate rustc_driver;
            use rustc_driver::plugin::Registry;

            #[cfg_attr(not(target_env = "msvc"), link(name = "builder"))]
            #[cfg_attr(target_env = "msvc", link(name = "builder.dll"))]
            extern { fn foo(); }

            #[plugin_registrar]
            pub fn bar(_reg: &mut Registry) {
                unsafe { foo() }
            }
        "#,
        )
        .build();

    build.cargo("build").run();

    let root = build.root().join("target").join("debug");
    foo.cargo("build -v").env("BUILDER_ROOT", root).run();
}

#[cargo_test]
fn plugin_integration() {
    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [package]
            name = "foo"
            version = "0.0.1"
            authors = []
            build = "build.rs"

            [lib]
            name = "foo"
            plugin = true
            doctest = false
        "#,
        )
        .file("build.rs", "fn main() {}")
        .file("src/lib.rs", "")
        .file("tests/it_works.rs", "")
        .build();

    p.cargo("test -v").run();
}

#[cargo_test]
fn doctest_a_plugin() {
    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [package]
            name = "foo"
            version = "0.0.1"
            authors = []

            [dependencies]
            bar = { path = "bar" }
        "#,
        )
        .file("src/lib.rs", "#[macro_use] extern crate bar;")
        .file(
            "bar/Cargo.toml",
            r#"
            [package]
            name = "bar"
            version = "0.0.1"
            authors = []

            [lib]
            name = "bar"
            plugin = true
        "#,
        )
        .file("bar/src/lib.rs", "pub fn bar() {}")
        .build();

    p.cargo("test -v").run();
}

// See #1515
#[cargo_test]
fn native_plugin_dependency_with_custom_linker() {
    let target = rustc_host();

    let _foo = project()
        .file(
            "Cargo.toml",
            r#"
            [package]
            name = "foo"
            version = "0.0.1"
            authors = []

            [lib]
            plugin = true
        "#,
        )
        .file("src/lib.rs", "")
        .build();

    let bar = project()
        .at("bar")
        .file(
            "Cargo.toml",
            r#"
            [package]
            name = "bar"
            version = "0.0.1"
            authors = []

            [dependencies.foo]
            path = "../foo"
        "#,
        )
        .file("src/lib.rs", "")
        .file(
            ".cargo/config",
            &format!(
                r#"
            [target.{}]
            linker = "nonexistent-linker"
        "#,
                target
            ),
        )
        .build();

    bar.cargo("build --verbose")
        .with_status(101)
        .with_stderr_contains(
            "\
[COMPILING] foo v0.0.1 ([..])
[RUNNING] `rustc [..] -C linker=nonexistent-linker [..]`
[ERROR] [..]linker[..]
",
        )
        .run();
}

#[cargo_test]
fn panic_abort_plugins() {
    if !is_nightly() {
        // requires rustc_private
        return;
    }

    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [package]
            name = "foo"
            version = "0.0.1"
            authors = []

            [profile.dev]
            panic = 'abort'

            [dependencies]
            bar = { path = "bar" }
        "#,
        )
        .file("src/lib.rs", "")
        .file(
            "bar/Cargo.toml",
            r#"
            [package]
            name = "bar"
            version = "0.0.1"
            authors = []

            [lib]
            plugin = true
        "#,
        )
        .file(
            "bar/src/lib.rs",
            r#"
            #![feature(rustc_private)]
            extern crate syntax;
        "#,
        )
        .build();

    p.cargo("build").run();
}

#[cargo_test]
fn shared_panic_abort_plugins() {
    if !is_nightly() {
        // requires rustc_private
        return;
    }

    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [package]
            name = "foo"
            version = "0.0.1"
            authors = []

            [profile.dev]
            panic = 'abort'

            [dependencies]
            bar = { path = "bar" }
            baz = { path = "baz" }
        "#,
        )
        .file("src/lib.rs", "extern crate baz;")
        .file(
            "bar/Cargo.toml",
            r#"
            [package]
            name = "bar"
            version = "0.0.1"
            authors = []

            [lib]
            plugin = true

            [dependencies]
            baz = { path = "../baz" }
        "#,
        )
        .file(
            "bar/src/lib.rs",
            r#"
            #![feature(rustc_private)]
            extern crate syntax;
            extern crate baz;
        "#,
        )
        .file("baz/Cargo.toml", &basic_manifest("baz", "0.0.1"))
        .file("baz/src/lib.rs", "")
        .build();

    p.cargo("build").run();
}
<commit_msg>Fix plugin tests for latest nightly.<commit_after>//! Tests for rustc plugins.

use cargo_test_support::{basic_manifest, project};
use cargo_test_support::{is_nightly, rustc_host};

#[cargo_test]
fn plugin_to_the_max() {
    if !is_nightly() {
        // plugins are unstable
        return;
    }

    let foo = project()
        .file(
            "Cargo.toml",
            r#"
            [package]
            name = "foo"
            version = "0.0.1"
            authors = []

            [lib]
            name = "foo_lib"

            [dependencies.bar]
            path = "../bar"
        "#,
        )
        .file(
            "src/main.rs",
            r#"
            #![feature(plugin)]
            #![plugin(bar)]
            extern crate foo_lib;

            fn main() { foo_lib::foo(); }
        "#,
        )
        .file(
            "src/foo_lib.rs",
            r#"
            #![feature(plugin)]
            #![plugin(bar)]

            pub fn foo() {}
        "#,
        )
        .build();
    let _bar = project()
        .at("bar")
        .file(
            "Cargo.toml",
            r#"
            [package]
            name = "bar"
            version = "0.0.1"
            authors = []

            [lib]
            name = "bar"
            plugin = true

            [dependencies.baz]
            path = "../baz"
        "#,
        )
        .file(
            "src/lib.rs",
            r#"
            #![feature(plugin_registrar, rustc_private)]

            extern crate baz;
            extern crate rustc_driver;

            use rustc_driver::plugin::Registry;

            #[plugin_registrar]
            pub fn foo(_reg: &mut Registry) {
                println!("{}", baz::baz());
            }
        "#,
        )
        .build();
    let _baz = project()
        .at("baz")
        .file(
            "Cargo.toml",
            r#"
            [package]
            name = "baz"
            version = "0.0.1"
            authors = []

            [lib]
            name = "baz"
            crate_type = ["dylib"]
        "#,
        )
        .file("src/lib.rs", "pub fn baz() -> i32 { 1 }")
        .build();

    foo.cargo("build").run();
    foo.cargo("doc").run();
}

#[cargo_test]
fn plugin_with_dynamic_native_dependency() {
    if !is_nightly() {
        // plugins are unstable
        return;
    }

    let build = project()
        .at("builder")
        .file(
            "Cargo.toml",
            r#"
            [package]
            name = "builder"
            version = "0.0.1"
            authors = []

            [lib]
            name = "builder"
            crate-type = ["dylib"]
        "#,
        )
        .file("src/lib.rs", "#[no_mangle] pub extern fn foo() {}")
        .build();

    let foo = project()
        .file(
            "Cargo.toml",
            r#"
            [package]
            name = "foo"
            version = "0.0.1"
            authors = []

            [dependencies.bar]
            path = "bar"
        "#,
        )
        .file(
            "src/main.rs",
            r#"
            #![feature(plugin)]
            #![plugin(bar)]

            fn main() {}
        "#,
        )
        .file(
            "bar/Cargo.toml",
            r#"
            [package]
            name = "bar"
            version = "0.0.1"
            authors = []
            build = 'build.rs'

            [lib]
            name = "bar"
            plugin = true
        "#,
        )
        .file(
            "bar/build.rs",
            r#"
            use std::env;
            use std::fs;
            use std::path::PathBuf;

            fn main() {
                let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
                let root = PathBuf::from(env::var("BUILDER_ROOT").unwrap());
                let file = format!("{}builder{}",
                    env::consts::DLL_PREFIX,
                    env::consts::DLL_SUFFIX);
                let src = root.join(&file);
                let dst = out_dir.join(&file);
                fs::copy(src, dst).unwrap();
                if cfg!(windows) {
                    fs::copy(root.join("builder.dll.lib"),
                             out_dir.join("builder.dll.lib")).unwrap();
                }
                println!("cargo:rustc-flags=-L {}", out_dir.display());
            }
        "#,
        )
        .file(
            "bar/src/lib.rs",
            r#"
            #![feature(plugin_registrar, rustc_private)]

            extern crate rustc_driver;
            use rustc_driver::plugin::Registry;

            #[cfg_attr(not(target_env = "msvc"), link(name = "builder"))]
            #[cfg_attr(target_env = "msvc", link(name = "builder.dll"))]
            extern { fn foo(); }

            #[plugin_registrar]
            pub fn bar(_reg: &mut Registry) {
                unsafe { foo() }
            }
        "#,
        )
        .build();

    build.cargo("build").run();

    let root = build.root().join("target").join("debug");
    foo.cargo("build -v").env("BUILDER_ROOT", root).run();
}

#[cargo_test]
fn plugin_integration() {
    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [package]
            name = "foo"
            version = "0.0.1"
            authors = []
            build = "build.rs"

            [lib]
            name = "foo"
            plugin = true
            doctest = false
        "#,
        )
        .file("build.rs", "fn main() {}")
        .file("src/lib.rs", "")
        .file("tests/it_works.rs", "")
        .build();

    p.cargo("test -v").run();
}

#[cargo_test]
fn doctest_a_plugin() {
    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [package]
            name = "foo"
            version = "0.0.1"
            authors = []

            [dependencies]
            bar = { path = "bar" }
        "#,
        )
        .file("src/lib.rs", "#[macro_use] extern crate bar;")
        .file(
            "bar/Cargo.toml",
            r#"
            [package]
            name = "bar"
            version = "0.0.1"
            authors = []

            [lib]
            name = "bar"
            plugin = true
        "#,
        )
        .file("bar/src/lib.rs", "pub fn bar() {}")
        .build();

    p.cargo("test -v").run();
}

// See #1515
#[cargo_test]
fn native_plugin_dependency_with_custom_linker() {
    let target = rustc_host();

    let _foo = project()
        .file(
            "Cargo.toml",
            r#"
            [package]
            name = "foo"
            version = "0.0.1"
            authors = []

            [lib]
            plugin = true
        "#,
        )
        .file("src/lib.rs", "")
        .build();

    let bar = project()
        .at("bar")
        .file(
            "Cargo.toml",
            r#"
            [package]
            name = "bar"
            version = "0.0.1"
            authors = []

            [dependencies.foo]
            path = "../foo"
        "#,
        )
        .file("src/lib.rs", "")
        .file(
            ".cargo/config",
            &format!(
                r#"
            [target.{}]
            linker = "nonexistent-linker"
        "#,
                target
            ),
        )
        .build();

    bar.cargo("build --verbose")
        .with_status(101)
        .with_stderr_contains(
            "\
[COMPILING] foo v0.0.1 ([..])
[RUNNING] `rustc [..] -C linker=nonexistent-linker [..]`
[ERROR] [..]linker[..]
",
        )
        .run();
}

#[cargo_test]
fn panic_abort_plugins() {
    if !is_nightly() {
        // requires rustc_private
        return;
    }

    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [package]
            name = "foo"
            version = "0.0.1"
            authors = []

            [profile.dev]
            panic = 'abort'

            [dependencies]
            bar = { path = "bar" }
        "#,
        )
        .file("src/lib.rs", "")
        .file(
            "bar/Cargo.toml",
            r#"
            [package]
            name = "bar"
            version = "0.0.1"
            authors = []

            [lib]
            plugin = true
        "#,
        )
        .file(
            "bar/src/lib.rs",
            r#"
            #![feature(rustc_private)]
            extern crate rustc_ast;
        "#,
        )
        .build();

    p.cargo("build").run();
}

#[cargo_test]
fn shared_panic_abort_plugins() {
    if !is_nightly() {
        // requires rustc_private
        return;
    }

    let p = project()
        .file(
            "Cargo.toml",
            r#"
            [package]
            name = "foo"
            version = "0.0.1"
            authors = []

            [profile.dev]
            panic = 'abort'

            [dependencies]
            bar = { path = "bar" }
            baz = { path = "baz" }
        "#,
        )
        .file("src/lib.rs", "extern crate baz;")
        .file(
            "bar/Cargo.toml",
            r#"
            [package]
            name = "bar"
            version = "0.0.1"
            authors = []

            [lib]
            plugin = true

            [dependencies]
            baz = { path = "../baz" }
        "#,
        )
        .file(
            "bar/src/lib.rs",
            r#"
            #![feature(rustc_private)]
            extern crate rustc_ast;
            extern crate baz;
        "#,
        )
        .file("baz/Cargo.toml", &basic_manifest("baz", "0.0.1"))
        .file("baz/src/lib.rs", "")
        .build();

    p.cargo("build").run();
}
<|endoftext|>


<commit_before>use std::sync::{Arc, mpsc};
use std::sync::atomic::{AtomicBool, Ordering};
use mio;

use ::{Action, Message};
pub type Action_ = Action;
pub type Message_ = Message;

pub struct Transfer {
    token: mio::Token,
    notify: mio::Sender<Message>,
    is_notified: Arc<AtomicBool>,
    sender: mpsc::Sender<Action>,
}

pub fn new(token: mio::Token, notify: mio::Sender<Message_>, sender: mpsc::Sender<Action_>, is_notified: Arc<AtomicBool>) -> Transfer {
    Transfer {
        token: token,
        notify: notify,
        is_notified: is_notified,
        sender: sender,
    }
}

impl Transfer {

    pub fn write(&mut self, data: &[u8]) {
        self.send(Action::Write(Some(data.to_vec())));
    }

    pub fn eof(&mut self) {
        self.send(Action::Write(None));
    }

    pub fn resume(&mut self) {
        self.notify.send(
            Message::Action(self.token, Action::Register(mio::EventSet::readable()))
        ).unwrap();
    }

    pub fn close(&mut self) {
        //TODO: consume self?
        self.notify.send(
            Message::Action(self.token, Action::Close)
        ).unwrap();
    }

    fn send(&self, action: Action) {
        if self.is_notified.load(Ordering::Acquire) {
            self.sender.send(action);
        } else {
            self.notify.send(Message::Action(self.token, action)).unwrap();
            self.is_notified.store(true, Ordering::Release);
        }
    }
    // fn abort()
    // fn pause()
}

impl Drop for Transfer {
    fn drop(&mut self) {
        self.close();
    }
}
<commit_msg>add debug fmt to transfer<commit_after>use std::fmt;
use std::sync::{Arc, mpsc};
use std::sync::atomic::{AtomicBool, Ordering};
use mio;

use ::{Action, Message};
pub type Action_ = Action;
pub type Message_ = Message;

pub struct Transfer {
    token: mio::Token,
    notify: mio::Sender<Message>,
    is_notified: Arc<AtomicBool>,
    sender: mpsc::Sender<Action>,
}

pub fn new(token: mio::Token, notify: mio::Sender<Message_>, sender: mpsc::Sender<Action_>, is_notified: Arc<AtomicBool>) -> Transfer {
    Transfer {
        token: token,
        notify: notify,
        is_notified: is_notified,
        sender: sender,
    }
}

impl Transfer {

    pub fn write(&mut self, data: &[u8]) {
        self.send(Action::Write(Some(data.to_vec())));
    }

    pub fn eof(&mut self) {
        self.send(Action::Write(None));
    }

    pub fn resume(&mut self) {
        self.notify.send(
            Message::Action(self.token, Action::Register(mio::EventSet::readable()))
        ).unwrap();
    }

    pub fn close(&mut self) {
        //TODO: consume self?
        self.notify.send(
            Message::Action(self.token, Action::Close)
        ).unwrap();
    }

    fn send(&self, action: Action) {
        if self.is_notified.load(Ordering::Acquire) {
            self.sender.send(action);
        } else {
            self.notify.send(Message::Action(self.token, action)).unwrap();
            self.is_notified.store(true, Ordering::Release);
        }
    }
    // fn abort()
    // fn pause()
}

impl fmt::Debug for Transfer {
    fn fmt(&self, f: &mut fmt::Foramtter) -> fmt::Result {
        f.debug_struct("Transfer")
            .field("token", &self.token)
            .finish()
    }
}

impl Drop for Transfer {
    fn drop(&mut self) {
        self.close();
    }
}
<|endoftext|>