<commit_before>//! Implementation of a Frequency Distribution in Rust. Keeps track of how many
//! times an object appears in a larger context (for example, how many times a
//! word appears in a piece of text). The underlying data structure of the
//! Frequency Distribution is a HashMap, so the object that is being counted
//! must be hashable.
//!
//! # Example
//!
//! ```
//! # use freqdist::FrequencyDistribution;
//! #
//! let mut fdist: FrequencyDistribution<&str> = FrequencyDistribution::new();
//!
//! fdist.insert("hello");
//! fdist.insert("hello");
//! fdist.insert("goodbye");
//!
//! assert_eq!(fdist.get(&"hello"), 2);
//! ```

#![feature(std_misc, hashmap_hasher)]
#![warn(missing_docs)]
#![cfg_attr(test, feature(test))]

#[cfg(test)] extern crate test;

use std::ops::Index;
use std::default::Default;
use std::hash::{Hasher, Hash, SipHasher};
use std::iter::{FromIterator, IntoIterator};
use std::borrow::Borrow;
use std::collections::HashMap;
use std::collections::hash_map::{Keys, IntoIter, Iter};
use std::collections::hash_state::{HashState, DefaultState};

static ZERO: usize = 0;

#[allow(missing_docs)] pub struct FrequencyDistribution<K, S = DefaultState<SipHasher>> {
  hashmap: HashMap<K, usize, S>,
  sum_counts: usize
}

impl<K, H = SipHasher, S = DefaultState<SipHasher>> FrequencyDistribution<K, S>
  where K : Eq + Hash,
        H : Hasher,
        S : HashState<Hasher = H>
{
  /// Creates a new FrequencyDistrbution with a hash state and size, where
  /// the size is known or can be estimated.
  #[inline(always)] pub fn with_capacity_and_hash_state(
    size: usize,
    state: S
  ) -> FrequencyDistribution<K, S> {
    FrequencyDistribution {
      hashmap: HashMap::with_capacity_and_hash_state(size, state),
      sum_counts: 0
    }
  }

  /// Creates a new FrequencyDistribution with a hash state and default size.
  #[inline(always)] pub fn with_hash_state(state: S) -> FrequencyDistribution<K, S> {
    FrequencyDistribution {
      hashmap: HashMap::with_hash_state(state),
      sum_counts: 0
    }
  }

    /// Iterator over the keys.
  #[inline(always)] pub fn keys(&self) -> Keys<K, usize> {
    self.hashmap.keys()
  }

  /// Iterator over the key, frequency pairs.
  #[inline(always)] pub fn iter(&self) -> Iter<K, usize> {
    self.hashmap.iter()
  }

  /// Iterator over the non-zero frequency keys.
  ///
  /// # Example
  ///
  /// ```
  /// # use std::iter::{FromIterator, IntoIterator};
  /// # use freqdist::FrequencyDistribution;
  /// #
  /// let existing = vec![
  ///   ("shoes", 1),
  ///   ("scarf", 0),
  ///   ("shirt", 13),
  ///   ("pants", 4)
  /// ];
  ///
  /// let fdist: FrequencyDistribution<&str> =
  ///   FromIterator::from_iter(existing.into_iter());
  /// let mut iter = fdist.iter_non_zero();
  ///
  /// assert_eq!(*iter.next().unwrap(), "shirt");
  /// assert_eq!(*iter.next().unwrap(), "shoes");
  /// assert_eq!(*iter.next().unwrap(), "pants");
  /// assert!(iter.next().is_none());
  /// ```
  #[inline(always)] pub fn iter_non_zero(&self) -> NonZeroKeysIter<K> {
    NonZeroKeysIter { iter: self.iter() }
  }

  /// Sum of the total number of items counted thus far.
  #[inline(always)] pub fn sum_counts(&self) -> usize {
    self.sum_counts
  }

  /// Returns the number of entries in the distribution
  #[inline(always)] pub fn len(&self) -> usize {
    self.hashmap.len()
  }

  /// Gets the frequency in which the key occurs.
  #[inline(always)] pub fn get<Q : ?Sized>(&self, k: &Q) -> usize
    where K : Borrow<Q>, Q : Hash + Eq
  {
    self[k]
  }

  /// Clears the counts of all keys and clears all keys from
  /// the distribution.
  #[inline(always)] pub fn clear(&mut self) {
    self.hashmap.clear()
  }

  /// Updates the frequency of the value found with the key if it
  /// already exists. Otherwise, inserts the key sizeo the hashmap,
  /// and sets its frequency to 1.
  #[inline(always)] pub fn insert(&mut self, k: K) {
    self.insert_or_incr_by(k, 1);
  }

  /// Removes an item and its associated counts.
  #[inline(always)] pub fn remove<Q : ?Sized>(&mut self, k: &Q)
    where K : Borrow<Q>, Q : Hash + Eq
  {
    match self.hashmap.remove(k) {
      Some(count) => self.sum_counts -= count,
      None => ()
    }
  }

  /// Inserts a value sizeo the hashmap if it does not exist with a new quantity
  /// specified by the increment. If the value already exists, increments by
  /// the specified amount.
  #[inline] fn insert_or_incr_by(&mut self, k: K, incr: usize) {
    if !self.hashmap.contains_key(&k) {
      self.hashmap.insert(k, incr);
    } else {
      *self.hashmap.get_mut(&k).unwrap() += incr;
    }

    self.sum_counts += incr;
  }
}

impl<K, H = SipHasher, S = DefaultState<SipHasher>> FrequencyDistribution<K, S>
  where K : Eq + Hash,
        H : Hasher + Default,
        S : HashState<Hasher = H> + Default
{
  /// Creates a new FrequencyDistribution where the size of the
  /// HashMap is unknown.
  #[inline(always)] pub fn new() -> FrequencyDistribution<K, S> {
    FrequencyDistribution::with_hash_state(Default::default())
  }

  /// Creates a new FrequencyDistribution where the size of the HashMap
  /// is known, or a estimate can be made.
  #[inline(always)] pub fn with_capacity(
    size: usize
  ) -> FrequencyDistribution<K, S> {
    FrequencyDistribution::with_capacity_and_hash_state(size, Default::default())
  }
}

impl<K, H = SipHasher, S = DefaultState<SipHasher>> Default for FrequencyDistribution<K, S>
  where K : Eq + Hash,
        H : Hasher + Default,
        S : HashState<Hasher = H> + Default
{
  /// Creates a default FrequencyDistribution.
  #[inline(always)] fn default() -> FrequencyDistribution<K, S> {
    FrequencyDistribution::new()
  }
}

impl<K, H, S> FromIterator<(K, usize)> for FrequencyDistribution<K, S>
  where K : Eq + Hash,
        H : Hasher,
        S : HashState<Hasher = H> + Default
{
  /// Iterates through an iterator, and creates a new FrequencyDistribution from
  /// it. The iterator should be an iterator over keys and frequencies. If a
  /// upper bounded `size_hsize` is available, then it is used, otherwise the lower
  /// bounded `size_hsize` is used.
  ///
  /// # Example
  ///
  /// ```
  /// # use std::iter::FromIterator;
  /// # use freqdist::FrequencyDistribution;
  /// #
  /// let existing = vec![
  ///   ("apples", 3),
  ///   ("oranges", 4),
  ///   ("bannana", 7)
  /// ];
  ///
  /// let fdist: FrequencyDistribution<&str> =
  ///   FromIterator::from_iter(existing.into_iter());
  ///
  /// assert_eq!(fdist.get(&"apples"), 3);
  /// assert_eq!(fdist.get(&"oranges"), 4);
  /// assert_eq!(fdist.get(&"bannana"), 7);
  /// ```
  fn from_iter<T>(iter: T) -> FrequencyDistribution<K, S>
    where T : IntoIterator<Item = (K, usize)>
  {
    let iterator = iter.into_iter();
    let mut fdist = if iterator.size_hint().1.is_some() {
      FrequencyDistribution::with_capacity_and_hash_state(
        iterator.size_hint().1.unwrap(),
        Default::default())
    } else {
      FrequencyDistribution::with_capacity_and_hash_state(
        iterator.size_hint().0,
        Default::default())
    };

    for (k, freq) in iterator { fdist.insert_or_incr_by(k, freq); }

    fdist
  }
}

impl<K, H, S> Extend<(K, usize)> for FrequencyDistribution<K, S>
  where K : Eq + Hash,
        H : Hasher,
        S : HashState<Hasher = H>
{
  /// Extends the hashmap by adding the keys or updating the frequencies of the keys.
  fn extend<T>(&mut self, iter: T)
    where T: IntoIterator<Item = (K, usize)>
  {
    for (k, freq) in iter.into_iter() { self.insert_or_incr_by(k, freq); }
  }
}

impl<K, H, S> IntoIterator for FrequencyDistribution<K, S>
  where K : Eq + Hash,
        H : Hasher,
        S : HashState<Hasher = H>
{
  type Item = (K, usize);
  type IntoIter = IntoIter<K, usize>;

  /// Consumes the distribution, and creates an iterator over the
  /// (Key, Quantity: usize) pairs.
  #[inline] fn into_iter(self) -> IntoIter<K, usize> {
    self.hashmap.into_iter()
  }
}

impl<'a, K, H, S, Q : ?Sized> Index<&'a Q> for FrequencyDistribution<K, S>
  where K : Eq + Hash + Borrow<Q>,
        H : Hasher,
        S : HashState<Hasher = H>,
        Q : Eq + Hash
{
  type Output = usize;

  #[inline] fn index<'b>(&'b self, index: &Q) -> &'b usize {
    self.hashmap.get(index).unwrap_or(&ZERO)
  }
}

/// Iterator over entries with non-zero quantities.
pub struct NonZeroKeysIter<'a, K: 'a> {
  iter: Iter<'a, K, usize>
}

impl<'a, K: 'a> Iterator for NonZeroKeysIter<'a, K> {
  type Item = &'a K;

  #[inline(always)] fn next(&mut self) -> Option<&'a K> {
    loop {
      match self.iter.next() {
        Some((k, c)) if *c > 0 => return Some(k),
        None => return None,
        _ => ()
      }
    }
  }
}

#[test]
fn smoke_test_frequency_distribution_insert() {
  let words = vec!("alpha", "beta");
  let mut dist: FrequencyDistribution<&str> = FrequencyDistribution::new();

  dist.insert(words[0]);

  assert_eq!(dist.get(&words[0]), 1);

  dist.insert(words[1]);

  assert_eq!(dist.get(&words[1]), 1);

  for _ in (0..7u32) { dist.insert(words[0]); }

  assert_eq!(dist.get(&words[0]), 8);
}

#[test]
fn smoke_test_frequency_distribution_iter() {
  let words = vec!(("a", 50usize), ("b", 100usize), ("c", 75usize), ("d", 0usize));
  let dist: FrequencyDistribution<&str> = FromIterator::from_iter(words.into_iter());

  assert_eq!(dist.get(&"a"), 50);
  assert_eq!(dist.get(&"b"), 100);
  assert_eq!(dist.get(&"c"), 75);

  let mut iter = dist.iter_non_zero();

  assert!(iter.next().is_some());
  assert!(iter.next().is_some());
  assert!(iter.next().is_some());
  assert!(iter.next().is_none());

  assert_eq!(dist.sum_counts(), 225);
}

#[test]
fn smoke_test_frequency_distribution_remove() {
  let words = vec!(("a", 50usize), ("b", 100usize), ("c", 25usize));
  let mut dist: FrequencyDistribution<&str> = FromIterator::from_iter(words.into_iter());

  assert_eq!(dist.get(&"a"), 50);

  dist.remove(&"a");

  assert_eq!(dist.get(&"a"), 0);
  assert_eq!(dist.sum_counts(), 125);
}

#[test]
fn smoke_test_frequency_sum_counts() {
  let words = vec!(("a", 7usize), ("b", 5usize), ("c", 8usize), ("d", 3usize));
  let mut dist: FrequencyDistribution<&str> = FromIterator::from_iter(words.into_iter());

  assert_eq!(dist.sum_counts(), 23);

  dist.insert("e");

  assert_eq!(dist.sum_counts(), 24);
}
<commit_msg>removed unused feature<commit_after>//! Implementation of a Frequency Distribution in Rust. Keeps track of how many
//! times an object appears in a larger context (for example, how many times a
//! word appears in a piece of text). The underlying data structure of the
//! Frequency Distribution is a HashMap, so the object that is being counted
//! must be hashable.
//!
//! # Example
//!
//! ```
//! # use freqdist::FrequencyDistribution;
//! #
//! let mut fdist: FrequencyDistribution<&str> = FrequencyDistribution::new();
//!
//! fdist.insert("hello");
//! fdist.insert("hello");
//! fdist.insert("goodbye");
//!
//! assert_eq!(fdist.get(&"hello"), 2);
//! ```

#![feature(hashmap_hasher)]
#![warn(missing_docs)]
#![cfg_attr(test, feature(test))]

#[cfg(test)] extern crate test;

use std::ops::Index;
use std::default::Default;
use std::hash::{Hasher, Hash, SipHasher};
use std::iter::{FromIterator, IntoIterator};
use std::borrow::Borrow;
use std::collections::HashMap;
use std::collections::hash_map::{Keys, IntoIter, Iter};
use std::collections::hash_state::{HashState, DefaultState};

static ZERO: usize = 0;

#[allow(missing_docs)] pub struct FrequencyDistribution<K, S = DefaultState<SipHasher>> {
  hashmap: HashMap<K, usize, S>,
  sum_counts: usize
}

impl<K, H = SipHasher, S = DefaultState<SipHasher>> FrequencyDistribution<K, S>
  where K : Eq + Hash,
        H : Hasher,
        S : HashState<Hasher = H>
{
  /// Creates a new FrequencyDistrbution with a hash state and size, where
  /// the size is known or can be estimated.
  #[inline(always)] pub fn with_capacity_and_hash_state(
    size: usize,
    state: S
  ) -> FrequencyDistribution<K, S> {
    FrequencyDistribution {
      hashmap: HashMap::with_capacity_and_hash_state(size, state),
      sum_counts: 0
    }
  }

  /// Creates a new FrequencyDistribution with a hash state and default size.
  #[inline(always)] pub fn with_hash_state(state: S) -> FrequencyDistribution<K, S> {
    FrequencyDistribution {
      hashmap: HashMap::with_hash_state(state),
      sum_counts: 0
    }
  }

    /// Iterator over the keys.
  #[inline(always)] pub fn keys(&self) -> Keys<K, usize> {
    self.hashmap.keys()
  }

  /// Iterator over the key, frequency pairs.
  #[inline(always)] pub fn iter(&self) -> Iter<K, usize> {
    self.hashmap.iter()
  }

  /// Iterator over the non-zero frequency keys.
  ///
  /// # Example
  ///
  /// ```
  /// # use std::iter::{FromIterator, IntoIterator};
  /// # use freqdist::FrequencyDistribution;
  /// #
  /// let existing = vec![
  ///   ("shoes", 1),
  ///   ("scarf", 0),
  ///   ("shirt", 13),
  ///   ("pants", 4)
  /// ];
  ///
  /// let fdist: FrequencyDistribution<&str> =
  ///   FromIterator::from_iter(existing.into_iter());
  /// let mut iter = fdist.iter_non_zero();
  ///
  /// assert_eq!(*iter.next().unwrap(), "shirt");
  /// assert_eq!(*iter.next().unwrap(), "shoes");
  /// assert_eq!(*iter.next().unwrap(), "pants");
  /// assert!(iter.next().is_none());
  /// ```
  #[inline(always)] pub fn iter_non_zero(&self) -> NonZeroKeysIter<K> {
    NonZeroKeysIter { iter: self.iter() }
  }

  /// Sum of the total number of items counted thus far.
  #[inline(always)] pub fn sum_counts(&self) -> usize {
    self.sum_counts
  }

  /// Returns the number of entries in the distribution
  #[inline(always)] pub fn len(&self) -> usize {
    self.hashmap.len()
  }

  /// Gets the frequency in which the key occurs.
  #[inline(always)] pub fn get<Q : ?Sized>(&self, k: &Q) -> usize
    where K : Borrow<Q>, Q : Hash + Eq
  {
    self[k]
  }

  /// Clears the counts of all keys and clears all keys from
  /// the distribution.
  #[inline(always)] pub fn clear(&mut self) {
    self.hashmap.clear()
  }

  /// Updates the frequency of the value found with the key if it
  /// already exists. Otherwise, inserts the key sizeo the hashmap,
  /// and sets its frequency to 1.
  #[inline(always)] pub fn insert(&mut self, k: K) {
    self.insert_or_incr_by(k, 1);
  }

  /// Removes an item and its associated counts.
  #[inline(always)] pub fn remove<Q : ?Sized>(&mut self, k: &Q)
    where K : Borrow<Q>, Q : Hash + Eq
  {
    match self.hashmap.remove(k) {
      Some(count) => self.sum_counts -= count,
      None => ()
    }
  }

  /// Inserts a value sizeo the hashmap if it does not exist with a new quantity
  /// specified by the increment. If the value already exists, increments by
  /// the specified amount.
  #[inline] fn insert_or_incr_by(&mut self, k: K, incr: usize) {
    if !self.hashmap.contains_key(&k) {
      self.hashmap.insert(k, incr);
    } else {
      *self.hashmap.get_mut(&k).unwrap() += incr;
    }

    self.sum_counts += incr;
  }
}

impl<K, H = SipHasher, S = DefaultState<SipHasher>> FrequencyDistribution<K, S>
  where K : Eq + Hash,
        H : Hasher + Default,
        S : HashState<Hasher = H> + Default
{
  /// Creates a new FrequencyDistribution where the size of the
  /// HashMap is unknown.
  #[inline(always)] pub fn new() -> FrequencyDistribution<K, S> {
    FrequencyDistribution::with_hash_state(Default::default())
  }

  /// Creates a new FrequencyDistribution where the size of the HashMap
  /// is known, or a estimate can be made.
  #[inline(always)] pub fn with_capacity(
    size: usize
  ) -> FrequencyDistribution<K, S> {
    FrequencyDistribution::with_capacity_and_hash_state(size, Default::default())
  }
}

impl<K, H = SipHasher, S = DefaultState<SipHasher>> Default for FrequencyDistribution<K, S>
  where K : Eq + Hash,
        H : Hasher + Default,
        S : HashState<Hasher = H> + Default
{
  /// Creates a default FrequencyDistribution.
  #[inline(always)] fn default() -> FrequencyDistribution<K, S> {
    FrequencyDistribution::new()
  }
}

impl<K, H, S> FromIterator<(K, usize)> for FrequencyDistribution<K, S>
  where K : Eq + Hash,
        H : Hasher,
        S : HashState<Hasher = H> + Default
{
  /// Iterates through an iterator, and creates a new FrequencyDistribution from
  /// it. The iterator should be an iterator over keys and frequencies. If a
  /// upper bounded `size_hsize` is available, then it is used, otherwise the lower
  /// bounded `size_hsize` is used.
  ///
  /// # Example
  ///
  /// ```
  /// # use std::iter::FromIterator;
  /// # use freqdist::FrequencyDistribution;
  /// #
  /// let existing = vec![
  ///   ("apples", 3),
  ///   ("oranges", 4),
  ///   ("bannana", 7)
  /// ];
  ///
  /// let fdist: FrequencyDistribution<&str> =
  ///   FromIterator::from_iter(existing.into_iter());
  ///
  /// assert_eq!(fdist.get(&"apples"), 3);
  /// assert_eq!(fdist.get(&"oranges"), 4);
  /// assert_eq!(fdist.get(&"bannana"), 7);
  /// ```
  fn from_iter<T>(iter: T) -> FrequencyDistribution<K, S>
    where T : IntoIterator<Item = (K, usize)>
  {
    let iterator = iter.into_iter();
    let mut fdist = if iterator.size_hint().1.is_some() {
      FrequencyDistribution::with_capacity_and_hash_state(
        iterator.size_hint().1.unwrap(),
        Default::default())
    } else {
      FrequencyDistribution::with_capacity_and_hash_state(
        iterator.size_hint().0,
        Default::default())
    };

    for (k, freq) in iterator { fdist.insert_or_incr_by(k, freq); }

    fdist
  }
}

impl<K, H, S> Extend<(K, usize)> for FrequencyDistribution<K, S>
  where K : Eq + Hash,
        H : Hasher,
        S : HashState<Hasher = H>
{
  /// Extends the hashmap by adding the keys or updating the frequencies of the keys.
  fn extend<T>(&mut self, iter: T)
    where T: IntoIterator<Item = (K, usize)>
  {
    for (k, freq) in iter.into_iter() { self.insert_or_incr_by(k, freq); }
  }
}

impl<K, H, S> IntoIterator for FrequencyDistribution<K, S>
  where K : Eq + Hash,
        H : Hasher,
        S : HashState<Hasher = H>
{
  type Item = (K, usize);
  type IntoIter = IntoIter<K, usize>;

  /// Consumes the distribution, and creates an iterator over the
  /// (Key, Quantity: usize) pairs.
  #[inline] fn into_iter(self) -> IntoIter<K, usize> {
    self.hashmap.into_iter()
  }
}

impl<'a, K, H, S, Q : ?Sized> Index<&'a Q> for FrequencyDistribution<K, S>
  where K : Eq + Hash + Borrow<Q>,
        H : Hasher,
        S : HashState<Hasher = H>,
        Q : Eq + Hash
{
  type Output = usize;

  #[inline] fn index<'b>(&'b self, index: &Q) -> &'b usize {
    self.hashmap.get(index).unwrap_or(&ZERO)
  }
}

/// Iterator over entries with non-zero quantities.
pub struct NonZeroKeysIter<'a, K: 'a> {
  iter: Iter<'a, K, usize>
}

impl<'a, K: 'a> Iterator for NonZeroKeysIter<'a, K> {
  type Item = &'a K;

  #[inline(always)] fn next(&mut self) -> Option<&'a K> {
    loop {
      match self.iter.next() {
        Some((k, c)) if *c > 0 => return Some(k),
        None => return None,
        _ => ()
      }
    }
  }
}

#[test]
fn smoke_test_frequency_distribution_insert() {
  let words = vec!("alpha", "beta");
  let mut dist: FrequencyDistribution<&str> = FrequencyDistribution::new();

  dist.insert(words[0]);

  assert_eq!(dist.get(&words[0]), 1);

  dist.insert(words[1]);

  assert_eq!(dist.get(&words[1]), 1);

  for _ in (0..7u32) { dist.insert(words[0]); }

  assert_eq!(dist.get(&words[0]), 8);
}

#[test]
fn smoke_test_frequency_distribution_iter() {
  let words = vec!(("a", 50usize), ("b", 100usize), ("c", 75usize), ("d", 0usize));
  let dist: FrequencyDistribution<&str> = FromIterator::from_iter(words.into_iter());

  assert_eq!(dist.get(&"a"), 50);
  assert_eq!(dist.get(&"b"), 100);
  assert_eq!(dist.get(&"c"), 75);

  let mut iter = dist.iter_non_zero();

  assert!(iter.next().is_some());
  assert!(iter.next().is_some());
  assert!(iter.next().is_some());
  assert!(iter.next().is_none());

  assert_eq!(dist.sum_counts(), 225);
}

#[test]
fn smoke_test_frequency_distribution_remove() {
  let words = vec!(("a", 50usize), ("b", 100usize), ("c", 25usize));
  let mut dist: FrequencyDistribution<&str> = FromIterator::from_iter(words.into_iter());

  assert_eq!(dist.get(&"a"), 50);

  dist.remove(&"a");

  assert_eq!(dist.get(&"a"), 0);
  assert_eq!(dist.sum_counts(), 125);
}

#[test]
fn smoke_test_frequency_sum_counts() {
  let words = vec!(("a", 7usize), ("b", 5usize), ("c", 8usize), ("d", 3usize));
  let mut dist: FrequencyDistribution<&str> = FromIterator::from_iter(words.into_iter());

  assert_eq!(dist.sum_counts(), 23);

  dist.insert("e");

  assert_eq!(dist.sum_counts(), 24);
}
<|endoftext|>


<commit_before>mod block_tokenizer;
mod block_parser;
mod constants;
mod tokens;
pub mod ast;

pub use block_parser::{BlockParser};<commit_msg>Fix formatting<commit_after>mod block_tokenizer;
mod block_parser;
mod constants;
mod tokens;
pub mod ast;

pub use block_parser::BlockParser;<|endoftext|>


<commit_before>//! Rust bindings for [libpcuid](https://github.com/anrieff/libcpuid)
//! CPU detection and feature extraction library.
//!
//! `rust-cpuid` provides a high-level interface for getting information
//! about the features of the CPU that runs your code. All the essential
//! work is done by the `libcpuid` C library and exposed through Rust's
//! FFI mechanism as a simple and convenient API.
//!
//! # Available features
//!
//! * CPU vendor, brand and codename detection
//! * information about number of cores (both physical and logical)
//! * cache size
//! * clock frequency
//!
//! # Installation
//!
//! First - download, and build libcpuid as [described in the readme](https://github.com/anrieff/libcpuid). Install it by running `make install` (you may want to run `ldconfig` afterwards).
//!
//! Add to your `Cargo.toml`:
//!
//! ```toml
//! [dependencies]
//! cpuid = "*"
//! ```
//!
//! # Example
//!
//! ```rust
//! extern crate cpuid;
//!
//! fn main () {
//!     match cpuid::identify() {
//!         Ok(info) => {
//!             println!("Found: {} CPU, model: {}", info.vendor, info.codename);
//!             println!("The full brand string is: {}", info.brand);
//!             println!("Hardware AES support: {}", if info.has_feature(cpuid::CpuFeature::AES) { "yes" } else { "no" });
//!         },
//!         Err(err) => println!("cpuid error: {}", err),
//!     };
//!     match cpuid::clock_frequency() {
//!         Some(frequency) => println!("CPU speed: {} MHz", frequency),
//!         None => println!("Couldn't get CPU speed."),
//!     };
//! }
//! ```

#![allow(unstable)]

extern crate libc;

use std::mem;
use std::str;

mod ffi;

/// A struct holding information about CPU features.
///
/// This data structure is returned by `identify()`. You can consult
/// [libcpuid docs for cpu_id_t](http://libcpuid.sourceforge.net/doxy/structcpu__id__t.html)
/// for more detailed descriptions of these fields.
pub struct CpuInfo {
    /// CPU vendor string, for example *GenuineIntel*.
    pub vendor: String,
    /// Brand string, for example *Intel(R) Core(TM) i5-2410M CPU @ 2.30GHz*.
    pub brand: String,
    /// Brief CPU codename, such as *Sandy Bridge (Core i5)*.
    pub codename: String,
    /// Number of physical cores of the current CPU.
    pub num_cores: i32,
    /// Number of logical processors (may include HyperThreading or such).
    pub num_logical_cpus: i32,
    /// Total number of logical processors.
    pub total_logical_cpus: i32,
    /// L1 data cache size in kB. `Some(0)` if the CPU lacks cache, `None` if it couldn't be determined.
    pub l1_data_cache: Option<i32>,
    /// L1 instruction cache size in kB. `Some(0)` if the CPU lacks cache, `None` if it couldn't be determined.
    pub l1_instruction_cache: Option<i32>,
    /// L2 cache size in kB. `Some(0)` if the CPU lacks L2 cache, `None` if it couldn't be determined.
    pub l2_cache: Option<i32>,
    /// L3 cache size in kB. `Some(0)` if the CPU lacks L3 cache, `None` if it couldn't be determined.
    pub l3_cache: Option<i32>,
    flags: [u8; ffi::CPU_FLAGS_MAX],
}

/// CPU feature identifiers.
#[derive(Copy)]
pub enum CpuFeature {
    FloatingPointUnit = 0,
    VirtualModeExtension,
    DebugingExtension,
    PageSizeExtension,
    TimestampCounter,
    ModelSpecificRegisters,
    PhysicalAddressExtension,
    MachineCheckException,
    CMPXCHG8B,
    APIC,
    MemoryTypeRangeRegisters,
    SysenterSysexit,
    PageGlobalEnable,
    MachineCheckArchitecture,
    CMOV,
    PageAttributeTable,
    PageAddressExtension36bit,
    ProcessorSerialNumber,
    CLFLUSH,
    DebugStore,
    ACPI,
    MMX,
    FXSAVE,
    SSE,
    SSE2,
    SelfSnoop,
    HyperThreading,
    ThermalMonitor,
    IA64,
    PendingBreakEnable,
    SSE3,
    PCLMULQDQ,
    DebugStore64,
    MONITOR,
    CplQualifiedDebugStore,
    VirtualisationTechnology,
    SaferModeExceptions,
    EnhancedSpeedStep,
    ThermalMonitor2,
    SSSE3,
    ContextId,
    CMPXCHG16B,
    SendTaskPriorityMessages,
    PerformanceCapabilitiesMSR,
    DirectCacheAccess,
    SSE41,
    SSE42,
    SyscallSysret,
    ExecuteDisableBit,
    MOVBE,
    POPCNT,
    AES,
    XSAVE,
    OSXSAVE,
    AdvancedVectorExtensions,
    MMXExtensions,
    AMD3DNow,
    AMD3DNowExtended,
    NoExecuteBit,
    FXSAVEOptimizations,
    RDTSCP,
    LongMode,
    LAHFLongMode,
    CoreMultiProcessingLegacyMode,
    AMDSecureVirtualMachine,
    LZCNT,
    MisalignedSSE,
    SSE4a,
    PREFETCH,
    OsVisibleWorkaround,
    InstructionBasedSampling,
    SSE5,
    SKINIT,
    WatchdogTimer,
    TemperatureSensor,
    FrequencyIDControl,
    VoltageIDControl,
    THERMTRIP,
    AMDThermalControl,
    SoftwareThermalControl,
    Multiplier100Mhz,
    HardwarePstateControl,
    ConstantTSCTicks,
    XOP,
    FMA3,
    FMA4,
    TrailingBitManipulation,
    FPConvert16Bit,
    RDRAND,
    X2APIC,
    CorePerformanceBoost,
    MPERF,
    ProcessorFeedbackInterface,
    ProcessorAccumulator,
    NumCpuFeatures,
}

impl CpuInfo {
    /// Checks if current CPU supports given feature.
    ///
    /// See `CpuFeature` for a list of available feature identifiers.
    pub fn has_feature(&self, feature: CpuFeature) -> bool {
        return self.flags[feature as usize] == 1u8
    }
}

/// Checks if the CPUID instruction is present.
pub fn is_present() -> bool {
    unsafe {
        ffi::cpuid_present() == 1
    }
}

/// Returns libcpuid version string.
pub fn version() -> String {
    unsafe {
        let ptr = ffi::cpuid_lib_version();
        let bytes = std::ffi::c_str_to_bytes(&ptr);
        str::from_utf8(bytes).ok().expect("Invalid UTF8 string").to_string()
    }
}

/// Returns last libcpuid error string.
pub fn error() -> String {
    unsafe {
        let ptr = ffi::cpuid_error();
        let bytes = std::ffi::c_str_to_bytes(&ptr);
        str::from_utf8(bytes).ok().expect("Invalid UTF8 string").to_string()
    }
}

/// Tries to identify the current CPU and its features.
///
/// In case of successful detection, a `CpuInfo` struct is returned (wrapped
/// with `Ok`) which contains all available data about the processor.
/// If libcpuid encounters an error, `identify` returns an `Err` with
/// the error message inside.
pub fn identify() -> Result<CpuInfo, String> {
    let mut raw: ffi::cpu_raw_data_t = unsafe { mem::uninitialized() };
    let raw_result = unsafe {
        ffi::cpuid_get_raw_data(&mut raw)
    };
    if raw_result != 0 {
        return Err(error());
    }
    let mut data: ffi::cpu_id_t = unsafe { mem::uninitialized() };
    let identify_result = unsafe {
        ffi::cpu_identify(&mut raw, &mut data)
    };
    if identify_result != 0 {
        Err(error())
    } else {
        Ok(CpuInfo {
            vendor: String::from_utf8(data.vendor_str.iter().map(|&x| x as u8).collect()).ok().expect("Invalid vendor string"),
            brand: String::from_utf8(data.brand_str.iter().map(|&x| x as u8).collect()).ok().expect("Invalid brand string"),
            codename: String::from_utf8(data.cpu_codename.iter().map(|&x| x as u8).collect()).ok().expect("Invalid codename string"),
            num_cores: data.num_cores as i32,
            num_logical_cpus: data.num_logical_cpus as i32,
            total_logical_cpus: data.total_logical_cpus as i32,
            l1_data_cache: if data.l1_data_cache != -1 { Some(data.l1_data_cache as i32) } else { None },
            l1_instruction_cache: if data.l1_instruction_cache != -1 { Some(data.l1_instruction_cache as i32) } else { None },
            l2_cache: if data.l2_cache != -1 { Some(data.l2_cache as i32) } else { None },
            l3_cache: if data.l3_cache != -1 { Some(data.l3_cache as i32) } else { None },
            flags: data.flags,
        })
    }
}

/// Gets the CPU clock frequency in MHz.
///
/// The underlying implementation uses several methods to discover CPU
/// speed, including direct measurement. If all these methods fail, function
/// returns `None`.
pub fn clock_frequency() -> Option<i32> {
    let frequency = unsafe {
        ffi::cpu_clock()
    };
    if frequency != -1 {
        Some(frequency as i32)
    } else {
        None
    }
}

#[test]
fn test_is_present() {
    assert!(is_present());
}
<commit_msg>Added feature gates for libc and ffi.<commit_after>//! Rust bindings for [libpcuid](https://github.com/anrieff/libcpuid)
//! CPU detection and feature extraction library.
//!
//! `rust-cpuid` provides a high-level interface for getting information
//! about the features of the CPU that runs your code. All the essential
//! work is done by the `libcpuid` C library and exposed through Rust's
//! FFI mechanism as a simple and convenient API.
//!
//! # Available features
//!
//! * CPU vendor, brand and codename detection
//! * information about number of cores (both physical and logical)
//! * cache size
//! * clock frequency
//!
//! # Installation
//!
//! First - download, and build libcpuid as [described in the readme](https://github.com/anrieff/libcpuid). Install it by running `make install` (you may want to run `ldconfig` afterwards).
//!
//! Add to your `Cargo.toml`:
//!
//! ```toml
//! [dependencies]
//! cpuid = "*"
//! ```
//!
//! # Example
//!
//! ```rust
//! extern crate cpuid;
//!
//! fn main () {
//!     match cpuid::identify() {
//!         Ok(info) => {
//!             println!("Found: {} CPU, model: {}", info.vendor, info.codename);
//!             println!("The full brand string is: {}", info.brand);
//!             println!("Hardware AES support: {}", if info.has_feature(cpuid::CpuFeature::AES) { "yes" } else { "no" });
//!         },
//!         Err(err) => println!("cpuid error: {}", err),
//!     };
//!     match cpuid::clock_frequency() {
//!         Some(frequency) => println!("CPU speed: {} MHz", frequency),
//!         None => println!("Couldn't get CPU speed."),
//!     };
//! }
//! ```

#![feature(libc, std_misc)]

extern crate libc;

use std::mem;
use std::str;

mod ffi;

/// A struct holding information about CPU features.
///
/// This data structure is returned by `identify()`. You can consult
/// [libcpuid docs for cpu_id_t](http://libcpuid.sourceforge.net/doxy/structcpu__id__t.html)
/// for more detailed descriptions of these fields.
pub struct CpuInfo {
    /// CPU vendor string, for example *GenuineIntel*.
    pub vendor: String,
    /// Brand string, for example *Intel(R) Core(TM) i5-2410M CPU @ 2.30GHz*.
    pub brand: String,
    /// Brief CPU codename, such as *Sandy Bridge (Core i5)*.
    pub codename: String,
    /// Number of physical cores of the current CPU.
    pub num_cores: i32,
    /// Number of logical processors (may include HyperThreading or such).
    pub num_logical_cpus: i32,
    /// Total number of logical processors.
    pub total_logical_cpus: i32,
    /// L1 data cache size in kB. `Some(0)` if the CPU lacks cache, `None` if it couldn't be determined.
    pub l1_data_cache: Option<i32>,
    /// L1 instruction cache size in kB. `Some(0)` if the CPU lacks cache, `None` if it couldn't be determined.
    pub l1_instruction_cache: Option<i32>,
    /// L2 cache size in kB. `Some(0)` if the CPU lacks L2 cache, `None` if it couldn't be determined.
    pub l2_cache: Option<i32>,
    /// L3 cache size in kB. `Some(0)` if the CPU lacks L3 cache, `None` if it couldn't be determined.
    pub l3_cache: Option<i32>,
    flags: [u8; ffi::CPU_FLAGS_MAX],
}

/// CPU feature identifiers.
#[derive(Copy)]
pub enum CpuFeature {
    FloatingPointUnit = 0,
    VirtualModeExtension,
    DebugingExtension,
    PageSizeExtension,
    TimestampCounter,
    ModelSpecificRegisters,
    PhysicalAddressExtension,
    MachineCheckException,
    CMPXCHG8B,
    APIC,
    MemoryTypeRangeRegisters,
    SysenterSysexit,
    PageGlobalEnable,
    MachineCheckArchitecture,
    CMOV,
    PageAttributeTable,
    PageAddressExtension36bit,
    ProcessorSerialNumber,
    CLFLUSH,
    DebugStore,
    ACPI,
    MMX,
    FXSAVE,
    SSE,
    SSE2,
    SelfSnoop,
    HyperThreading,
    ThermalMonitor,
    IA64,
    PendingBreakEnable,
    SSE3,
    PCLMULQDQ,
    DebugStore64,
    MONITOR,
    CplQualifiedDebugStore,
    VirtualisationTechnology,
    SaferModeExceptions,
    EnhancedSpeedStep,
    ThermalMonitor2,
    SSSE3,
    ContextId,
    CMPXCHG16B,
    SendTaskPriorityMessages,
    PerformanceCapabilitiesMSR,
    DirectCacheAccess,
    SSE41,
    SSE42,
    SyscallSysret,
    ExecuteDisableBit,
    MOVBE,
    POPCNT,
    AES,
    XSAVE,
    OSXSAVE,
    AdvancedVectorExtensions,
    MMXExtensions,
    AMD3DNow,
    AMD3DNowExtended,
    NoExecuteBit,
    FXSAVEOptimizations,
    RDTSCP,
    LongMode,
    LAHFLongMode,
    CoreMultiProcessingLegacyMode,
    AMDSecureVirtualMachine,
    LZCNT,
    MisalignedSSE,
    SSE4a,
    PREFETCH,
    OsVisibleWorkaround,
    InstructionBasedSampling,
    SSE5,
    SKINIT,
    WatchdogTimer,
    TemperatureSensor,
    FrequencyIDControl,
    VoltageIDControl,
    THERMTRIP,
    AMDThermalControl,
    SoftwareThermalControl,
    Multiplier100Mhz,
    HardwarePstateControl,
    ConstantTSCTicks,
    XOP,
    FMA3,
    FMA4,
    TrailingBitManipulation,
    FPConvert16Bit,
    RDRAND,
    X2APIC,
    CorePerformanceBoost,
    MPERF,
    ProcessorFeedbackInterface,
    ProcessorAccumulator,
    NumCpuFeatures,
}

impl CpuInfo {
    /// Checks if current CPU supports given feature.
    ///
    /// See `CpuFeature` for a list of available feature identifiers.
    pub fn has_feature(&self, feature: CpuFeature) -> bool {
        return self.flags[feature as usize] == 1u8
    }
}

/// Checks if the CPUID instruction is present.
pub fn is_present() -> bool {
    unsafe {
        ffi::cpuid_present() == 1
    }
}

/// Returns libcpuid version string.
pub fn version() -> String {
    unsafe {
        let ptr = ffi::cpuid_lib_version();
        let bytes = std::ffi::c_str_to_bytes(&ptr);
        str::from_utf8(bytes).ok().expect("Invalid UTF8 string").to_string()
    }
}

/// Returns last libcpuid error string.
pub fn error() -> String {
    unsafe {
        let ptr = ffi::cpuid_error();
        let bytes = std::ffi::c_str_to_bytes(&ptr);
        str::from_utf8(bytes).ok().expect("Invalid UTF8 string").to_string()
    }
}

/// Tries to identify the current CPU and its features.
///
/// In case of successful detection, a `CpuInfo` struct is returned (wrapped
/// with `Ok`) which contains all available data about the processor.
/// If libcpuid encounters an error, `identify` returns an `Err` with
/// the error message inside.
pub fn identify() -> Result<CpuInfo, String> {
    let mut raw: ffi::cpu_raw_data_t = unsafe { mem::uninitialized() };
    let raw_result = unsafe {
        ffi::cpuid_get_raw_data(&mut raw)
    };
    if raw_result != 0 {
        return Err(error());
    }
    let mut data: ffi::cpu_id_t = unsafe { mem::uninitialized() };
    let identify_result = unsafe {
        ffi::cpu_identify(&mut raw, &mut data)
    };
    if identify_result != 0 {
        Err(error())
    } else {
        Ok(CpuInfo {
            vendor: String::from_utf8(data.vendor_str.iter().map(|&x| x as u8).collect()).ok().expect("Invalid vendor string"),
            brand: String::from_utf8(data.brand_str.iter().map(|&x| x as u8).collect()).ok().expect("Invalid brand string"),
            codename: String::from_utf8(data.cpu_codename.iter().map(|&x| x as u8).collect()).ok().expect("Invalid codename string"),
            num_cores: data.num_cores as i32,
            num_logical_cpus: data.num_logical_cpus as i32,
            total_logical_cpus: data.total_logical_cpus as i32,
            l1_data_cache: if data.l1_data_cache != -1 { Some(data.l1_data_cache as i32) } else { None },
            l1_instruction_cache: if data.l1_instruction_cache != -1 { Some(data.l1_instruction_cache as i32) } else { None },
            l2_cache: if data.l2_cache != -1 { Some(data.l2_cache as i32) } else { None },
            l3_cache: if data.l3_cache != -1 { Some(data.l3_cache as i32) } else { None },
            flags: data.flags,
        })
    }
}

/// Gets the CPU clock frequency in MHz.
///
/// The underlying implementation uses several methods to discover CPU
/// speed, including direct measurement. If all these methods fail, function
/// returns `None`.
pub fn clock_frequency() -> Option<i32> {
    let frequency = unsafe {
        ffi::cpu_clock()
    };
    if frequency != -1 {
        Some(frequency as i32)
    } else {
        None
    }
}

#[test]
fn test_is_present() {
    assert!(is_present());
}
<|endoftext|>


<commit_before>#![feature(globs)]
#![feature(macro_rules)]
#![feature(slicing_syntax)]

pub use consts::*;
pub use proto::{Acl, Stat, WatchedEvent};
pub use zookeeper::*;

mod consts;
mod proto;
mod zookeeper;
<commit_msg>follow up nightly fixes<commit_after>pub use consts::*;
pub use proto::{Acl, Stat, WatchedEvent};
pub use zookeeper::*;

mod consts;
mod proto;
mod zookeeper;
<|endoftext|>


<commit_before>extern crate websocket;
extern crate rustc_serialize;

use std::thread;

use rustc_serialize::json;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

extern crate websocket;
extern crate rustc_serialize;

use std::thread;

use rustc_serialize::json;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


<commit_before>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

/*!
Syntax extension to create floating point literals from hexadecimal strings

Once loaded, hexfloat!() is called with a string containing the hexadecimal
floating-point literal, and an optional type (f32 or f64).
If the type is omitted, the literal is treated the same as a normal unsuffixed
literal.

# Examples

To load the extension and use it:

```rust,ignore
#[phase(plugin)]
extern crate hexfloat;

fn main() {
    let val = hexfloat!("0x1.ffffb4", f32);
}
```

# References

* [ExploringBinary: hexadecimal floating point constants]
  (http://www.exploringbinary.com/hexadecimal-floating-point-constants/)

*/

#![crate_name = "hexfloat"]
#![experimental]
#![crate_type = "rlib"]
#![crate_type = "dylib"]
#![license = "MIT/ASL2"]
#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
       html_favicon_url = "http://www.rust-lang.org/favicon.ico",
       html_root_url = "http://doc.rust-lang.org/master/")]
#![feature(plugin_registrar)]

extern crate syntax;
extern crate rustc;

use syntax::ast;
use syntax::codemap::{Span, mk_sp};
use syntax::ext::base;
use syntax::ext::base::{ExtCtxt, MacExpr};
use syntax::ext::build::AstBuilder;
use syntax::parse::token;
use syntax::ptr::P;
use rustc::plugin::Registry;

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_macro("hexfloat", expand_syntax_ext);
}

//Check if the literal is valid (as LLVM expects),
//and return a descriptive error if not.
fn hex_float_lit_err(s: &str) -> Option<(uint, String)> {
    let mut chars = s.chars().peekable();
    let mut i = 0;
    if chars.peek() == Some(&'-') { chars.next(); i+= 1 }
    if chars.next() != Some('0') {
        return Some((i, "Expected '0'".to_string()));
    } i+=1;
    if chars.next() != Some('x') {
        return Some((i, "Expected 'x'".to_string()));
    } i+=1;
    let mut d_len = 0i;
    for _ in chars.take_while(|c| c.is_digit_radix(16)) { chars.next(); i+=1; d_len += 1;}
    if chars.next() != Some('.') {
        return Some((i, "Expected '.'".to_string()));
    } i+=1;
    let mut f_len = 0i;
    for _ in chars.take_while(|c| c.is_digit_radix(16)) { chars.next(); i+=1; f_len += 1;}
    if d_len == 0 && f_len == 0 {
        return Some((i, "Expected digits before or after decimal \
                         point".to_string()));
    }
    if chars.next() != Some('p') {
        return Some((i, "Expected 'p'".to_string()));
    } i+=1;
    if chars.peek() == Some(&'-') { chars.next(); i+= 1 }
    let mut e_len = 0i;
    for _ in chars.take_while(|c| c.is_digit()) { chars.next(); i+=1; e_len += 1}
    if e_len == 0 {
        return Some((i, "Expected exponent digits".to_string()));
    }
    match chars.next() {
        None => None,
        Some(_) => Some((i, "Expected end of string".to_string()))
    }
}

pub fn expand_syntax_ext(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
                         -> Box<base::MacResult + 'static> {
    let (expr, ty_lit) = parse_tts(cx, tts);

    let ty = match ty_lit {
        None => None,
        Some(Ident{ident, span}) => match token::get_ident(ident).get() {
            "f32" => Some(ast::TyF32),
            "f64" => Some(ast::TyF64),
            _ => {
                cx.span_err(span, "invalid floating point type in hexfloat!");
                None
            }
        }
    };

    let s = match expr.node {
        // expression is a literal
        ast::ExprLit(ref lit) => match lit.node {
            // string literal
            ast::LitStr(ref s, _) => {
                s.clone()
            }
            _ => {
                cx.span_err(expr.span, "unsupported literal in hexfloat!");
                return base::DummyResult::expr(sp);
            }
        },
        _ => {
            cx.span_err(expr.span, "non-literal in hexfloat!");
            return base::DummyResult::expr(sp);
        }
    };

    {
        let err = hex_float_lit_err(s.get());
        match err {
            Some((err_pos, err_str)) => {
                let pos = expr.span.lo + syntax::codemap::Pos::from_uint(err_pos + 1);
                let span = syntax::codemap::mk_sp(pos,pos);
                cx.span_err(span,
                            format!("invalid hex float literal in hexfloat!: \
                                     {}",
                                    err_str).as_slice());
                return base::DummyResult::expr(sp);
            }
            _ => ()
        }
    }

    let lit = match ty {
        None => ast::LitFloatUnsuffixed(s),
        Some (ty) => ast::LitFloat(s, ty)
    };
    MacExpr::new(cx.expr_lit(sp, lit))
}

struct Ident {
    ident: ast::Ident,
    span: Span
}

fn parse_tts(cx: &ExtCtxt,
             tts: &[ast::TokenTree]) -> (P<ast::Expr>, Option<Ident>) {
    let p = &mut cx.new_parser_from_tts(tts);
    let ex = p.parse_expr();
    let id = if p.token == token::EOF {
        None
    } else {
        p.expect(&token::COMMA);
        let lo = p.span.lo;
        let ident = p.parse_ident();
        let hi = p.last_span.hi;
        Some(Ident{ident: ident, span: mk_sp(lo, hi)})
    };
    if p.token != token::EOF {
        p.unexpected();
    }
    (ex, id)
}

// FIXME (10872): This is required to prevent an LLVM assert on Windows
#[test]
fn dummy_test() { }
<commit_msg>Update to rust master<commit_after>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

/*!
Syntax extension to create floating point literals from hexadecimal strings

Once loaded, hexfloat!() is called with a string containing the hexadecimal
floating-point literal, and an optional type (f32 or f64).
If the type is omitted, the literal is treated the same as a normal unsuffixed
literal.

# Examples

To load the extension and use it:

```rust,ignore
#[phase(plugin)]
extern crate hexfloat;

fn main() {
    let val = hexfloat!("0x1.ffffb4", f32);
}
```

# References

* [ExploringBinary: hexadecimal floating point constants]
  (http://www.exploringbinary.com/hexadecimal-floating-point-constants/)

*/

#![crate_name = "hexfloat"]
#![experimental]
#![crate_type = "rlib"]
#![crate_type = "dylib"]
#![license = "MIT/ASL2"]
#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
       html_favicon_url = "http://www.rust-lang.org/favicon.ico",
       html_root_url = "http://doc.rust-lang.org/master/")]
#![feature(plugin_registrar)]

extern crate syntax;
extern crate rustc;

use syntax::ast;
use syntax::codemap::{Span, mk_sp};
use syntax::ext::base;
use syntax::ext::base::{ExtCtxt, MacExpr};
use syntax::ext::build::AstBuilder;
use syntax::parse::token;
use syntax::ptr::P;
use rustc::plugin::Registry;

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_macro("hexfloat", expand_syntax_ext);
}

//Check if the literal is valid (as LLVM expects),
//and return a descriptive error if not.
fn hex_float_lit_err(s: &str) -> Option<(uint, String)> {
    let mut chars = s.chars().peekable();
    let mut i = 0;
    if chars.peek() == Some(&'-') { chars.next(); i+= 1 }
    if chars.next() != Some('0') {
        return Some((i, "Expected '0'".to_string()));
    } i+=1;
    if chars.next() != Some('x') {
        return Some((i, "Expected 'x'".to_string()));
    } i+=1;
    let mut d_len = 0i;
    for _ in chars.take_while(|c| c.is_digit_radix(16)) { chars.next(); i+=1; d_len += 1;}
    if chars.next() != Some('.') {
        return Some((i, "Expected '.'".to_string()));
    } i+=1;
    let mut f_len = 0i;
    for _ in chars.take_while(|c| c.is_digit_radix(16)) { chars.next(); i+=1; f_len += 1;}
    if d_len == 0 && f_len == 0 {
        return Some((i, "Expected digits before or after decimal \
                         point".to_string()));
    }
    if chars.next() != Some('p') {
        return Some((i, "Expected 'p'".to_string()));
    } i+=1;
    if chars.peek() == Some(&'-') { chars.next(); i+= 1 }
    let mut e_len = 0i;
    for _ in chars.take_while(|c| c.is_digit()) { chars.next(); i+=1; e_len += 1}
    if e_len == 0 {
        return Some((i, "Expected exponent digits".to_string()));
    }
    match chars.next() {
        None => None,
        Some(_) => Some((i, "Expected end of string".to_string()))
    }
}

pub fn expand_syntax_ext(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
                         -> Box<base::MacResult + 'static> {
    let (expr, ty_lit) = parse_tts(cx, tts);

    let ty = match ty_lit {
        None => None,
        Some(Ident{ident, span}) => match token::get_ident(ident).get() {
            "f32" => Some(ast::TyF32),
            "f64" => Some(ast::TyF64),
            _ => {
                cx.span_err(span, "invalid floating point type in hexfloat!");
                None
            }
        }
    };

    let s = match expr.node {
        // expression is a literal
        ast::ExprLit(ref lit) => match lit.node {
            // string literal
            ast::LitStr(ref s, _) => {
                s.clone()
            }
            _ => {
                cx.span_err(expr.span, "unsupported literal in hexfloat!");
                return base::DummyResult::expr(sp);
            }
        },
        _ => {
            cx.span_err(expr.span, "non-literal in hexfloat!");
            return base::DummyResult::expr(sp);
        }
    };

    {
        let err = hex_float_lit_err(s.get());
        match err {
            Some((err_pos, err_str)) => {
                let pos = expr.span.lo + syntax::codemap::Pos::from_uint(err_pos + 1);
                let span = syntax::codemap::mk_sp(pos,pos);
                cx.span_err(span,
                            format!("invalid hex float literal in hexfloat!: \
                                     {}",
                                    err_str).as_slice());
                return base::DummyResult::expr(sp);
            }
            _ => ()
        }
    }

    let lit = match ty {
        None => ast::LitFloatUnsuffixed(s),
        Some (ty) => ast::LitFloat(s, ty)
    };
    MacExpr::new(cx.expr_lit(sp, lit))
}

struct Ident {
    ident: ast::Ident,
    span: Span
}

fn parse_tts(cx: &ExtCtxt,
             tts: &[ast::TokenTree]) -> (P<ast::Expr>, Option<Ident>) {
    let p = &mut cx.new_parser_from_tts(tts);
    let ex = p.parse_expr();
    let id = if p.token == token::Eof {
        None
    } else {
        p.expect(&token::Comma);
        let lo = p.span.lo;
        let ident = p.parse_ident();
        let hi = p.last_span.hi;
        Some(Ident{ident: ident, span: mk_sp(lo, hi)})
    };
    if p.token != token::Eof {
        p.unexpected();
    }
    (ex, id)
}

// FIXME (10872): This is required to prevent an LLVM assert on Windows
#[test]
fn dummy_test() { }
<|endoftext|>


<commit_before>mod traits;
mod solver;

pub use traits::*; // re-export the traits
pub use solver::*;
<commit_msg>Small correction<commit_after>mod traits;
mod solver;

// re-exports

pub use traits::*;
pub use solver::*;
<|endoftext|>


<commit_before>#![feature(globs)]

extern crate serialize;

use std::io::*;
use std::sync::{Arc, RWLock};
use std::ascii::AsciiExt;
use std::collections::HashMap;
use serialize::json;

struct IrcConnection {
    name: String,
    socket: TcpStream,
    data_out: Option<Sender<IrcMessage>>,
    data_in: Option<Receiver<String>>,
}

impl IrcConnection {
    pub fn create(name: &str, addr: &str, data_out: Sender<IrcMessage>, data_in: Receiver<String>) -> Result<(), IoError> {
        let socket = try!(TcpStream::connect(addr));
        let connection_receiving = IrcConnection {
            name: name.into_string(),
            socket: socket.clone(),
            data_out: Some(data_out),
            data_in: None
        };
        let connection_sending = IrcConnection {
            name: name.into_string(),
            socket: socket, // No need to clone a second time, as this is the last time we are using this socket
            data_out: None,
            data_in: Some(data_in)
        };
        connection_receiving.spawn_reading_thread().ok().expect("This was already setup to work");
        connection_sending.spawn_writing_thread().ok().expect("This was already setup to work");
        return Ok(())
    }

    fn spawn_reading_thread(self) -> Result<(), &'static str> {
        let data_out = match self.data_out {
            Some(ref v) => v.clone(),
            None => return Err("Can't start reading thread without data_out")
        };
        spawn(proc() {
            let mut reader = BufferedReader::new(self.socket.clone());
            loop {
                let whole_input = match reader.read_line() {
                    Ok(v) => v,
                    Err(e) => {
                        println!("Error: {}", e);
                        break;
                    }
                };
                let input = whole_input.trim_right();
                let message_split: Vec<&str> = input.split(' ').collect();
                let (command, args, possible_mask): (&str, &[&str], Option<String>) = if message_split[0].starts_with(":") {
                    (message_split[1], message_split.slice_from(2), Some(message_split[0].slice_from(1).into_string()))
                } else {
                    (message_split[0], message_split.slice_from(1), None)
                };
                match possible_mask {
                    Some(ref v) => println!("Received {} from {}: {}", command, v, args.connect(" ")),
                    None => println!("Received {}: {}", command, args.connect(" "))
                }
                let args_owned: Vec<String> = args.iter().map(|s: &&str| s.to_string()).collect();
                let message = IrcMessage::new(self.name.clone(), command.into_string(), args_owned, possible_mask);
                data_out.send(message);
            }
        });
        return Ok(())
    }

    fn spawn_writing_thread(mut self) -> Result<(), &'static str> {
        if (&self.data_in).is_none() {
            return Err("Can't start writing thread without data_in")
        };
        spawn(proc() {
            let data_in = self.data_in.expect("Already confirmed above");
            loop {
                let command = data_in.recv();
                println!("Sending: {}", command);
                self.socket.write(command.as_bytes()).ok().expect("Failed to write to stream");
                self.socket.write(b"\n").ok().expect("Failed to write to stream");
                self.socket.flush().ok().expect("Failed to flush stream");
            }
        });
        return Ok(())
    }
}

struct IrcMessage {
    server: String,
    command: String,
    args: Vec<String>,
    mask: Option<String>
}

impl IrcMessage {
    fn new(server: String, command: String, args: Vec<String>, mask: Option<String>) -> IrcMessage {
        return IrcMessage {
            server: server,
            command: command,
            args: args,
            mask: mask
        }
    }
}

struct Client {
    data_out: Sender<String>,
    data_in: Receiver<IrcMessage>,
    interface: IrcInterface,
    commands: Arc<RWLock<HashMap<String, Vec<|&mut CommandEvent|:'static>>>>,
    raw_listeners: Arc<RWLock<HashMap<String, Vec<|&mut IrcMessageEvent|:'static>>>>,
    name: String,
    bot_nick: String,
    bot_username: String,
    bot_real_name: String,
    server_address: String,
    command_prefix: String,
    // TODO: Update channels with channels actually joined, not just configured.
    channels: Vec<String>,
    irc_connection_channel: Option<(Sender<IrcMessage>, Receiver<String>)>,
}

impl Client {
    pub fn new(config: ClientConfiguration) -> Client {
        let (data_out, connection_data_in) = channel();
        let (connection_data_out, data_in) = channel();
        let client = Client {
            interface: IrcInterface::new(data_out.clone()),
            data_out: data_out,
            data_in: data_in,
            commands: Arc::new(RWLock::new(HashMap::new())),
            raw_listeners: Arc::new(RWLock::new(HashMap::new())),
            name: config.name,
            bot_nick: config.nick,
            bot_username: config.user,
            bot_real_name: config.real_name,
            channels: config.channels,
            server_address: config.address,
            command_prefix: config.command_prefix,
            irc_connection_channel: Some((connection_data_out, connection_data_in))
        };
        return client;
    }

    pub fn connect(mut self) -> Result<(), &'static str> {
        let (connection_data_out, connection_data_in) = match self.irc_connection_channel {
            Some(v) => v,
            None => return Err("Already connected")
        };
        self.irc_connection_channel = None;
        IrcConnection::create(self.name.as_slice(), self.server_address.as_slice(), connection_data_out, connection_data_in);
        self.spawn_dispatch_thread();
        return Ok(())
    }

    pub fn add_listener(&mut self, irc_command: &str, f: |&mut IrcMessageEvent|:'static) {
        let command_string = irc_command.into_string().to_ascii_lower();
        let mut listener_map = self.raw_listeners.write();
        {
            // I can't use a match here because then I would be borrowing listener_map mutably twice:
            // Once for the match statement, and a second time inside the None branch
            if listener_map.contains_key(&command_string) {
                listener_map.get_mut(&command_string).expect("Honestly, this won't happen.").push(f);
            } else {
                listener_map.insert(command_string, vec!(f));
            }
        }
        listener_map.downgrade();
    }

    pub fn add_command(&mut self, command: &str, f: |&mut CommandEvent|:'static) {
        let command_lower = command.into_string().to_ascii_lower();
        let mut command_map = self.commands.write();
        {
            // I can't use a match here because then I would be borrowing the command_map mutably twice:
            // Once for the match statement, and a second time inside the None branch
            if command_map.contains_key(&command_lower) {
                command_map.get_mut(&command_lower).expect("Honestly, this won't happen.").push(f);
            } else {
                command_map.insert(command_lower, vec!(f));
            }
        }
        command_map.downgrade();
    }

    fn spawn_dispatch_thread(self) {
        spawn(proc() {
            loop {
                let message: IrcMessage = self.data_in.recv();
                self.process_message(message);
            }
        });
    }

    // Noting: This has to be a separate method from spawn_dispatch_thread, so that we can name an 'a lifetime.
    // This allows us to give the new &str slices a specific lifetime, which I don't know a way to do without making a new function.
    fn process_message<'a>(&self, message: &'a IrcMessage) {
        let mask: Option<&'a str> = match message.mask {
            Some(ref v) => Some(v.as_slice()),
            None => None
        };
        let args_shared = message.args.iter().map(|s: &'a String| s.as_slice()).collect::<Vec<&'a str>>();
        let event = &mut IrcMessageEvent::new(&self.interface, message.command.as_slice(), args_shared.as_slice(), None);
        let mut listener_map = self.raw_listeners.write();
        {
            let mut listeners = match listener_map.get_mut(&message.command.to_ascii_lower()) {
                Some(v) => v,
                None => return
            };

            for listener in listeners.iter_mut() {
                (*listener)(event);
            }
        }
        listener_map.downgrade();
    }
}

pub struct IrcInterface {
    data_out: Sender<String>
}

impl IrcInterface {
    fn new(data_out: Sender<String>) -> IrcInterface {
        return IrcInterface {
            data_out: data_out
        }
    }
    pub fn send_raw(&self, line: String) {
        self.data_out.send(line);
    }

    pub fn send_command<'a>(&self, command: String, args: &[&str]) {
        let mut line = command;
        line.push(' ');
        line.push_str(args.connect(" ").as_slice());
        self.send_raw(line);
    }
}

        // client.add_listener("ping", |event: &mut InternalIrcEvent| {
        //     event.client.send(format!("PONG {}", event.args[0]).as_slice());
        // });
        // client.add_listener("004", |event: &mut InternalIrcEvent| {
        //     // TODO: Merge IrcClient and IrcConnection to deal with all this
        //     for channel in self.channels.iter() {
        //         event.client.send(format!("JOIN {}", channel.as_slice()).as_slice());
        //     }
        //     event.client.send("JOIN #bot");
        // });
        // client.add_listener("privmsg", |event: &mut InternalIrcEvent| {
        //     // let permitted = regex!(r"^Dabo[^!]*![^@]*@me.dabo.guru$");
        //     // let mask = event.mask.expect("PRIVMSG received without sender mask");
        //     let channel = event.args[0];
        //     if event.args[1].starts_with(format!(":{}", self.command_prefix.as_slice()).as_slice()) {
        //         let command = event.args[1].slice_from(2).into_string().to_ascii_lower();
        //         let mut command_map = self.commands.write();
        //         {
        //             let mut commands = match command_map.get_mut(&command) {
        //                 Some(v) => v,
        //                 None => return
        //             };

        //             let args = event.args.slice_from(2);
        //             let shared_self = &mut self.clone();
        //             let event = &mut CommandEvent::new(shared_self, command.as_slice(), args, event.mask);

        //             for command in commands.iter_mut() {
        //                 (*command)(event);
        //             }
        //         }
        //         command_map.downgrade();
        //     }

        // if event.args[1].eq_ignore_ascii_case(":quit") && permitted.is_match(mask) {
        //     event.client.send("QUIT :Testing.");
        // } else if event.args[1].eq_ignore_ascii_case(":raw") && permitted.is_match(mask) {
        //     event.client.send(event.args.slice_from(2).connect(" ").as_slice())
        // } else {
        //     event.client.send(format!("PRIVMSG {}", event.args.connect(" ")).as_slice());
        // }

pub struct IrcMessageEvent<'a> {
    pub client: &'a IrcInterface,
    pub command: &'a str,
    pub args: &'a [&'a str],
    pub mask: Option<&'a str>
}

pub struct CommandEvent<'a> {
    pub client: &'a IrcInterface,
    pub channel: &'a str,
    pub args: &'a [&'a str],
    pub mask: Option<&'a str>
}

impl <'a> IrcMessageEvent<'a> {
    fn new(client: &'a IrcInterface, command: &'a str, args: &'a [&'a str], mask: Option<&'a str>) -> IrcMessageEvent<'a> {
        return IrcMessageEvent {
            client: client,
            command: command,
            args: args,
            mask: mask
        }
    }
}

impl <'a> CommandEvent<'a> {
    fn new(client: &'a IrcInterface, channel: &'a str, args: &'a [&'a str], mask: Option<&'a str>) -> CommandEvent<'a> {
        return CommandEvent {
            client: client,
            channel: channel,
            args: args,
            mask: mask
        }
    }
}

#[deriving(Decodable, Encodable)]
pub struct ClientConfiguration {
    name: String,
    nick: String,
    user: String,
    real_name: String,
    channels: Vec<String>,
    address: String,
    command_prefix: String
}

pub fn load_config_from_file(path: &Path) -> Result<ClientConfiguration, String> {
    let config_contents = match File::open(path).read_to_string() {
        Ok(v) => v,
        Err(e) => return Err(format!("Failed to read config file: {}", e))
    };
    let client_config = match json::decode::<ClientConfiguration>(config_contents.as_slice()) {
        Ok(v) => v,
        Err(e) => return Err(format!("Failed to decode config file: {}", e))
    };
    return Ok(client_config)
}

pub fn start_from_config(config: ClientConfiguration) {
    let mut client = Client::new(config);
    client.add_command("say", |event: &mut CommandEvent| {
        event.client.send_command("PRIVMSG".to_string(), event.args);
    });
    client.connect().ok().expect("Failed to connect!");
}
<commit_msg>More fixing things, so that they work better. Actually get it to compile! Also, unrelated, implement Clone for IrcInterface.<commit_after>#![feature(globs)]

extern crate serialize;

use std::io::*;
use std::sync::{Arc, RWLock};
use std::ascii::AsciiExt;
use std::collections::HashMap;
use serialize::json;

struct IrcConnection {
    name: String,
    socket: TcpStream,
    data_out: Option<Sender<IrcMessage>>,
    data_in: Option<Receiver<String>>,
}

impl IrcConnection {
    pub fn create(name: &str, addr: &str, data_out: Sender<IrcMessage>, data_in: Receiver<String>) -> Result<(), IoError> {
        let socket = try!(TcpStream::connect(addr));
        let connection_receiving = IrcConnection {
            name: name.into_string(),
            socket: socket.clone(),
            data_out: Some(data_out),
            data_in: None
        };
        let connection_sending = IrcConnection {
            name: name.into_string(),
            socket: socket, // No need to clone a second time, as this is the last time we are using this socket
            data_out: None,
            data_in: Some(data_in)
        };
        connection_receiving.spawn_reading_thread().ok().expect("This was already setup to work");
        connection_sending.spawn_writing_thread().ok().expect("This was already setup to work");
        return Ok(())
    }

    fn spawn_reading_thread(self) -> Result<(), &'static str> {
        let data_out = match self.data_out {
            Some(ref v) => v.clone(),
            None => return Err("Can't start reading thread without data_out")
        };
        spawn(proc() {
            let mut reader = BufferedReader::new(self.socket.clone());
            loop {
                let whole_input = match reader.read_line() {
                    Ok(v) => v,
                    Err(e) => {
                        println!("Error: {}", e);
                        break;
                    }
                };
                let input = whole_input.trim_right();
                let message_split: Vec<&str> = input.split(' ').collect();
                let (command, args, possible_mask): (&str, &[&str], Option<String>) = if message_split[0].starts_with(":") {
                    (message_split[1], message_split.slice_from(2), Some(message_split[0].slice_from(1).into_string()))
                } else {
                    (message_split[0], message_split.slice_from(1), None)
                };
                match possible_mask {
                    Some(ref v) => println!("Received {} from {}: {}", command, v, args.connect(" ")),
                    None => println!("Received {}: {}", command, args.connect(" "))
                }
                let args_owned: Vec<String> = args.iter().map(|s: &&str| s.to_string()).collect();
                let message = IrcMessage::new(self.name.clone(), command.into_string(), args_owned, possible_mask);
                data_out.send(message);
            }
        });
        return Ok(())
    }

    fn spawn_writing_thread(mut self) -> Result<(), &'static str> {
        if (&self.data_in).is_none() {
            return Err("Can't start writing thread without data_in")
        };
        spawn(proc() {
            let data_in = self.data_in.expect("Already confirmed above");
            loop {
                let command = data_in.recv();
                println!("Sending: {}", command);
                self.socket.write(command.as_bytes()).ok().expect("Failed to write to stream");
                self.socket.write(b"\n").ok().expect("Failed to write to stream");
                self.socket.flush().ok().expect("Failed to flush stream");
            }
        });
        return Ok(())
    }
}

struct IrcMessage {
    server: String,
    command: String,
    args: Vec<String>,
    mask: Option<String>
}

impl IrcMessage {
    fn new(server: String, command: String, args: Vec<String>, mask: Option<String>) -> IrcMessage {
        return IrcMessage {
            server: server,
            command: command,
            args: args,
            mask: mask
        }
    }
}

struct Client {
    data_out: Sender<String>,
    data_in: Receiver<IrcMessage>,
    interface: IrcInterface,
    commands: Arc<RWLock<HashMap<String, Vec<|&mut CommandEvent|:'static>>>>,
    raw_listeners: Arc<RWLock<HashMap<String, Vec<|&mut IrcMessageEvent|:'static>>>>,
    name: String,
    bot_nick: String,
    bot_username: String,
    bot_real_name: String,
    server_address: String,
    command_prefix: String,
    // TODO: Update channels with channels actually joined, not just configured.
    channels: Vec<String>,
    irc_connection_channel: Option<(Sender<IrcMessage>, Receiver<String>)>,
}

impl Client {
    pub fn new(config: ClientConfiguration) -> Client {
        let (data_out, connection_data_in) = channel();
        let (connection_data_out, data_in) = channel();
        let client = Client {
            interface: IrcInterface::new(data_out.clone()),
            data_out: data_out,
            data_in: data_in,
            commands: Arc::new(RWLock::new(HashMap::new())),
            raw_listeners: Arc::new(RWLock::new(HashMap::new())),
            name: config.name,
            bot_nick: config.nick,
            bot_username: config.user,
            bot_real_name: config.real_name,
            channels: config.channels,
            server_address: config.address,
            command_prefix: config.command_prefix,
            irc_connection_channel: Some((connection_data_out, connection_data_in))
        };
        return client;
    }

    pub fn connect(mut self) -> Result<(), &'static str> {
        let (connection_data_out, connection_data_in) = match self.irc_connection_channel {
            Some(v) => v,
            None => return Err("Already connected")
        };
        self.irc_connection_channel = None;
        IrcConnection::create(self.name.as_slice(), self.server_address.as_slice(), connection_data_out, connection_data_in);
        self.spawn_dispatch_thread();
        return Ok(())
    }

    pub fn add_listener(&mut self, irc_command: &str, f: |&mut IrcMessageEvent|:'static) {
        let command_string = irc_command.into_string().to_ascii_lower();
        let mut listener_map = self.raw_listeners.write();
        {
            // I can't use a match here because then I would be borrowing listener_map mutably twice:
            // Once for the match statement, and a second time inside the None branch
            if listener_map.contains_key(&command_string) {
                listener_map.get_mut(&command_string).expect("Honestly, this won't happen.").push(f);
            } else {
                listener_map.insert(command_string, vec!(f));
            }
        }
        listener_map.downgrade();
    }

    pub fn add_command(&mut self, command: &str, f: |&mut CommandEvent|:'static) {
        let command_lower = command.into_string().to_ascii_lower();
        let mut command_map = self.commands.write();
        {
            // I can't use a match here because then I would be borrowing the command_map mutably twice:
            // Once for the match statement, and a second time inside the None branch
            if command_map.contains_key(&command_lower) {
                command_map.get_mut(&command_lower).expect("Honestly, this won't happen.").push(f);
            } else {
                command_map.insert(command_lower, vec!(f));
            }
        }
        command_map.downgrade();
    }

    fn spawn_dispatch_thread(self) {
        spawn(proc() {
            loop {
                let message: IrcMessage = self.data_in.recv();
                self.process_message(&message);
            }
        });
    }

    // Noting: This has to be a separate method from spawn_dispatch_thread, so that we can name an 'a lifetime.
    // This allows us to give the new &str slices a specific lifetime, which I don't know a way to do without making a new function.
    fn process_message<'a>(&self, message: &'a IrcMessage) {
        let shared_mask: Option<&str> = message.mask.as_ref().map(|s| &**s);
        let shared_args = message.args.iter().map(|s| &**s).collect::<Vec<&'a str>>();

        // Raw listeners
        let message_event = &mut IrcMessageEvent::new(&self.interface, message.command.as_slice(), shared_args.as_slice(), shared_mask);
        let mut listener_map = self.raw_listeners.write();
        // New scope so that listeners will go out of scope before we run listener_map.downgrade()
        {
            let mut listeners = match listener_map.get_mut(&message.command.to_ascii_lower()) {
                Some(v) => v,
                None => return
            };

            for listener in listeners.iter_mut() {
                (*listener)(message_event);
            }
        }
        listener_map.downgrade();
    }
}

pub struct IrcInterface {
    data_out: Sender<String>
}

impl IrcInterface {
    fn new(data_out: Sender<String>) -> IrcInterface {
        return IrcInterface {
            data_out: data_out
        }
    }
    pub fn send_raw(&self, line: String) {
        self.data_out.send(line);
    }

    pub fn send_command<'a>(&self, command: String, args: &[&str]) {
        let mut line = command;
        line.push(' ');
        line.push_str(args.connect(" ").as_slice());
        self.send_raw(line);
    }
}

impl Clone for IrcInterface {
    fn clone(&self) -> IrcInterface {
        return IrcInterface {
            data_out: self.data_out.clone()
        }
    }
}

        // client.add_listener("ping", |event: &mut InternalIrcEvent| {
        //     event.client.send(format!("PONG {}", event.args[0]).as_slice());
        // });
        // client.add_listener("004", |event: &mut InternalIrcEvent| {
        //     // TODO: Merge IrcClient and IrcConnection to deal with all this
        //     for channel in self.channels.iter() {
        //         event.client.send(format!("JOIN {}", channel.as_slice()).as_slice());
        //     }
        //     event.client.send("JOIN #bot");
        // });
        // client.add_listener("privmsg", |event: &mut InternalIrcEvent| {
        //     // let permitted = regex!(r"^Dabo[^!]*![^@]*@me.dabo.guru$");
        //     // let mask = event.mask.expect("PRIVMSG received without sender mask");
        //     let channel = event.args[0];
        //     if event.args[1].starts_with(format!(":{}", self.command_prefix.as_slice()).as_slice()) {
        //         let command = event.args[1].slice_from(2).into_string().to_ascii_lower();
        //         let mut command_map = self.commands.write();
        //         {
        //             let mut commands = match command_map.get_mut(&command) {
        //                 Some(v) => v,
        //                 None => return
        //             };

        //             let args = event.args.slice_from(2);
        //             let shared_self = &mut self.clone();
        //             let event = &mut CommandEvent::new(shared_self, command.as_slice(), args, event.mask);

        //             for command in commands.iter_mut() {
        //                 (*command)(event);
        //             }
        //         }
        //         command_map.downgrade();
        //     }

        // if event.args[1].eq_ignore_ascii_case(":quit") && permitted.is_match(mask) {
        //     event.client.send("QUIT :Testing.");
        // } else if event.args[1].eq_ignore_ascii_case(":raw") && permitted.is_match(mask) {
        //     event.client.send(event.args.slice_from(2).connect(" ").as_slice())
        // } else {
        //     event.client.send(format!("PRIVMSG {}", event.args.connect(" ")).as_slice());
        // }

pub struct IrcMessageEvent<'a> {
    pub client: &'a IrcInterface,
    pub command: &'a str,
    pub args: &'a [&'a str],
    pub mask: Option<&'a str>
}

pub struct CommandEvent<'a> {
    pub client: &'a IrcInterface,
    pub channel: &'a str,
    pub args: &'a [&'a str],
    pub mask: Option<&'a str>
}

impl <'a> IrcMessageEvent<'a> {
    fn new(client: &'a IrcInterface, command: &'a str, args: &'a [&'a str], mask: Option<&'a str>) -> IrcMessageEvent<'a> {
        return IrcMessageEvent {
            client: client,
            command: command,
            args: args,
            mask: mask
        }
    }
}

impl <'a> CommandEvent<'a> {
    fn new(client: &'a IrcInterface, channel: &'a str, args: &'a [&'a str], mask: Option<&'a str>) -> CommandEvent<'a> {
        return CommandEvent {
            client: client,
            channel: channel,
            args: args,
            mask: mask
        }
    }
}

#[deriving(Decodable, Encodable)]
pub struct ClientConfiguration {
    name: String,
    nick: String,
    user: String,
    real_name: String,
    channels: Vec<String>,
    address: String,
    command_prefix: String
}

pub fn load_config_from_file(path: &Path) -> Result<ClientConfiguration, String> {
    let config_contents = match File::open(path).read_to_string() {
        Ok(v) => v,
        Err(e) => return Err(format!("Failed to read config file: {}", e))
    };
    let client_config = match json::decode::<ClientConfiguration>(config_contents.as_slice()) {
        Ok(v) => v,
        Err(e) => return Err(format!("Failed to decode config file: {}", e))
    };
    return Ok(client_config)
}

pub fn start_from_config(config: ClientConfiguration) {
    let mut client = Client::new(config);
    client.add_command("say", |event: &mut CommandEvent| {
        event.client.send_command("PRIVMSG".to_string(), event.args);
    });
    client.connect().ok().expect("Failed to connect!");
}
<|endoftext|>