<commit_before>//! Metrics sent to GA.
//!
//! This module defines some events that can be used to capture usage metrics and send them
//! to a Google Analytics account.

use std::cell::RefCell;
use std::env::consts::{ARCH, OS};
use std::env::var;
use std::process::Command;
use std::str;
use std::sync::Mutex;

use anyhow::anyhow;
use lazy_static::lazy_static;
use log::{debug, warn};
use maplit::hashmap;
use reqwest::Client;
use uuid::Uuid;

static CIS: &'static [&str] = &[
  "CI",
  "CONTINUOUS_INTEGRATION",
  "BSTRUSE_BUILD_DIR",
  "APPVEYOR",
  "BUDDY_WORKSPACE_URL",
  "BUILDKITE",
  "CF_BUILD_URL",
  "CIRCLECI",
  "CODEBUILD_BUILD_ARN",
  "CONCOURSE_URL",
  "DRONE",
  "GITLAB_CI",
  "GO_SERVER_URL",
  "JENKINS_URL",
  "PROBO_ENVIRONMENT",
  "SEMAPHORE",
  "SHIPPABLE",
  "TDDIUM",
  "TEAMCITY_VERSION",
  "TF_BUILD",
  "TRAVIS",
  "WERCKER_ROOT"
];

/// Metric events to send
pub enum MetricEvent {
  /// Consumer test was run (number of interactions)
  ConsumerTestRun {
    /// Number of interactions in the test
    interactions: usize,
    /// Test framework used
    test_framework: String,
    /// Application name that executed the test
    app_name: String,
    /// Application version that executed the test
    app_version: String
  },

  /// Provider verification test ran (mode build tool or unit test etc.)
  ProviderVerificationRan {
    /// Number of verification tests run
    tests_run: usize,
    /// Test framework used
    test_framework: String,
    /// Application name that executed the test
    app_name: String,
    /// Application version that executed the test
    app_version: String
  }
}

impl MetricEvent {
  /// Application name for the event. For FFI calls, this will be the app doing the calling.
  pub(crate) fn app_name(&self) -> &str {
    match self {
      MetricEvent::ConsumerTestRun { app_name, .. } => app_name.as_str(),
      MetricEvent::ProviderVerificationRan { app_name, .. } => app_name.as_str()
    }
  }

  /// Application version for the event. For FFI calls, this will be the app doing the calling.
  pub(crate) fn app_version(&self) -> &str {
    match self {
      MetricEvent::ConsumerTestRun { app_version, .. } => app_version.as_str(),
      MetricEvent::ProviderVerificationRan { app_version, .. } => app_version.as_str()
    }
  }

  /// Test framework used (unit test framework or build tool)
  pub(crate) fn test_framework(&self) -> &str {
    match self {
      MetricEvent::ConsumerTestRun { test_framework, .. } => test_framework.as_str(),
      MetricEvent::ProviderVerificationRan { test_framework, .. } => test_framework.as_str()
    }
  }

  /// Event name
  pub(crate) fn name(&self) -> &str {
    match self {
      MetricEvent::ConsumerTestRun { .. } => "Pact consumer tests ran",
      MetricEvent::ProviderVerificationRan { .. } => "Pacts verified"
    }
  }

  /// Event category
  pub(crate) fn category(&self) -> &str {
    match self {
      MetricEvent::ConsumerTestRun { .. } => "ConsumerTest",
      MetricEvent::ProviderVerificationRan { .. } => "ProviderTest"
    }
  }

  /// Event action that occurred
  pub(crate) fn action(&self) -> &str {
    match self {
      MetricEvent::ConsumerTestRun { .. } => "Completed",
      MetricEvent::ProviderVerificationRan { .. } => "Completed"
    }
  }

  /// Value for the event
  pub(crate) fn value(&self) -> String {
    match self {
      MetricEvent::ConsumerTestRun { interactions, .. } => interactions.to_string(),
      MetricEvent::ProviderVerificationRan { tests_run, .. } => tests_run.to_string()
    }
  }
}

const GA_ACCOUNT: &str = "UA-117778936-1";
const GA_URL: &str = "https://www.google-analytics.com/collect";

lazy_static! {
  static ref WARNING_LOGGED: Mutex<RefCell<bool>> = Mutex::new(RefCell::new(false));
}

/// This sends anonymous metrics to a Google Analytics account. It is used to track usage of
/// Pact library and operating system versions. This can be disabled by setting the
/// `pact_do_not_track` environment variable to `true`.
///
/// This function needs to run in the context of a Tokio runtime.
pub fn send_metrics(event: MetricEvent) {
  let do_not_track = match var("pact_do_not_track") {
    Ok(val) => val == "true",
    Err(_) => false
  };

  if do_not_track {
    debug!("'pact_do_not_track' environment variable is set, will not send metrics");
  } else {
    match tokio::runtime::Handle::try_current() {
      Ok(handle) => {
        let mut guard = WARNING_LOGGED.lock().unwrap();
        let warning_logged = (*guard).get_mut();
        if *warning_logged == false {
          warn!(
            "\n\nPlease note:\n\
            Please note: we are tracking events anonymously to gather important usage statistics like Pact version \
            and operating system. To disable tracking, set the 'pact_do_not_track' system property or environment \
            variable to 'true'.\n\n"
          );
          *warning_logged = true;
        }

        handle.spawn(async move {
          let ci_context = if CIS.iter()
            .any(|n| var(n).map(|val| !val.is_empty()).unwrap_or(false)) {
            "CI"
          } else {
            "unknown"
          };
          let osarch = format!("{}-{}", OS, ARCH);
          let uid = hostname_hash();
          let value = event.value();

          let event_payload = hashmap!{
            "v" => "1",                                       // Version of the API
            "t" => "event",                                   // Hit type, Specifies the metric is for an event
            "tid" => GA_ACCOUNT,                              // Property ID
            "cid" => uid.as_str(),                            // Anonymous Client ID.
            "an" => event.app_name(),                         // App name.
            "aid" => event.app_name(),                        // App Id
            "av" => event.app_version(),                      // App version.
            "aip" => "true",                                  // Anonymise IP address
            "ds" => "client",                                 // Data source
            "cd2" => ci_context,                              // Custom Dimension 2: context
            "cd3" => osarch.as_str(),                         // Custom Dimension 3: osarch
            "cd6" => event.test_framework(),                  // Custom Dimension 6: test_framework
            "cd7" => env!("CARGO_PKG_VERSION"),               // Custom Dimension 7: platform_version
            "el" => event.name(),                             // Event
            "ec" => event.category(),                         // Category
            "ea" => event.action(),                           // Action
            "ev" => value.as_str()                            // Value
          };
          debug!("Sending event to GA - {:?}", event_payload);
          let result = Client::new().post(GA_URL)
            .form(&event_payload)
            .send()
            .await;
          if let Err(err) = result {
            debug!("Failed to post event - {}", err);
          }
        });
      },
      Err(err) => {
        debug!("Could not get the tokio runtime, will not send metrics - {}", err)
      }
    }
  }
}

/// Calculates a one-way hash of the hostname where the event occurred
fn hostname_hash() -> String {
  let host_name = if OS == "windows" {
    var("COMPUTERNAME")
  } else {
    var("HOSTNAME")
  }.or_else(|_| {
    exec_hostname_command()
  }).unwrap_or_else(|_| {
    Uuid::new_v4().to_string()
  });

  let digest = md5::compute(host_name.as_bytes());
  format!("{:x}", digest)
}

/// Execute the hostname command to get the hostname
fn exec_hostname_command() -> anyhow::Result<String> {
  match Command::new("hostname").output() {
    Ok(output) => if output.status.success() {
      Ok(str::from_utf8(&*output.stdout)?.to_string())
    } else {
      Err(anyhow!("Failed to invoke hostname command: status {}", output.status))
    }
    Err(err) => Err(anyhow!("Failed to invoke hostname command: {}", err))
  }
}
<commit_msg>fix: PACT_DO_NOT_TRACK should be upper case<commit_after>//! Metrics sent to GA.
//!
//! This module defines some events that can be used to capture usage metrics and send them
//! to a Google Analytics account.

use std::cell::RefCell;
use std::env::consts::{ARCH, OS};
use std::env::var;
use std::process::Command;
use std::str;
use std::sync::Mutex;

use anyhow::anyhow;
use lazy_static::lazy_static;
use log::{debug, warn};
use maplit::hashmap;
use reqwest::Client;
use uuid::Uuid;

static CIS: &'static [&str] = &[
  "CI",
  "CONTINUOUS_INTEGRATION",
  "BSTRUSE_BUILD_DIR",
  "APPVEYOR",
  "BUDDY_WORKSPACE_URL",
  "BUILDKITE",
  "CF_BUILD_URL",
  "CIRCLECI",
  "CODEBUILD_BUILD_ARN",
  "CONCOURSE_URL",
  "DRONE",
  "GITLAB_CI",
  "GO_SERVER_URL",
  "JENKINS_URL",
  "PROBO_ENVIRONMENT",
  "SEMAPHORE",
  "SHIPPABLE",
  "TDDIUM",
  "TEAMCITY_VERSION",
  "TF_BUILD",
  "TRAVIS",
  "WERCKER_ROOT"
];

/// Metric events to send
pub enum MetricEvent {
  /// Consumer test was run (number of interactions)
  ConsumerTestRun {
    /// Number of interactions in the test
    interactions: usize,
    /// Test framework used
    test_framework: String,
    /// Application name that executed the test
    app_name: String,
    /// Application version that executed the test
    app_version: String
  },

  /// Provider verification test ran (mode build tool or unit test etc.)
  ProviderVerificationRan {
    /// Number of verification tests run
    tests_run: usize,
    /// Test framework used
    test_framework: String,
    /// Application name that executed the test
    app_name: String,
    /// Application version that executed the test
    app_version: String
  }
}

impl MetricEvent {
  /// Application name for the event. For FFI calls, this will be the app doing the calling.
  pub(crate) fn app_name(&self) -> &str {
    match self {
      MetricEvent::ConsumerTestRun { app_name, .. } => app_name.as_str(),
      MetricEvent::ProviderVerificationRan { app_name, .. } => app_name.as_str()
    }
  }

  /// Application version for the event. For FFI calls, this will be the app doing the calling.
  pub(crate) fn app_version(&self) -> &str {
    match self {
      MetricEvent::ConsumerTestRun { app_version, .. } => app_version.as_str(),
      MetricEvent::ProviderVerificationRan { app_version, .. } => app_version.as_str()
    }
  }

  /// Test framework used (unit test framework or build tool)
  pub(crate) fn test_framework(&self) -> &str {
    match self {
      MetricEvent::ConsumerTestRun { test_framework, .. } => test_framework.as_str(),
      MetricEvent::ProviderVerificationRan { test_framework, .. } => test_framework.as_str()
    }
  }

  /// Event name
  pub(crate) fn name(&self) -> &str {
    match self {
      MetricEvent::ConsumerTestRun { .. } => "Pact consumer tests ran",
      MetricEvent::ProviderVerificationRan { .. } => "Pacts verified"
    }
  }

  /// Event category
  pub(crate) fn category(&self) -> &str {
    match self {
      MetricEvent::ConsumerTestRun { .. } => "ConsumerTest",
      MetricEvent::ProviderVerificationRan { .. } => "ProviderTest"
    }
  }

  /// Event action that occurred
  pub(crate) fn action(&self) -> &str {
    match self {
      MetricEvent::ConsumerTestRun { .. } => "Completed",
      MetricEvent::ProviderVerificationRan { .. } => "Completed"
    }
  }

  /// Value for the event
  pub(crate) fn value(&self) -> String {
    match self {
      MetricEvent::ConsumerTestRun { interactions, .. } => interactions.to_string(),
      MetricEvent::ProviderVerificationRan { tests_run, .. } => tests_run.to_string()
    }
  }
}

const GA_ACCOUNT: &str = "UA-117778936-1";
const GA_URL: &str = "https://www.google-analytics.com/collect";

lazy_static! {
  static ref WARNING_LOGGED: Mutex<RefCell<bool>> = Mutex::new(RefCell::new(false));
}

/// This sends anonymous metrics to a Google Analytics account. It is used to track usage of
/// Pact library and operating system versions. This can be disabled by setting the
/// `pact_do_not_track` environment variable to `true`.
///
/// This function needs to run in the context of a Tokio runtime.
pub fn send_metrics(event: MetricEvent) {
  let do_not_track = var("PACT_DO_NOT_TRACK")
    .or_else(|_| var("pact_do_not_track"))
    .map(|v| v == "true")
    .unwrap_or(false);

  if do_not_track {
    debug!("'PACT_DO_NOT_TRACK' environment variable is set, will not send metrics");
  } else {
    match tokio::runtime::Handle::try_current() {
      Ok(handle) => {
        let mut guard = WARNING_LOGGED.lock().unwrap();
        let warning_logged = (*guard).get_mut();
        if *warning_logged == false {
          warn!(
            "\n\nPlease note:\n\
            Please note: we are tracking events anonymously to gather important usage statistics like Pact version \
            and operating system. To disable tracking, set the 'PACT_DO_NOT_TRACK' environment \
            variable to 'true'.\n\n"
          );
          *warning_logged = true;
        }

        handle.spawn(async move {
          let ci_context = if CIS.iter()
            .any(|n| var(n).map(|val| !val.is_empty()).unwrap_or(false)) {
            "CI"
          } else {
            "unknown"
          };
          let osarch = format!("{}-{}", OS, ARCH);
          let uid = hostname_hash();
          let value = event.value();

          let event_payload = hashmap!{
            "v" => "1",                                       // Version of the API
            "t" => "event",                                   // Hit type, Specifies the metric is for an event
            "tid" => GA_ACCOUNT,                              // Property ID
            "cid" => uid.as_str(),                            // Anonymous Client ID.
            "an" => event.app_name(),                         // App name.
            "aid" => event.app_name(),                        // App Id
            "av" => event.app_version(),                      // App version.
            "aip" => "true",                                  // Anonymise IP address
            "ds" => "client",                                 // Data source
            "cd2" => ci_context,                              // Custom Dimension 2: context
            "cd3" => osarch.as_str(),                         // Custom Dimension 3: osarch
            "cd6" => event.test_framework(),                  // Custom Dimension 6: test_framework
            "cd7" => env!("CARGO_PKG_VERSION"),               // Custom Dimension 7: platform_version
            "el" => event.name(),                             // Event
            "ec" => event.category(),                         // Category
            "ea" => event.action(),                           // Action
            "ev" => value.as_str()                            // Value
          };
          debug!("Sending event to GA - {:?}", event_payload);
          let result = Client::new().post(GA_URL)
            .form(&event_payload)
            .send()
            .await;
          if let Err(err) = result {
            debug!("Failed to post event - {}", err);
          }
        });
      },
      Err(err) => {
        debug!("Could not get the tokio runtime, will not send metrics - {}", err)
      }
    }
  }
}

/// Calculates a one-way hash of the hostname where the event occurred
fn hostname_hash() -> String {
  let host_name = if OS == "windows" {
    var("COMPUTERNAME")
  } else {
    var("HOSTNAME")
  }.or_else(|_| {
    exec_hostname_command()
  }).unwrap_or_else(|_| {
    Uuid::new_v4().to_string()
  });

  let digest = md5::compute(host_name.as_bytes());
  format!("{:x}", digest)
}

/// Execute the hostname command to get the hostname
fn exec_hostname_command() -> anyhow::Result<String> {
  match Command::new("hostname").output() {
    Ok(output) => if output.status.success() {
      Ok(str::from_utf8(&*output.stdout)?.to_string())
    } else {
      Err(anyhow!("Failed to invoke hostname command: status {}", output.status))
    }
    Err(err) => Err(anyhow!("Failed to invoke hostname command: {}", err))
  }
}
<|endoftext|>


<commit_before>pub struct List {
    head: Link,
}

impl List {
    pub fn new() -> Self {
        List { head: None }
    }

    pub fn push(&mut self, elem: i32) {
        let new_node = Box::new(Node {
            elem: elem,
            next: self.head.take(),
        });

        self.head = Some(new_node);
    }

    pub fn pop(&mut self) -> Option<i32> {
        self.head.take().map(|node| {
            self.head = node.next;
            node.elem
        })
    }
}

impl Drop for List {
    fn drop(&mut self) {
        let mut cur_link = self.head.take();
        while let Some(mut boxed_node) = cur_link {
            cur_link = boxed_node.next.take();
            // Drops boxed_node without its next field
            // Prevents unbounded recursion
        }
    }
}

type Link = Option<Box<Node>>;

struct Node {
    elem: i32,
    next: Link,
}

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

    #[test]
    fn basics() {
        let mut list = List::new();

        // Empty list behaves
        assert_eq!(list.pop(), None);

        list.push(1);
        list.push(2);
        list.push(3);

        assert_eq!(list.pop(), Some(3));
        assert_eq!(list.pop(), Some(2));

        list.push(4);
        list.push(5);

        assert_eq!(list.pop(), Some(5));
        assert_eq!(list.pop(), Some(4));

        // Check exhaustion
        assert_eq!(list.pop(), Some(1));
        assert_eq!(list.pop(), None);
    }
}
<commit_msg>[Rust] Too Many Linked Lists - An Ok Stack<commit_after>pub struct List<T> {
    head: Link<T>,
}

pub struct IntoIter<T>(List<T>);

impl<T> Iterator for IntoIter<T> {
    type Item = T;
    fn next(&mut self) -> Option<Self::Item> {
        self.0.pop()
    }
}

pub struct Iter<'a, T> {
    next: Option<&'a Node<T>>,
}

impl <'a, T> Iterator for Iter<'a, T> {
    type Item = &'a T;

    fn next(&mut self) -> Option<Self::Item> {
        self.next.map(|node| {
            self.next = node.next.as_ref().map(|node| &**node);
            &node.elem
        })
    }
}

pub struct IterMut<'a, T> {
    next: Option<&'a mut Node<T>>,
}

impl<'a, T> Iterator for IterMut<'a, T> {
    type Item = &'a mut T;

    fn next(&mut self) -> Option<Self::Item> {
        self.next.take().map(|node| {
            self.next = node.next.as_mut().map(|node| &mut **node);
            &mut node.elem
        })
    }
}

impl<T> List<T> {
    pub fn iter_mut(&mut self) -> IterMut<'_, T> {
        IterMut { next: self.head.as_mut().map(|node| &mut **node) }
    }
}

impl<T> List<T> {
    pub fn new() -> Self {
        List { head: None }
    }

    pub fn push(&mut self, elem: T) {
        let new_node = Box::new(Node {
            elem: elem,
            next: self.head.take(),
        });

        self.head = Some(new_node);
    }

    pub fn pop(&mut self) -> Option<T> {
        self.head.take().map(|node| {
            self.head = node.next;
            node.elem
        })
    }

    pub fn peek(&self) -> Option<&T> {
        self.head.as_ref().map(|node| {
            &node.elem
        })
    }

    pub fn peek_mut(&mut self) -> Option<&mut T> {
        self.head.as_mut().map(|node| {
            &mut node.elem
        })
    }

    pub fn into_iter(self) -> IntoIter<T> {
        IntoIter(self)
    }

    pub fn iter(&self) -> Iter<T> {
        Iter { next: self.head.as_ref().map(|node| &**node) }
    }
}

impl<T> Drop for List<T> {
    fn drop(&mut self) {
        let mut cur_link = self.head.take();
        while let Some(mut boxed_node) = cur_link {
            cur_link = boxed_node.next.take();
            // Drops boxed_node without its next field
            // Prevents unbounded recursion
        }
    }
}

type Link<T> = Option<Box<Node<T>>>;

struct Node<T> {
    elem: T,
    next: Link<T>,
}

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

    #[test]
    fn basics() {
        let mut list = List::new();

        // Empty list behaves
        assert_eq!(list.pop(), None);

        list.push(1);
        list.push(2);
        list.push(3);

        assert_eq!(list.pop(), Some(3));
        assert_eq!(list.pop(), Some(2));

        list.push(4);
        list.push(5);

        assert_eq!(list.pop(), Some(5));
        assert_eq!(list.pop(), Some(4));

        // Check exhaustion
        assert_eq!(list.pop(), Some(1));
        assert_eq!(list.pop(), None);
    }

    #[test]
    fn peek() {
        let mut list: List<i32> = List::new();

        list.push(4);
        assert_eq!(list.peek(), Some(&4));

        list.peek_mut().map(|value| {
            *value = 8
        });

        assert_eq!(list.peek(), Some(&8));
    }

    #[test]
    fn into_iter() {
        let mut list = List::new();
        list.push(1);
        list.push(2);
        list.push(3);

        let mut iter = list.into_iter();
        assert_eq!(iter.next(), Some(3));
        assert_eq!(iter.next(), Some(2));
        assert_eq!(iter.next(), Some(1));
        assert_eq!(iter.next(), None);
    }

    #[test]
    fn iter() {
        let mut list = List::new();
        list.push(1);
        list.push(2);
        list.push(3);

        let mut iter = list.iter();
        assert_eq!(iter.next(), Some(&3));
        assert_eq!(iter.next(), Some(&2));
        assert_eq!(iter.next(), Some(&1));
        assert_eq!(iter.next(), None);
    }

    #[test]
    fn iter_mut() {
        let mut list = List::new();
        list.push(1);
        list.push(2);
        list.push(3);

        let mut iter = list.iter_mut();
        assert_eq!(iter.next(), Some(&mut 3));
        assert_eq!(iter.next(), Some(&mut 2));
        assert_eq!(iter.next(), Some(&mut 1));
        assert_eq!(iter.next(), None);
    }
}
<|endoftext|>


<commit_before>extern crate gcc;
extern crate bindgen;
#[cfg(feature = "static")]
extern crate meson;
extern crate gl_generator;

use std::env;
#[allow(unused_imports)]
use std::path::{Path, PathBuf};
use std::fs::File;
use gl_generator::{Registry, Api, Profile, Fallbacks, StaticGenerator};

fn main() {
    let generated = bindgen::builder()
        .derive_debug(true)
        .derive_default(true)
        .generate_comments(true)
        .header("src/wlroots.h")
        .whitelisted_type(r"^wlr_.*$")
        .whitelisted_function(r"^wlr_.*$")
        .ctypes_prefix("libc")
        .clang_arg("-Iwlroots/include")
        .clang_arg("-Iwlroots/include/wlr")
        .clang_arg("-Iwlroots/include/xcursor")
        .clang_arg("-I/usr/include/pixman-1")
        // Work around bug https://github.com/rust-lang-nursery/rust-bindgen/issues/687
        .hide_type("FP_NAN")
        .hide_type("FP_INFINITE")
        .hide_type("FP_ZERO")
        .hide_type("FP_SUBNORMAL")
        .hide_type("FP_NORMAL")
        .generate().unwrap();

    println!("cargo:rustc-link-lib=dylib=X11");
    println!("cargo:rustc-link-lib=dylib=X11-xcb");
    println!("cargo:rustc-link-lib=dylib=xkbcommon");
    println!("cargo:rustc-link-lib=dylib=xcb");
    println!("cargo:rustc-link-lib=dylib=cap");
    println!("cargo:rustc-link-lib=dylib=wayland-egl");
    println!("cargo:rustc-link-lib=dylib=wayland-client");
    println!("cargo:rustc-link-lib=dylib=wayland-server");
    println!("cargo:rustc-link-lib=dylib=EGL");
    println!("cargo:rustc-link-lib=dylib=GL");
    println!("cargo:rustc-link-lib=dylib=gbm");
    println!("cargo:rustc-link-lib=dylib=drm");
    println!("cargo:rustc-link-lib=dylib=input");
    println!("cargo:rustc-link-lib=dylib=udev");
    println!("cargo:rustc-link-lib=dylib=systemd");
    println!("cargo:rustc-link-lib=dylib=dbus-1");
    println!("cargo:rustc-link-lib=dylib=pixman-1");

    if !cfg!(feature = "static") {
        println!("cargo:rustc-link-lib=dylib=wlroots");
        println!("cargo:rustc-link-search=native=/usr/local/lib");
    }

    // generate the bindings
    generated.write_to_file("src/gen.rs").unwrap();

    meson();

    if cfg!(feature = "example") {

        // Example Khronos building stuff
        let dest = env::var("OUT_DIR").unwrap();
        let mut file = File::create(&Path::new(&dest).join("bindings.rs")).unwrap();
        Registry::new(Api::Gl, (4, 5), Profile::Core, Fallbacks::All, [])
            .write_bindings(StaticGenerator, &mut file)
            .unwrap();

        // Build share.d for examples
        let example_generated = bindgen::builder()
            .derive_debug(true)
            .derive_default(true)
            .generate_comments(true)
            .header("wlroots/examples/shared.h")
            .clang_arg("-Iwlroots/include")
            .clang_arg("-Iwlroots/include/wlr")
            .clang_arg("-Iwlroots/include/xcursor")
            .clang_arg("-I/usr/include/pixman-1")
            // Work around bug https://github.com/rust-lang-nursery/rust-bindgen/issues/687
            .hide_type("FP_NAN")
            .hide_type("FP_INFINITE")
            .hide_type("FP_ZERO")
            .hide_type("FP_SUBNORMAL")
            .hide_type("FP_NORMAL")
            .generate().unwrap();
        example_generated.write_to_file(format!("{}/shared.rs", dest)).unwrap();

        // Build shared.c for examples
        let mut config = gcc::Build::new();
        config.flag("-Wall");
        config.flag("-Wpedantic");
        config.flag("-Iwlroots/include");
        config.flag("-Iwlroots/include/wlr");
        config.file("wlroots/examples/shared.c");
        config.compile("libshared.a");

        // Link against libpam
        println!("cargo:rustc-flags=-l shared")
    }
}

#[cfg(not(feature = "static"))]
fn meson() {}

#[cfg(feature = "static")]
fn meson() {
    let build_path = PathBuf::from(env::var("OUT_DIR")
        .expect("Could not get OUT_DIR env variable"));
    build_path.join("build");
    let build_path_str = build_path.to_str()
        .expect("Could not turn build path into a string");
    println!("cargo:rustc-link-search=native=wlroots");
    println!("cargo:rustc-link-search=native={}/lib", build_path_str);
    println!("cargo:rustc-link-search=native={}/lib64", build_path_str);
    println!("cargo:rustc-link-search=native={}/build/", build_path_str);
    if cfg!(feature = "static") {
        println!("cargo:rustc-link-search=native={}/util/", build_path_str);
        println!("cargo:rustc-link-search=native={}/types/", build_path_str);
        println!("cargo:rustc-link-search=native={}/protocol/", build_path_str);
        println!("cargo:rustc-link-search=native={}/xcursor/", build_path_str);
        println!("cargo:rustc-link-search=native={}/xwayland/", build_path_str);
        println!("cargo:rustc-link-search=native={}/backend/", build_path_str);
        println!("cargo:rustc-link-search=native={}/render/", build_path_str);

        println!("cargo:rustc-link-lib=static=wlr_util");
        println!("cargo:rustc-link-lib=static=wlr_types");
        println!("cargo:rustc-link-lib=static=wlr_xcursor");
        println!("cargo:rustc-link-lib=static=wlr_xwayland");
        println!("cargo:rustc-link-lib=static=wlr_backend");
        println!("cargo:rustc-link-lib=static=wlr_render");
        // wayland protocols
        println!("cargo:rustc-link-lib=static=wl_protos");
    }

    meson::build("wlroots", build_path_str);
}
<commit_msg>Added xkb types to wlroots-sys<commit_after>extern crate gcc;
extern crate bindgen;
#[cfg(feature = "static")]
extern crate meson;
extern crate gl_generator;

use std::env;
#[allow(unused_imports)]
use std::path::{Path, PathBuf};
use std::fs::File;
use gl_generator::{Registry, Api, Profile, Fallbacks, StaticGenerator};

fn main() {
    let generated = bindgen::builder()
        .derive_debug(true)
        .derive_default(true)
        .generate_comments(true)
        .header("src/wlroots.h")
        .whitelisted_type(r"^wlr_.*$")
        .whitelisted_type(r"^xkb.*$")
        .whitelisted_function(r"^wlr_.*$")
        .ctypes_prefix("libc")
        .clang_arg("-Iwlroots/include")
        .clang_arg("-Iwlroots/include/wlr")
        .clang_arg("-Iwlroots/include/xcursor")
        .clang_arg("-I/usr/include/pixman-1")
        // Work around bug https://github.com/rust-lang-nursery/rust-bindgen/issues/687
        .hide_type("FP_NAN")
        .hide_type("FP_INFINITE")
        .hide_type("FP_ZERO")
        .hide_type("FP_SUBNORMAL")
        .hide_type("FP_NORMAL")
        .generate().unwrap();

    println!("cargo:rustc-link-lib=dylib=X11");
    println!("cargo:rustc-link-lib=dylib=X11-xcb");
    println!("cargo:rustc-link-lib=dylib=xkbcommon");
    println!("cargo:rustc-link-lib=dylib=xcb");
    println!("cargo:rustc-link-lib=dylib=cap");
    println!("cargo:rustc-link-lib=dylib=wayland-egl");
    println!("cargo:rustc-link-lib=dylib=wayland-client");
    println!("cargo:rustc-link-lib=dylib=wayland-server");
    println!("cargo:rustc-link-lib=dylib=EGL");
    println!("cargo:rustc-link-lib=dylib=GL");
    println!("cargo:rustc-link-lib=dylib=gbm");
    println!("cargo:rustc-link-lib=dylib=drm");
    println!("cargo:rustc-link-lib=dylib=input");
    println!("cargo:rustc-link-lib=dylib=udev");
    println!("cargo:rustc-link-lib=dylib=systemd");
    println!("cargo:rustc-link-lib=dylib=dbus-1");
    println!("cargo:rustc-link-lib=dylib=pixman-1");

    if !cfg!(feature = "static") {
        println!("cargo:rustc-link-lib=dylib=wlroots");
        println!("cargo:rustc-link-search=native=/usr/local/lib");
    }

    // generate the bindings
    generated.write_to_file("src/gen.rs").unwrap();

    meson();

    if cfg!(feature = "example") {

        // Example Khronos building stuff
        let dest = env::var("OUT_DIR").unwrap();
        let mut file = File::create(&Path::new(&dest).join("bindings.rs")).unwrap();
        Registry::new(Api::Gl, (4, 5), Profile::Core, Fallbacks::All, [])
            .write_bindings(StaticGenerator, &mut file)
            .unwrap();

        // Build share.d for examples
        let example_generated = bindgen::builder()
            .derive_debug(true)
            .derive_default(true)
            .generate_comments(true)
            .header("wlroots/examples/shared.h")
            .clang_arg("-Iwlroots/include")
            .clang_arg("-Iwlroots/include/wlr")
            .clang_arg("-Iwlroots/include/xcursor")
            .clang_arg("-I/usr/include/pixman-1")
            // Work around bug https://github.com/rust-lang-nursery/rust-bindgen/issues/687
            .hide_type("FP_NAN")
            .hide_type("FP_INFINITE")
            .hide_type("FP_ZERO")
            .hide_type("FP_SUBNORMAL")
            .hide_type("FP_NORMAL")
            .generate().unwrap();
        example_generated.write_to_file(format!("{}/shared.rs", dest)).unwrap();

        // Build shared.c for examples
        let mut config = gcc::Build::new();
        config.flag("-Wall");
        config.flag("-Wpedantic");
        config.flag("-Iwlroots/include");
        config.flag("-Iwlroots/include/wlr");
        config.file("wlroots/examples/shared.c");
        config.compile("libshared.a");

        // Link against libpam
        println!("cargo:rustc-flags=-l shared")
    }
}

#[cfg(not(feature = "static"))]
fn meson() {}

#[cfg(feature = "static")]
fn meson() {
    let build_path = PathBuf::from(env::var("OUT_DIR")
        .expect("Could not get OUT_DIR env variable"));
    build_path.join("build");
    let build_path_str = build_path.to_str()
        .expect("Could not turn build path into a string");
    println!("cargo:rustc-link-search=native=wlroots");
    println!("cargo:rustc-link-search=native={}/lib", build_path_str);
    println!("cargo:rustc-link-search=native={}/lib64", build_path_str);
    println!("cargo:rustc-link-search=native={}/build/", build_path_str);
    if cfg!(feature = "static") {
        println!("cargo:rustc-link-search=native={}/util/", build_path_str);
        println!("cargo:rustc-link-search=native={}/types/", build_path_str);
        println!("cargo:rustc-link-search=native={}/protocol/", build_path_str);
        println!("cargo:rustc-link-search=native={}/xcursor/", build_path_str);
        println!("cargo:rustc-link-search=native={}/xwayland/", build_path_str);
        println!("cargo:rustc-link-search=native={}/backend/", build_path_str);
        println!("cargo:rustc-link-search=native={}/render/", build_path_str);

        println!("cargo:rustc-link-lib=static=wlr_util");
        println!("cargo:rustc-link-lib=static=wlr_types");
        println!("cargo:rustc-link-lib=static=wlr_xcursor");
        println!("cargo:rustc-link-lib=static=wlr_xwayland");
        println!("cargo:rustc-link-lib=static=wlr_backend");
        println!("cargo:rustc-link-lib=static=wlr_render");
        // wayland protocols
        println!("cargo:rustc-link-lib=static=wl_protos");
    }

    meson::build("wlroots", build_path_str);
}
<|endoftext|>


<commit_before>//! Enumeration of all supported IRCv3 capability extensions.

/// List of all supported IRCv3 capability extensions from the
/// [IRCv3 specifications](http://ircv3.net/irc/).
#[derive(Debug, PartialEq)]
pub enum Capability {
    /// [multi-prefix](http://ircv3.net/specs/extensions/multi-prefix-3.1.html)
    MultiPrefix,
    /// [account-notify](http://ircv3.net/specs/extensions/account-notify-3.1.html)
    AccountNotify,
    /// [away-notify](http://ircv3.net/specs/extensions/away-notify-3.1.html)
    AwayNotify,
    /// [extended-join](http://ircv3.net/specs/extensions/extended-join-3.1.html)
    ExtendedJoin,
}

/// List of IRCv3 capability negotiation versions.
pub enum NegotiationVersion {
    /// [IRCv3.1](http://ircv3.net/specs/core/capability-negotiation-3.1.html)
    V301,
    /// [IRCv3.2](http://ircv3.net/specs/core/capability-negotiation-3.2.html)
    V302,
}

impl AsRef<str> for Capability {
    fn as_ref(&self) -> &str {
        match *self {
            Capability::MultiPrefix => "multi-prefix",
            Capability::AccountNotify => "account-notify",
            Capability::AwayNotify => "away-notify",
            Capability::ExtendedJoin => "extended-join",
        }
    }
}

#[cfg(test)]
mod test {
    use super::Capability::*;

    #[test]
    fn to_str() {
        assert_eq!(MultiPrefix.as_ref(), "multi-prefix");
        assert_eq!(AccountNotify.as_ref(), "account-notify");
        assert_eq!(AwayNotify.as_ref(), "away-notify");
        assert_eq!(ExtendedJoin.as_ref(), "extended-join");
    }
}
<commit_msg>Added monitor to capability request API.<commit_after>//! Enumeration of all supported IRCv3 capability extensions.

/// List of all supported IRCv3 capability extensions from the
/// [IRCv3 specifications](http://ircv3.net/irc/).
#[derive(Debug, PartialEq)]
pub enum Capability {
    /// [multi-prefix](http://ircv3.net/specs/extensions/multi-prefix-3.1.html)
    MultiPrefix,
    /// [account-notify](http://ircv3.net/specs/extensions/account-notify-3.1.html)
    AccountNotify,
    /// [away-notify](http://ircv3.net/specs/extensions/away-notify-3.1.html)
    AwayNotify,
    /// [extended-join](http://ircv3.net/specs/extensions/extended-join-3.1.html)
    ExtendedJoin,
    /// [monitor](http://ircv3.net/specs/core/monitor-3.2.html)
    Monitor,
}

/// List of IRCv3 capability negotiation versions.
pub enum NegotiationVersion {
    /// [IRCv3.1](http://ircv3.net/specs/core/capability-negotiation-3.1.html)
    V301,
    /// [IRCv3.2](http://ircv3.net/specs/core/capability-negotiation-3.2.html)
    V302,
}

impl AsRef<str> for Capability {
    fn as_ref(&self) -> &str {
        match *self {
            Capability::MultiPrefix => "multi-prefix",
            Capability::AccountNotify => "account-notify",
            Capability::AwayNotify => "away-notify",
            Capability::ExtendedJoin => "extended-join",
            Capability::Monitor => "monitor",
        }
    }
}

#[cfg(test)]
mod test {
    use super::Capability::*;

    #[test]
    fn to_str() {
        assert_eq!(MultiPrefix.as_ref(), "multi-prefix");
        assert_eq!(AccountNotify.as_ref(), "account-notify");
        assert_eq!(AwayNotify.as_ref(), "away-notify");
        assert_eq!(ExtendedJoin.as_ref(), "extended-join");
        assert_eq!(Monitor.as_ref(), "monitor");

    }
}
<|endoftext|>


<commit_before>//! Hardware Abstraction Layer
//!
//! The goal of this module is to abstract the machine/architecture so that
//! the API is the same no matter the underlying hardware.
//!
//! For now only Xen (x86/x86_64) is supported. A goal for the future would
//! be to be compatible with the virtio standard as well and support Xen arm

use io::{Read, Write, Result};

pub mod mmu;

pub use self::hw_imp::*;

/// Generic console wrapper
pub struct Console<'a>(HwConsoleType<'a>);

impl<'a> Read for Console<'a> {
    #[inline]
    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
        self.0.read(buf)
    }
}

impl<'a> Write for Console<'a> {
    #[inline]
    fn write(&mut self, buf: &[u8]) -> Result<usize> {
        self.0.write(buf)
    }

    #[inline]
    fn flush(&mut self) -> Result<()> {
        self.0.flush()
    }
}

#[cfg(feature = "xen")]
pub mod xen;

#[cfg(feature = "xen")]
mod hw_imp {
    use super::xen;
    use super::Console;

    use cell::GlobalCellMutRef;

    /// Abstracts the actual hardware console type
    pub type HwConsoleType<'a> = GlobalCellMutRef<'a, xen::console::Console>;

    #[inline]
    /// Unprotected access to the hardware console
    ///
    /// This *SHOULD NOT* be used as is.
    ///
    /// Instead use [`io::stdout()`][stdout] or [`io::stdin()`][stdin]
    ///
    /// [stdout]: ../io/fn.stdout.html
    /// [stdin]: ../io/fn.stdin.html
    pub fn console<'a>() -> Console<'a> {
        Console(xen::console::console())
    }

    #[inline]
    /// Disable local interrupt delivery
    pub fn local_irq_disable() {
        xen::disable_upcalls();
    }

    #[inline]
    /// Enable local interrupt delivery
    pub fn local_irq_enable() {
        xen::enable_upcalls();
    }

    #[inline]
    /// Disable local interrupt delivery and return the previous state of
    /// interrupt delivery
    pub fn local_irq_save() -> usize {
        xen::disable_upcalls() as usize
    }

    #[inline]
    /// Restore a local interrupt delivery state
    pub fn local_irq_restore(state: usize) {
        xen::set_upcalls_state(state as u8);
    }

    /// Work with the state of the application
    pub mod app {
        use hal::xen;

        /// Block the application until an interruption arrives
        #[inline]
        pub fn block() {
            xen::sched::block();
        }

        /// Crash the application
        ///
        /// On certain platform this might be equivalent to
        /// [`exit()`][exit]
        ///
        /// [exit]: fn.exit.html
        #[inline]
        pub fn crash() {
            xen::sched::crash();
        }

        /// Exit the application
        ///
        /// This will also power off the system.
        ///
        /// Note that the error code might be ignored on certain platforms
        #[inline]
        pub fn exit(code: isize) {
            xen::sched::poweroff(code as xen::defs::Ulong);
        }
    }
}

#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
#[path="x86"]
pub mod arch {
    pub mod mmu;
    pub mod defs;
    pub mod utils;
}
<commit_msg>hal: Add module net<commit_after>//! Hardware Abstraction Layer
//!
//! The goal of this module is to abstract the machine/architecture so that
//! the API is the same no matter the underlying hardware.
//!
//! For now only Xen (x86/x86_64) is supported. A goal for the future would
//! be to be compatible with the virtio standard as well and support Xen arm

use io::{Read, Write, Result};

pub mod mmu;

pub use self::hw_imp::*;

/// Generic console wrapper
pub struct Console<'a>(HwConsoleType<'a>);

impl<'a> Read for Console<'a> {
    #[inline]
    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
        self.0.read(buf)
    }
}

impl<'a> Write for Console<'a> {
    #[inline]
    fn write(&mut self, buf: &[u8]) -> Result<usize> {
        self.0.write(buf)
    }

    #[inline]
    fn flush(&mut self) -> Result<()> {
        self.0.flush()
    }
}

#[cfg(feature = "xen")]
pub mod xen;

#[cfg(feature = "xen")]
mod hw_imp {
    use super::xen;
    use super::Console;

    use cell::GlobalCellMutRef;

    /// Abstracts the actual hardware console type
    pub type HwConsoleType<'a> = GlobalCellMutRef<'a, xen::console::Console>;

    #[inline]
    /// Unprotected access to the hardware console
    ///
    /// This *SHOULD NOT* be used as is.
    ///
    /// Instead use [`io::stdout()`][stdout] or [`io::stdin()`][stdin]
    ///
    /// [stdout]: ../io/fn.stdout.html
    /// [stdin]: ../io/fn.stdin.html
    pub fn console<'a>() -> Console<'a> {
        Console(xen::console::console())
    }

    #[inline]
    /// Disable local interrupt delivery
    pub fn local_irq_disable() {
        xen::disable_upcalls();
    }

    #[inline]
    /// Enable local interrupt delivery
    pub fn local_irq_enable() {
        xen::enable_upcalls();
    }

    #[inline]
    /// Disable local interrupt delivery and return the previous state of
    /// interrupt delivery
    pub fn local_irq_save() -> usize {
        xen::disable_upcalls() as usize
    }

    #[inline]
    /// Restore a local interrupt delivery state
    pub fn local_irq_restore(state: usize) {
        xen::set_upcalls_state(state as u8);
    }

    /// Work with the state of the application
    pub mod app {
        use hal::xen;

        /// Block the application until an interruption arrives
        #[inline]
        pub fn block() {
            xen::sched::block();
        }

        /// Crash the application
        ///
        /// On certain platform this might be equivalent to
        /// [`exit()`][exit]
        ///
        /// [exit]: fn.exit.html
        #[inline]
        pub fn crash() {
            xen::sched::crash();
        }

        /// Exit the application
        ///
        /// This will also power off the system.
        ///
        /// Note that the error code might be ignored on certain platforms
        #[inline]
        pub fn exit(code: isize) {
            xen::sched::poweroff(code as xen::defs::Ulong);
        }
    }

    #[cfg(feature = "net")]
    /// Network device driver abstraction
    pub mod net {
    }
}

#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
#[path="x86"]
pub mod arch {
    pub mod mmu;
    pub mod defs;
    pub mod utils;
}
<|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.

//! Implementations of serialization for structures found in libcollections

use std::uint;
use std::default::Default;
use std::hash::{Hash, Hasher};

use {Decodable, Encodable, Decoder, Encoder};
use std::collections::{BTreeMap, BTreeSet, DList, RingBuf, HashMap, HashSet, VecMap};
use std::collections::enum_set::{EnumSet, CLike};

impl<
    E,
    S: Encoder<E>,
    T: Encodable<S, E>
> Encodable<S, E> for DList<T> {
    fn encode(&self, s: &mut S) -> Result<(), E> {
        s.emit_seq(self.len(), |s| {
            for (i, e) in self.iter().enumerate() {
                try!(s.emit_seq_elt(i, |s| e.encode(s)));
            }
            Ok(())
        })
    }
}

impl<E, D:Decoder<E>,T:Decodable<D, E>> Decodable<D, E> for DList<T> {
    fn decode(d: &mut D) -> Result<DList<T>, E> {
        d.read_seq(|d, len| {
            let mut list = DList::new();
            for i in range(0u, len) {
                list.push_back(try!(d.read_seq_elt(i, |d| Decodable::decode(d))));
            }
            Ok(list)
        })
    }
}

impl<
    E,
    S: Encoder<E>,
    T: Encodable<S, E>
> Encodable<S, E> for RingBuf<T> {
    fn encode(&self, s: &mut S) -> Result<(), E> {
        s.emit_seq(self.len(), |s| {
            for (i, e) in self.iter().enumerate() {
                try!(s.emit_seq_elt(i, |s| e.encode(s)));
            }
            Ok(())
        })
    }
}

impl<E, D:Decoder<E>,T:Decodable<D, E>> Decodable<D, E> for RingBuf<T> {
    fn decode(d: &mut D) -> Result<RingBuf<T>, E> {
        d.read_seq(|d, len| {
            let mut deque: RingBuf<T> = RingBuf::new();
            for i in range(0u, len) {
                deque.push_back(try!(d.read_seq_elt(i, |d| Decodable::decode(d))));
            }
            Ok(deque)
        })
    }
}

impl<
    E,
    S: Encoder<E>,
    K: Encodable<S, E> + PartialEq + Ord,
    V: Encodable<S, E> + PartialEq
> Encodable<S, E> for TreeMap<K, V> {
    fn encode(&self, e: &mut S) -> Result<(), E> {
        e.emit_map(self.len(), |e| {
            let mut i = 0;
            for (key, val) in self.iter() {
                try!(e.emit_map_elt_key(i, |e| key.encode(e)));
                try!(e.emit_map_elt_val(i, |e| val.encode(e)));
                i += 1;
            }
            Ok(())
        })
    }
}

impl<
    E,
    D: Decoder<E>,
    K: Decodable<D, E> + PartialEq + Ord,
    V: Decodable<D, E> + PartialEq
> Decodable<D, E> for TreeMap<K, V> {
    fn decode(d: &mut D) -> Result<TreeMap<K, V>, E> {
        d.read_map(|d, len| {
            let mut map = TreeMap::new();
            for i in range(0u, len) {
                let key = try!(d.read_map_elt_key(i, |d| Decodable::decode(d)));
                let val = try!(d.read_map_elt_val(i, |d| Decodable::decode(d)));
                map.insert(key, val);
            }
            Ok(map)
        })
    }
}

impl<
    E,
    S: Encoder<E>,
    T: Encodable<S, E> + PartialEq + Ord
> Encodable<S, E> for TreeSet<T> {
    fn encode(&self, s: &mut S) -> Result<(), E> {
        s.emit_seq(self.len(), |s| {
            let mut i = 0;
            for e in self.iter() {
                try!(s.emit_seq_elt(i, |s| e.encode(s)));
                i += 1;
            }
            Ok(())
        })
    }
}

impl<
    E,
    D: Decoder<E>,
    T: Decodable<D, E> + PartialEq + Ord
> Decodable<D, E> for TreeSet<T> {
    fn decode(d: &mut D) -> Result<TreeSet<T>, E> {
        d.read_seq(|d, len| {
            let mut set = TreeSet::new();
            for i in range(0u, len) {
                set.insert(try!(d.read_seq_elt(i, |d| Decodable::decode(d))));
            }
            Ok(set)
        })
    }
}

impl<
    E,
    S: Encoder<E>,
    T: Encodable<S, E> + CLike
> Encodable<S, E> for EnumSet<T> {
    fn encode(&self, s: &mut S) -> Result<(), E> {
        let mut bits = 0;
        for item in self.iter() {
            bits |= item.to_uint();
        }
        s.emit_uint(bits)
    }
}

impl<
    E,
    D: Decoder<E>,
    T: Decodable<D, E> + CLike
> Decodable<D, E> for EnumSet<T> {
    fn decode(d: &mut D) -> Result<EnumSet<T>, E> {
        let bits = try!(d.read_uint());
        let mut set = EnumSet::new();
        for bit in range(0, uint::BITS) {
            if bits & (1 << bit) != 0 {
                set.insert(CLike::from_uint(1 << bit));
            }
        }
        Ok(set)
    }
}

impl<
    E,
    S: Encoder<E>,
    K: Encodable<S, E> + Hash<X> + Eq,
    V: Encodable<S, E>,
    X,
    H: Hasher<X>
> Encodable<S, E> for HashMap<K, V, H> {
    fn encode(&self, e: &mut S) -> Result<(), E> {
        e.emit_map(self.len(), |e| {
            let mut i = 0;
            for (key, val) in self.iter() {
                try!(e.emit_map_elt_key(i, |e| key.encode(e)));
                try!(e.emit_map_elt_val(i, |e| val.encode(e)));
                i += 1;
            }
            Ok(())
        })
    }
}

impl<
    E,
    D: Decoder<E>,
    K: Decodable<D, E> + Hash<S> + Eq,
    V: Decodable<D, E>,
    S,
    H: Hasher<S> + Default
> Decodable<D, E> for HashMap<K, V, H> {
    fn decode(d: &mut D) -> Result<HashMap<K, V, H>, E> {
        d.read_map(|d, len| {
            let hasher = Default::default();
            let mut map = HashMap::with_capacity_and_hasher(len, hasher);
            for i in range(0u, len) {
                let key = try!(d.read_map_elt_key(i, |d| Decodable::decode(d)));
                let val = try!(d.read_map_elt_val(i, |d| Decodable::decode(d)));
                map.insert(key, val);
            }
            Ok(map)
        })
    }
}

impl<
    E,
    S: Encoder<E>,
    T: Encodable<S, E> + Hash<X> + Eq,
    X,
    H: Hasher<X>
> Encodable<S, E> for HashSet<T, H> {
    fn encode(&self, s: &mut S) -> Result<(), E> {
        s.emit_seq(self.len(), |s| {
            let mut i = 0;
            for e in self.iter() {
                try!(s.emit_seq_elt(i, |s| e.encode(s)));
                i += 1;
            }
            Ok(())
        })
    }
}

impl<
    E,
    D: Decoder<E>,
    T: Decodable<D, E> + Hash<S> + Eq,
    S,
    H: Hasher<S> + Default
> Decodable<D, E> for HashSet<T, H> {
    fn decode(d: &mut D) -> Result<HashSet<T, H>, E> {
        d.read_seq(|d, len| {
            let mut set = HashSet::with_capacity_and_hasher(len, Default::default());
            for i in range(0u, len) {
                set.insert(try!(d.read_seq_elt(i, |d| Decodable::decode(d))));
            }
            Ok(set)
        })
    }
}

impl<
    E,
    S: Encoder<E>,
    V: Encodable<S, E>
> Encodable<S, E> for TrieMap<V> {
    fn encode(&self, e: &mut S) -> Result<(), E> {
        e.emit_map(self.len(), |e| {
                for (i, (key, val)) in self.iter().enumerate() {
                    try!(e.emit_map_elt_key(i, |e| key.encode(e)));
                    try!(e.emit_map_elt_val(i, |e| val.encode(e)));
                }
                Ok(())
            })
    }
}

impl<
    E,
    D: Decoder<E>,
    V: Decodable<D, E>
> Decodable<D, E> for TrieMap<V> {
    fn decode(d: &mut D) -> Result<TrieMap<V>, E> {
        d.read_map(|d, len| {
            let mut map = TrieMap::new();
            for i in range(0u, len) {
                let key = try!(d.read_map_elt_key(i, |d| Decodable::decode(d)));
                let val = try!(d.read_map_elt_val(i, |d| Decodable::decode(d)));
                map.insert(key, val);
            }
            Ok(map)
        })
    }
}

impl<E, S: Encoder<E>> Encodable<S, E> for TrieSet {
    fn encode(&self, s: &mut S) -> Result<(), E> {
        s.emit_seq(self.len(), |s| {
                for (i, e) in self.iter().enumerate() {
                    try!(s.emit_seq_elt(i, |s| e.encode(s)));
                }
                Ok(())
            })
    }
}

impl<E, D: Decoder<E>> Decodable<D, E> for TrieSet {
    fn decode(d: &mut D) -> Result<TrieSet, E> {
        d.read_seq(|d, len| {
            let mut set = TrieSet::new();
            for i in range(0u, len) {
                set.insert(try!(d.read_seq_elt(i, |d| Decodable::decode(d))));
            }
            Ok(set)
        })
    }
}

impl<
    E,
    S: Encoder<E>,
    V: Encodable<S, E>
> Encodable<S, E> for VecMap<V> {
    fn encode(&self, e: &mut S) -> Result<(), E> {
        e.emit_map(self.len(), |e| {
                for (i, (key, val)) in self.iter().enumerate() {
                    try!(e.emit_map_elt_key(i, |e| key.encode(e)));
                    try!(e.emit_map_elt_val(i, |e| val.encode(e)));
                }
                Ok(())
            })
    }
}

impl<
    E,
    D: Decoder<E>,
    V: Decodable<D, E>
> Decodable<D, E> for VecMap<V> {
    fn decode(d: &mut D) -> Result<VecMap<V>, E> {
        d.read_map(|d, len| {
            let mut map = VecMap::new();
            for i in range(0u, len) {
                let key = try!(d.read_map_elt_key(i, |d| Decodable::decode(d)));
                let val = try!(d.read_map_elt_val(i, |d| Decodable::decode(d)));
                map.insert(key, val);
            }
            Ok(map)
        })
    }
}
<commit_msg>Use the `BTree*` variants ...<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.

//! Implementations of serialization for structures found in libcollections

use std::uint;
use std::default::Default;
use std::hash::{Hash, Hasher};

use {Decodable, Encodable, Decoder, Encoder};
use std::collections::{BTreeMap, BTreeSet, DList, RingBuf, HashMap, HashSet, VecMap};
use std::collections::enum_set::{EnumSet, CLike};

impl<
    E,
    S: Encoder<E>,
    T: Encodable<S, E>
> Encodable<S, E> for DList<T> {
    fn encode(&self, s: &mut S) -> Result<(), E> {
        s.emit_seq(self.len(), |s| {
            for (i, e) in self.iter().enumerate() {
                try!(s.emit_seq_elt(i, |s| e.encode(s)));
            }
            Ok(())
        })
    }
}

impl<E, D:Decoder<E>,T:Decodable<D, E>> Decodable<D, E> for DList<T> {
    fn decode(d: &mut D) -> Result<DList<T>, E> {
        d.read_seq(|d, len| {
            let mut list = DList::new();
            for i in range(0u, len) {
                list.push_back(try!(d.read_seq_elt(i, |d| Decodable::decode(d))));
            }
            Ok(list)
        })
    }
}

impl<
    E,
    S: Encoder<E>,
    T: Encodable<S, E>
> Encodable<S, E> for RingBuf<T> {
    fn encode(&self, s: &mut S) -> Result<(), E> {
        s.emit_seq(self.len(), |s| {
            for (i, e) in self.iter().enumerate() {
                try!(s.emit_seq_elt(i, |s| e.encode(s)));
            }
            Ok(())
        })
    }
}

impl<E, D:Decoder<E>,T:Decodable<D, E>> Decodable<D, E> for RingBuf<T> {
    fn decode(d: &mut D) -> Result<RingBuf<T>, E> {
        d.read_seq(|d, len| {
            let mut deque: RingBuf<T> = RingBuf::new();
            for i in range(0u, len) {
                deque.push_back(try!(d.read_seq_elt(i, |d| Decodable::decode(d))));
            }
            Ok(deque)
        })
    }
}

impl<
    E,
    S: Encoder<E>,
    K: Encodable<S, E> + PartialEq + Ord,
    V: Encodable<S, E> + PartialEq
> Encodable<S, E> for BTreeMap<K, V> {
    fn encode(&self, e: &mut S) -> Result<(), E> {
        e.emit_map(self.len(), |e| {
            let mut i = 0;
            for (key, val) in self.iter() {
                try!(e.emit_map_elt_key(i, |e| key.encode(e)));
                try!(e.emit_map_elt_val(i, |e| val.encode(e)));
                i += 1;
            }
            Ok(())
        })
    }
}

impl<
    E,
    D: Decoder<E>,
    K: Decodable<D, E> + PartialEq + Ord,
    V: Decodable<D, E> + PartialEq
> Decodable<D, E> for BTreeMap<K, V> {
    fn decode(d: &mut D) -> Result<BTreeMap<K, V>, E> {
        d.read_map(|d, len| {
            let mut map = BTreeMap::new();
            for i in range(0u, len) {
                let key = try!(d.read_map_elt_key(i, |d| Decodable::decode(d)));
                let val = try!(d.read_map_elt_val(i, |d| Decodable::decode(d)));
                map.insert(key, val);
            }
            Ok(map)
        })
    }
}

impl<
    E,
    S: Encoder<E>,
    T: Encodable<S, E> + PartialEq + Ord
> Encodable<S, E> for BTreeSet<T> {
    fn encode(&self, s: &mut S) -> Result<(), E> {
        s.emit_seq(self.len(), |s| {
            let mut i = 0;
            for e in self.iter() {
                try!(s.emit_seq_elt(i, |s| e.encode(s)));
                i += 1;
            }
            Ok(())
        })
    }
}

impl<
    E,
    D: Decoder<E>,
    T: Decodable<D, E> + PartialEq + Ord
> Decodable<D, E> for BTreeSet<T> {
    fn decode(d: &mut D) -> Result<BTreeSet<T>, E> {
        d.read_seq(|d, len| {
            let mut set = BTreeSet::new();
            for i in range(0u, len) {
                set.insert(try!(d.read_seq_elt(i, |d| Decodable::decode(d))));
            }
            Ok(set)
        })
    }
}

impl<
    E,
    S: Encoder<E>,
    T: Encodable<S, E> + CLike
> Encodable<S, E> for EnumSet<T> {
    fn encode(&self, s: &mut S) -> Result<(), E> {
        let mut bits = 0;
        for item in self.iter() {
            bits |= item.to_uint();
        }
        s.emit_uint(bits)
    }
}

impl<
    E,
    D: Decoder<E>,
    T: Decodable<D, E> + CLike
> Decodable<D, E> for EnumSet<T> {
    fn decode(d: &mut D) -> Result<EnumSet<T>, E> {
        let bits = try!(d.read_uint());
        let mut set = EnumSet::new();
        for bit in range(0, uint::BITS) {
            if bits & (1 << bit) != 0 {
                set.insert(CLike::from_uint(1 << bit));
            }
        }
        Ok(set)
    }
}

impl<
    E,
    S: Encoder<E>,
    K: Encodable<S, E> + Hash<X> + Eq,
    V: Encodable<S, E>,
    X,
    H: Hasher<X>
> Encodable<S, E> for HashMap<K, V, H> {
    fn encode(&self, e: &mut S) -> Result<(), E> {
        e.emit_map(self.len(), |e| {
            let mut i = 0;
            for (key, val) in self.iter() {
                try!(e.emit_map_elt_key(i, |e| key.encode(e)));
                try!(e.emit_map_elt_val(i, |e| val.encode(e)));
                i += 1;
            }
            Ok(())
        })
    }
}

impl<
    E,
    D: Decoder<E>,
    K: Decodable<D, E> + Hash<S> + Eq,
    V: Decodable<D, E>,
    S,
    H: Hasher<S> + Default
> Decodable<D, E> for HashMap<K, V, H> {
    fn decode(d: &mut D) -> Result<HashMap<K, V, H>, E> {
        d.read_map(|d, len| {
            let hasher = Default::default();
            let mut map = HashMap::with_capacity_and_hasher(len, hasher);
            for i in range(0u, len) {
                let key = try!(d.read_map_elt_key(i, |d| Decodable::decode(d)));
                let val = try!(d.read_map_elt_val(i, |d| Decodable::decode(d)));
                map.insert(key, val);
            }
            Ok(map)
        })
    }
}

impl<
    E,
    S: Encoder<E>,
    T: Encodable<S, E> + Hash<X> + Eq,
    X,
    H: Hasher<X>
> Encodable<S, E> for HashSet<T, H> {
    fn encode(&self, s: &mut S) -> Result<(), E> {
        s.emit_seq(self.len(), |s| {
            let mut i = 0;
            for e in self.iter() {
                try!(s.emit_seq_elt(i, |s| e.encode(s)));
                i += 1;
            }
            Ok(())
        })
    }
}

impl<
    E,
    D: Decoder<E>,
    T: Decodable<D, E> + Hash<S> + Eq,
    S,
    H: Hasher<S> + Default
> Decodable<D, E> for HashSet<T, H> {
    fn decode(d: &mut D) -> Result<HashSet<T, H>, E> {
        d.read_seq(|d, len| {
            let mut set = HashSet::with_capacity_and_hasher(len, Default::default());
            for i in range(0u, len) {
                set.insert(try!(d.read_seq_elt(i, |d| Decodable::decode(d))));
            }
            Ok(set)
        })
    }
}

impl<
    E,
    S: Encoder<E>,
    V: Encodable<S, E>
> Encodable<S, E> for TrieMap<V> {
    fn encode(&self, e: &mut S) -> Result<(), E> {
        e.emit_map(self.len(), |e| {
                for (i, (key, val)) in self.iter().enumerate() {
                    try!(e.emit_map_elt_key(i, |e| key.encode(e)));
                    try!(e.emit_map_elt_val(i, |e| val.encode(e)));
                }
                Ok(())
            })
    }
}

impl<
    E,
    D: Decoder<E>,
    V: Decodable<D, E>
> Decodable<D, E> for TrieMap<V> {
    fn decode(d: &mut D) -> Result<TrieMap<V>, E> {
        d.read_map(|d, len| {
            let mut map = TrieMap::new();
            for i in range(0u, len) {
                let key = try!(d.read_map_elt_key(i, |d| Decodable::decode(d)));
                let val = try!(d.read_map_elt_val(i, |d| Decodable::decode(d)));
                map.insert(key, val);
            }
            Ok(map)
        })
    }
}

impl<E, S: Encoder<E>> Encodable<S, E> for TrieSet {
    fn encode(&self, s: &mut S) -> Result<(), E> {
        s.emit_seq(self.len(), |s| {
                for (i, e) in self.iter().enumerate() {
                    try!(s.emit_seq_elt(i, |s| e.encode(s)));
                }
                Ok(())
            })
    }
}

impl<E, D: Decoder<E>> Decodable<D, E> for TrieSet {
    fn decode(d: &mut D) -> Result<TrieSet, E> {
        d.read_seq(|d, len| {
            let mut set = TrieSet::new();
            for i in range(0u, len) {
                set.insert(try!(d.read_seq_elt(i, |d| Decodable::decode(d))));
            }
            Ok(set)
        })
    }
}

impl<
    E,
    S: Encoder<E>,
    V: Encodable<S, E>
> Encodable<S, E> for VecMap<V> {
    fn encode(&self, e: &mut S) -> Result<(), E> {
        e.emit_map(self.len(), |e| {
                for (i, (key, val)) in self.iter().enumerate() {
                    try!(e.emit_map_elt_key(i, |e| key.encode(e)));
                    try!(e.emit_map_elt_val(i, |e| val.encode(e)));
                }
                Ok(())
            })
    }
}

impl<
    E,
    D: Decoder<E>,
    V: Decodable<D, E>
> Decodable<D, E> for VecMap<V> {
    fn decode(d: &mut D) -> Result<VecMap<V>, E> {
        d.read_map(|d, len| {
            let mut map = VecMap::new();
            for i in range(0u, len) {
                let key = try!(d.read_map_elt_key(i, |d| Decodable::decode(d)));
                let val = try!(d.read_map_elt_val(i, |d| Decodable::decode(d)));
                map.insert(key, val);
            }
            Ok(map)
        })
    }
}
<|endoftext|>


<commit_before>//! The main entry point for the Space API server.
//!
//! Running this code starts a HTTP server instance. The default port is 3000, but you can set your
//! own favorite port by exporting the `PORT` environment variable.

extern crate rustc_serialize;
extern crate iron;
extern crate spaceapi;

pub mod datastore;
pub mod redis_store;

use std::net::Ipv4Addr;
use std::sync::{Mutex, Arc};

use rustc_serialize::json::{Json, ToJson};
use iron::{Request, Response, IronResult, Iron, Set};
use iron::{status, headers, middleware};
use iron::modifiers::Header;

pub use datastore::DataStore;
use spaceapi::utils::Optional;
use spaceapi::SensorTemplate::{PeopleNowPresentSensorTemplate, TemperatureSensorTemplate};

/// A specification of a sensor.
///
/// The ``sensor`` field contains the static data of a sensor and the
/// ``data_key`` says how to find the sensor value in the datastore.
#[derive(Debug)]
pub struct SensorSpec {
    sensor: spaceapi::SensorTemplate,
    data_key: String,
    data_type: SensorValueType,
}

/// All possible value types a sensor can have.
#[derive(Debug)]
pub enum SensorValueType {
    Int,
    Float,
    Bool,
}

/// The actual sensor values.
#[derive(Debug)]
pub enum SensorValue {
    Int(i64),
    Float(f64),
    Bool(bool),
}

/// A datastore wrapped in an Arc, a Mutex and a Box. Safe for use in multithreaded situations.
pub type SafeDataStore = Arc<Mutex<Box<DataStore>>>;

/// A Space API server instance.
///
/// You can create a new instance using the ``new`` constructor method by
/// passing it the host, the port, the ``Status`` object and a datastore.
///
/// The ``SpaceapiServer`` includes a web server through
/// [Hyper](http://hyper.rs/hyper/hyper/server/index.html). Simply call the ``serve`` method.
pub struct SpaceapiServer {
    host: Ipv4Addr,
    port: u16,
    status: spaceapi::Status,
    datastore: SafeDataStore,
    sensors: Vec<SensorSpec>
}

impl SpaceapiServer {

    pub fn new(host: Ipv4Addr, port: u16, status: spaceapi::Status, datastore: SafeDataStore) -> SpaceapiServer {
        SpaceapiServer {
            host: host,
            port: port,
            status: status,
            datastore: datastore,
            sensors: vec![],
        }
    }

    /// Start a HTTP server listening on ``self.host:self.port``.
    ///
    /// This call is blocking. It can be interrupted with SIGINT (Ctrl+C).
    pub fn serve(self) {
        let host = self.host;
        let port = self.port;
        println!("Starting HTTP server on http://{}:{}...", host, port);
        Iron::new(self).http((host, port)).unwrap();
    }

    /// Register a new sensor.
    ///
    /// The first argument is a ``spaceapi::Sensor`` instance containing all static data. The
    /// second argument specifies how to get the actual sensor value from the datastore. And the
    /// third argument specifies the data type of the value.
    pub fn register_sensor(&mut self, sensor: spaceapi::SensorTemplate, data_key: String, data_type: SensorValueType) {
        self.sensors.push(
            SensorSpec { sensor: sensor, data_key: data_key, data_type: data_type }
        );
    }

    fn build_response_json(&self) -> Json {

        // Create a mutable copy of the status struct
        let mut status_copy = self.status.clone();

        // Prepare sensor vector
        let mut temperature_sensors: Vec<spaceapi::TemperatureSensor> = Vec::new();
        let mut people_now_present_sensors: Vec<spaceapi::PeopleNowPresentSensor> = Vec::new();

        // Process registered sensors
        if !self.sensors.is_empty() {

            // Add sensor data
            for sensor_spec in &self.sensors {

                let value = sensor_spec.get_sensor_value(&self.datastore);

                // If value is available, save sensor data
                if value.is_some() {
                    match sensor_spec.sensor {

                        // Create temperature sensor instance
                        TemperatureSensorTemplate { ref unit, ref location, ref name, ref description } => {
                            let sensor = spaceapi::TemperatureSensor {
                                unit: (*unit).clone(),
                                location: (*location).clone(),
                                name: (*name).clone(),
                                description: (*description).clone(),
                                value: match value.unwrap() {
                                    SensorValue::Float(v) => v,
                                    _ => unreachable!(),
                                },
                            };
                            temperature_sensors.push(sensor);
                        },

                        // Create people now present sensor instance
                        PeopleNowPresentSensorTemplate { ref location, ref name, ref names, ref description } => {
                            let sensor = spaceapi::PeopleNowPresentSensor {
                                location: (*location).clone(),
                                name: (*name).clone(),
                                names: (*names).clone(),
                                description: (*description).clone(),
                                value: match value.unwrap() {
                                    SensorValue::Int(v) => v,
                                    _ => unreachable!(),
                                },
                            };
                            people_now_present_sensors.push(sensor);
                        },

                    }
                }

            }

        }

        // Add sensors to status object
        status_copy.sensors = Optional::Value(spaceapi::Sensors {
            people_now_present: match people_now_present_sensors.is_empty() {
                true => Optional::Absent,
                false => Optional::Value(people_now_present_sensors),
            },
            temperature: match temperature_sensors.is_empty() {
                true => Optional::Absent,
                false => Optional::Value(temperature_sensors),
            },
        });

        // Serialize to JSON
        status_copy.to_json()
    }

}

trait SensorDataFetcher {
    fn get_sensor_value(&self, datastore: &SafeDataStore) -> Option<SensorValue>;
}

impl SensorDataFetcher for SensorSpec {

    /// Retrieve sensor value from the datastore.
    fn get_sensor_value(&self, datastore: &SafeDataStore) -> Option<SensorValue> {
        let datastore_clone = datastore.clone();
        let datastore_lock = datastore_clone.lock().unwrap();
        match datastore_lock.retrieve(&self.data_key) {
            Ok(v) => {
                match self.data_type {
                    SensorValueType::Float => match v.parse::<f64>() {
                        Ok(i) => Some(SensorValue::Float(i)),
                        Err(_) => None,
                    },
                    SensorValueType::Int => match v.parse::<i64>() {
                        Ok(i) => Some(SensorValue::Int(i)),
                        Err(_) => None,
                    },
                    SensorValueType::Bool => None,  // TODO
                }
            },
            Err(_) => None,
        }
    }

}

impl middleware::Handler for SpaceapiServer {

    fn handle(&self, _: &mut Request) -> IronResult<Response> {
        // Get response body
        let body = self.build_response_json().to_string();

        // Create response
        let mut response = Response::with((status::Ok, body));

        // Set headers
        response.set_mut(Header(headers::ContentType("application/json; charset=utf-8".parse().unwrap())));
        response.set_mut(Header(headers::CacheControl(vec![headers::CacheDirective::NoCache])));
        response.set_mut(Header(headers::AccessControlAllowOrigin::Any));

        Ok(response)
    }
}

#[cfg(test)]
mod test {
    extern crate spaceapi;
    extern crate rustc_serialize;

    use std::net::Ipv4Addr;
    use std::sync::{Mutex,Arc};
    use rustc_serialize::json::Json;
    use spaceapi::utils::Optional;
    use super::SpaceapiServer;
    use super::datastore::DataStore;
    use super::redis_store::RedisStore;

    fn get_test_data() -> Json {
        // Create minimal status object
        let status = spaceapi::Status::new(
            "ourspace".to_string(),
            "https://example.com/logo.png".to_string(),
            "https://example.com/".to_string(),
            spaceapi::Location {
                address: Optional::Value("Street 1, Zürich, Switzerland".to_string()),
                lat: 47.123,
                lon: 8.88,
            },
            spaceapi::Contact {
                irc: Optional::Absent,
                twitter: Optional::Absent,
                foursquare: Optional::Absent,
                email: Optional::Value("hi@example.com".to_string()),
            },
            vec![
                "email".to_string(),
                "twitter".to_string(),
            ],
        );

        // Create datastore (TODO: Create dummy store for testing?)
        let datastore = Arc::new(Mutex::new(Box::new(RedisStore::new().unwrap()) as Box<DataStore>));

        // Initialize server
        let server = SpaceapiServer::new(Ipv4Addr::new(127, 0, 0, 1), 3001, status, datastore);

        // Build JSON
        server.build_response_json()
    }

    #[test]
    /// Verify that the result is a JSON object.
    fn verify_json_obj() {
        let json = get_test_data();
        assert!(json.is_object());
    }

    #[test]
    /// Verify that the result has the correct keys.
    fn verify_json_keys() {
        let json = get_test_data();
        let status = json.as_object().unwrap();  // We get back a BTreeMap<String, Json>
        let keys: Vec<String> = status.keys().cloned().collect();  // Collect the keys
        assert_eq!(keys, ["api", "contact", "issue_report_channels", "location",
                          "logo", "sensors", "space", "state", "url"]);
    }

    #[test]
    /// Verify static data
    fn verify_json_static_data() {
        let json = get_test_data();
        let status = json.as_object().unwrap();  // We get back a BTreeMap<String, Json>

        // Strings
        assert_eq!(status.get("api").unwrap().as_string().unwrap(), "0.13");
        assert_eq!(status.get("space").unwrap().as_string().unwrap(), "ourspace");
        assert_eq!(status.get("url").unwrap().as_string().unwrap(), "https://example.com/");
        assert_eq!(status.get("logo").unwrap().as_string().unwrap(), "https://example.com/logo.png");

        // Channels
        let channels: &Vec<Json> = status.get("issue_report_channels").unwrap().as_array().unwrap();
        let channel_values: Vec<String> = channels.iter()
                                                  .cloned()
                                                  .map(|c| c.as_string().unwrap().to_string())
                                                  .collect();
        assert_eq!(vec!["email", "twitter"], channel_values);
    }

    #[test]
    /// Verify location
    fn verify_json_location() {
        let json = get_test_data();
        let status = json.as_object().unwrap();
        let location = status.get("location").unwrap().as_object().unwrap();

        // Verify data
        let address = location.get("address").unwrap().as_string().unwrap();
        assert_eq!("Street 1, Zürich, Switzerland".to_string(), address);
        assert_eq!(47.123, location.get("lat").unwrap().as_f64().unwrap());
        assert_eq!(8.88, location.get("lon").unwrap().as_f64().unwrap());
    }

    #[test]
    /// Verify contact
    fn verify_json_contact() {
        let json = get_test_data();
        let status = json.as_object().unwrap();
        let contact = status.get("contact").unwrap().as_object().unwrap();

        // Verify data
        let email = contact.get("email").unwrap().as_string().unwrap();
        assert_eq!("hi@example.com".to_string(), email);
    }

    /* TODO: Update
    #[test]
    /// Verify sensor data
    fn verify_json_sensors() {
        let json = get_test_data();
        let status = json.as_object().unwrap();
        let sensors = status.get("sensors").unwrap().as_object().unwrap();
        let temperature_sensor = sensors.get("temperature").unwrap()
                                        .as_array().unwrap()[0]  // List of sensors
                                        .as_object().unwrap();  // Sensor object
        let people_sensor = sensors.get("people_now_present").unwrap()  // List of sensors
                                        .as_array().unwrap()[0]  // Sensor object
                                        .as_object().unwrap();

        // Verify temperature sensor
        let temp_location = temperature_sensor.get("location").unwrap().as_string().unwrap();
        let temp_name = temperature_sensor.get("name").unwrap().as_string().unwrap();
        let temp_unit = temperature_sensor.get("unit").unwrap().as_string().unwrap();
        let temp_value = temperature_sensor.get("value").unwrap().as_f64().unwrap();
        assert_eq!("Basement".to_string(), temp_location);
        assert_eq!("Raspberry CPU".to_string(), temp_name);
        assert_eq!("°C".to_string(), temp_unit);
        assert_eq!(42.5, temp_value);

        // Verify peoplesensor
        let people_location = people_sensor.get("location").unwrap().as_string().unwrap();
        let people_value = people_sensor.get("value").unwrap().as_u64().unwrap();
        assert_eq!("Hackerspace".to_string(), people_location);
        assert_eq!(23, people_value);
    }
    */

}
<commit_msg>Renamed SpaceapiServer.sensors to SpaceapiServer.sensor_specs<commit_after>//! The main entry point for the Space API server.
//!
//! Running this code starts a HTTP server instance. The default port is 3000, but you can set your
//! own favorite port by exporting the `PORT` environment variable.

extern crate rustc_serialize;
extern crate iron;
extern crate spaceapi;

pub mod datastore;
pub mod redis_store;

use std::net::Ipv4Addr;
use std::sync::{Mutex, Arc};

use rustc_serialize::json::{Json, ToJson};
use iron::{Request, Response, IronResult, Iron, Set};
use iron::{status, headers, middleware};
use iron::modifiers::Header;

pub use datastore::DataStore;
use spaceapi::utils::Optional;
use spaceapi::SensorTemplate::{PeopleNowPresentSensorTemplate, TemperatureSensorTemplate};

/// A specification of a sensor.
///
/// The ``sensor`` field contains the static data of a sensor and the
/// ``data_key`` says how to find the sensor value in the datastore.
#[derive(Debug)]
pub struct SensorSpec {
    sensor: spaceapi::SensorTemplate,
    data_key: String,
    data_type: SensorValueType,
}

/// All possible value types a sensor can have.
#[derive(Debug)]
pub enum SensorValueType {
    Int,
    Float,
    Bool,
}

/// The actual sensor values.
#[derive(Debug)]
pub enum SensorValue {
    Int(i64),
    Float(f64),
    Bool(bool),
}

/// A datastore wrapped in an Arc, a Mutex and a Box. Safe for use in multithreaded situations.
pub type SafeDataStore = Arc<Mutex<Box<DataStore>>>;

/// A Space API server instance.
///
/// You can create a new instance using the ``new`` constructor method by
/// passing it the host, the port, the ``Status`` object and a datastore.
///
/// The ``SpaceapiServer`` includes a web server through
/// [Hyper](http://hyper.rs/hyper/hyper/server/index.html). Simply call the ``serve`` method.
pub struct SpaceapiServer {
    host: Ipv4Addr,
    port: u16,
    status: spaceapi::Status,
    datastore: SafeDataStore,
    sensor_specs: Vec<SensorSpec>
}

impl SpaceapiServer {

    pub fn new(host: Ipv4Addr, port: u16, status: spaceapi::Status, datastore: SafeDataStore) -> SpaceapiServer {
        SpaceapiServer {
            host: host,
            port: port,
            status: status,
            datastore: datastore,
            sensor_specs: vec![],
        }
    }

    /// Start a HTTP server listening on ``self.host:self.port``.
    ///
    /// This call is blocking. It can be interrupted with SIGINT (Ctrl+C).
    pub fn serve(self) {
        let host = self.host;
        let port = self.port;
        println!("Starting HTTP server on http://{}:{}...", host, port);
        Iron::new(self).http((host, port)).unwrap();
    }

    /// Register a new sensor.
    ///
    /// The first argument is a ``spaceapi::Sensor`` instance containing all static data. The
    /// second argument specifies how to get the actual sensor value from the datastore. And the
    /// third argument specifies the data type of the value.
    pub fn register_sensor(&mut self, sensor: spaceapi::SensorTemplate, data_key: String, data_type: SensorValueType) {
        self.sensor_specs.push(
            SensorSpec { sensor: sensor, data_key: data_key, data_type: data_type }
        );
    }

    fn build_response_json(&self) -> Json {

        // Create a mutable copy of the status struct
        let mut status_copy = self.status.clone();

        // Prepare sensor vector
        let mut temperature_sensors: Vec<spaceapi::TemperatureSensor> = Vec::new();
        let mut people_now_present_sensors: Vec<spaceapi::PeopleNowPresentSensor> = Vec::new();

        // Process registered sensors
        if !self.sensor_specs.is_empty() {

            // Add sensor data
            for sensor_spec in &self.sensor_specs {

                let value = sensor_spec.get_sensor_value(&self.datastore);

                // If value is available, save sensor data
                if value.is_some() {
                    match sensor_spec.sensor {

                        // Create temperature sensor instance
                        TemperatureSensorTemplate { ref unit, ref location, ref name, ref description } => {
                            let sensor = spaceapi::TemperatureSensor {
                                unit: (*unit).clone(),
                                location: (*location).clone(),
                                name: (*name).clone(),
                                description: (*description).clone(),
                                value: match value.unwrap() {
                                    SensorValue::Float(v) => v,
                                    _ => unreachable!(),
                                },
                            };
                            temperature_sensors.push(sensor);
                        },

                        // Create people now present sensor instance
                        PeopleNowPresentSensorTemplate { ref location, ref name, ref names, ref description } => {
                            let sensor = spaceapi::PeopleNowPresentSensor {
                                location: (*location).clone(),
                                name: (*name).clone(),
                                names: (*names).clone(),
                                description: (*description).clone(),
                                value: match value.unwrap() {
                                    SensorValue::Int(v) => v,
                                    _ => unreachable!(),
                                },
                            };
                            people_now_present_sensors.push(sensor);
                        },

                    }
                }

            }

        }

        // Add sensors to status object
        status_copy.sensors = Optional::Value(spaceapi::Sensors {
            people_now_present: match people_now_present_sensors.is_empty() {
                true => Optional::Absent,
                false => Optional::Value(people_now_present_sensors),
            },
            temperature: match temperature_sensors.is_empty() {
                true => Optional::Absent,
                false => Optional::Value(temperature_sensors),
            },
        });

        // Serialize to JSON
        status_copy.to_json()
    }

}

trait SensorDataFetcher {
    fn get_sensor_value(&self, datastore: &SafeDataStore) -> Option<SensorValue>;
}

impl SensorDataFetcher for SensorSpec {

    /// Retrieve sensor value from the datastore.
    fn get_sensor_value(&self, datastore: &SafeDataStore) -> Option<SensorValue> {
        let datastore_clone = datastore.clone();
        let datastore_lock = datastore_clone.lock().unwrap();
        match datastore_lock.retrieve(&self.data_key) {
            Ok(v) => {
                match self.data_type {
                    SensorValueType::Float => match v.parse::<f64>() {
                        Ok(i) => Some(SensorValue::Float(i)),
                        Err(_) => None,
                    },
                    SensorValueType::Int => match v.parse::<i64>() {
                        Ok(i) => Some(SensorValue::Int(i)),
                        Err(_) => None,
                    },
                    SensorValueType::Bool => None,  // TODO
                }
            },
            Err(_) => None,
        }
    }

}

impl middleware::Handler for SpaceapiServer {

    fn handle(&self, _: &mut Request) -> IronResult<Response> {
        // Get response body
        let body = self.build_response_json().to_string();

        // Create response
        let mut response = Response::with((status::Ok, body));

        // Set headers
        response.set_mut(Header(headers::ContentType("application/json; charset=utf-8".parse().unwrap())));
        response.set_mut(Header(headers::CacheControl(vec![headers::CacheDirective::NoCache])));
        response.set_mut(Header(headers::AccessControlAllowOrigin::Any));

        Ok(response)
    }
}

#[cfg(test)]
mod test {
    extern crate spaceapi;
    extern crate rustc_serialize;

    use std::net::Ipv4Addr;
    use std::sync::{Mutex,Arc};
    use rustc_serialize::json::Json;
    use spaceapi::utils::Optional;
    use super::SpaceapiServer;
    use super::datastore::DataStore;
    use super::redis_store::RedisStore;

    fn get_test_data() -> Json {
        // Create minimal status object
        let status = spaceapi::Status::new(
            "ourspace".to_string(),
            "https://example.com/logo.png".to_string(),
            "https://example.com/".to_string(),
            spaceapi::Location {
                address: Optional::Value("Street 1, Zürich, Switzerland".to_string()),
                lat: 47.123,
                lon: 8.88,
            },
            spaceapi::Contact {
                irc: Optional::Absent,
                twitter: Optional::Absent,
                foursquare: Optional::Absent,
                email: Optional::Value("hi@example.com".to_string()),
            },
            vec![
                "email".to_string(),
                "twitter".to_string(),
            ],
        );

        // Create datastore (TODO: Create dummy store for testing?)
        let datastore = Arc::new(Mutex::new(Box::new(RedisStore::new().unwrap()) as Box<DataStore>));

        // Initialize server
        let server = SpaceapiServer::new(Ipv4Addr::new(127, 0, 0, 1), 3001, status, datastore);

        // Build JSON
        server.build_response_json()
    }

    #[test]
    /// Verify that the result is a JSON object.
    fn verify_json_obj() {
        let json = get_test_data();
        assert!(json.is_object());
    }

    #[test]
    /// Verify that the result has the correct keys.
    fn verify_json_keys() {
        let json = get_test_data();
        let status = json.as_object().unwrap();  // We get back a BTreeMap<String, Json>
        let keys: Vec<String> = status.keys().cloned().collect();  // Collect the keys
        assert_eq!(keys, ["api", "contact", "issue_report_channels", "location",
                          "logo", "sensors", "space", "state", "url"]);
    }

    #[test]
    /// Verify static data
    fn verify_json_static_data() {
        let json = get_test_data();
        let status = json.as_object().unwrap();  // We get back a BTreeMap<String, Json>

        // Strings
        assert_eq!(status.get("api").unwrap().as_string().unwrap(), "0.13");
        assert_eq!(status.get("space").unwrap().as_string().unwrap(), "ourspace");
        assert_eq!(status.get("url").unwrap().as_string().unwrap(), "https://example.com/");
        assert_eq!(status.get("logo").unwrap().as_string().unwrap(), "https://example.com/logo.png");

        // Channels
        let channels: &Vec<Json> = status.get("issue_report_channels").unwrap().as_array().unwrap();
        let channel_values: Vec<String> = channels.iter()
                                                  .cloned()
                                                  .map(|c| c.as_string().unwrap().to_string())
                                                  .collect();
        assert_eq!(vec!["email", "twitter"], channel_values);
    }

    #[test]
    /// Verify location
    fn verify_json_location() {
        let json = get_test_data();
        let status = json.as_object().unwrap();
        let location = status.get("location").unwrap().as_object().unwrap();

        // Verify data
        let address = location.get("address").unwrap().as_string().unwrap();
        assert_eq!("Street 1, Zürich, Switzerland".to_string(), address);
        assert_eq!(47.123, location.get("lat").unwrap().as_f64().unwrap());
        assert_eq!(8.88, location.get("lon").unwrap().as_f64().unwrap());
    }

    #[test]
    /// Verify contact
    fn verify_json_contact() {
        let json = get_test_data();
        let status = json.as_object().unwrap();
        let contact = status.get("contact").unwrap().as_object().unwrap();

        // Verify data
        let email = contact.get("email").unwrap().as_string().unwrap();
        assert_eq!("hi@example.com".to_string(), email);
    }

    /* TODO: Update
    #[test]
    /// Verify sensor data
    fn verify_json_sensors() {
        let json = get_test_data();
        let status = json.as_object().unwrap();
        let sensors = status.get("sensors").unwrap().as_object().unwrap();
        let temperature_sensor = sensors.get("temperature").unwrap()
                                        .as_array().unwrap()[0]  // List of sensors
                                        .as_object().unwrap();  // Sensor object
        let people_sensor = sensors.get("people_now_present").unwrap()  // List of sensors
                                        .as_array().unwrap()[0]  // Sensor object
                                        .as_object().unwrap();

        // Verify temperature sensor
        let temp_location = temperature_sensor.get("location").unwrap().as_string().unwrap();
        let temp_name = temperature_sensor.get("name").unwrap().as_string().unwrap();
        let temp_unit = temperature_sensor.get("unit").unwrap().as_string().unwrap();
        let temp_value = temperature_sensor.get("value").unwrap().as_f64().unwrap();
        assert_eq!("Basement".to_string(), temp_location);
        assert_eq!("Raspberry CPU".to_string(), temp_name);
        assert_eq!("°C".to_string(), temp_unit);
        assert_eq!(42.5, temp_value);

        // Verify peoplesensor
        let people_location = people_sensor.get("location").unwrap().as_string().unwrap();
        let people_value = people_sensor.get("value").unwrap().as_u64().unwrap();
        assert_eq!("Hackerspace".to_string(), people_location);
        assert_eq!(23, people_value);
    }
    */

}
<|endoftext|>