<commit_before>#![warn(missing_docs)]

//! input-map
//! =========
//!
//! A library for binding input keys to actions, and manipulating mouse input

extern crate input;
extern crate piston_window;
extern crate rustc_serialize;
extern crate viewport;

mod builder;

use input::{Input, Button, Motion};
use piston_window::Size;
use std::collections::HashMap;
use std::default::Default;
use std::hash::Hash;
use viewport::Viewport;

pub use builder::InputTranslatorBuilder;

/// Represents a logical action to be bound to a particular button press, e.g.
/// jump, attack, or move forward. Needs to be hashable, as it is used as a
/// lookup key when rebinding an action to a different button.
pub trait Action: Copy + PartialEq + Eq + Hash { }

/// A translated action.
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum Translated<A: Action> {
    /// A keypress event which was bound to an action
    Press(A),

    /// A key release event which was bound to an action
    Release(A),

    /// A translated mouse motion. The logical origin of a translated MouseCursor event
    /// is in the top left corner of the window, and the logical scroll is non-natural.
    /// Relative events are unchanged for now.
    Move(Motion)
}

/// A three-element tuple of Option<Button>. For simplicity, a maximum number of 3
/// buttons can be bound to each action, and this is exposed through the `InputRebind`
/// struct.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct ButtonTuple(pub Option<Button>, pub Option<Button>, pub Option<Button>);

impl ButtonTuple {
    /// Creates a new tuple with no buttons in it (equivalent to `Default::default()`).
    pub fn new() -> Self { Default::default() }

    /// Check if the button is in the tuple.
    pub fn contains(&self, button: Button) -> bool {
        let sbtn = Some(button);
        self.0 == sbtn || self.1 == sbtn || self.2 == sbtn
    }

    /// Insert a button into the tuple if there is room, searching from left to right.
    /// If the button is inserted, returns true. Otherwise, if the button is not inserted,
    /// this function returns false.
    pub fn insert_inplace(&mut self, button: Button) -> bool {
        match self {
            &mut ButtonTuple(a, b, c) if a.is_none() => {*self = ButtonTuple(Some(button), b, c); true},
            &mut ButtonTuple(a, b, c) if b.is_none() => {*self = ButtonTuple(a, Some(button), c); true},
            &mut ButtonTuple(a, b, c) if c.is_none() => {*self = ButtonTuple(a, b, Some(button)); true}
            _ => false
        }
    }
}

impl Default for ButtonTuple {
    /// Creates a new tuple with no buttons in it.
    fn default() -> Self { ButtonTuple(None, None, None) }
}

/// An object which translates piston::input::Input events into input_map::Translated<A> events
#[derive(Clone)]
pub struct InputTranslator<A: Action> {
    keymap: HashMap<Button, A>,
    mouse_translator: MouseTranslator
}

impl<A: Action> InputTranslator<A> {

    /// Creates an empty InputTranslator.
    pub fn new(size: Size) -> Self {
        InputTranslator {
            keymap: HashMap::new(),
            mouse_translator: MouseTranslator::new(size)
        }
    }

    /// Translate an Input into a Translated<A> event
    pub fn translate(&self, input: &Input) -> Option<Translated<A>> {
        macro_rules! translate_button(($but_state:ident, $but_var:ident) => (
            match self.keymap.get(&$but_var).map(|x| *x) {
                Some(act) => Some(Translated::$but_state(act)),
                None => None
            });
        );

        match input {
            &Input::Press(button) => translate_button!(Press, button),
            &Input::Release(button) => translate_button!(Release, button),
            &Input::Move(motion) =>
                Some(Translated::Move(self.mouse_translator.translate(motion))),
            _ => None
        }
    }

    /// Re-set the mouse bounds size used for calculating mouse events
    pub fn set_size(&mut self, size: Size) {
        self.mouse_translator.data.viewport_size = size
    }

    /// Re-set the mouse bounds size from a viewport
    pub fn set_size_from_viewport(&mut self, vp: Viewport) {
        self.set_size(Size::from(vp.draw_size));
    }
}

#[derive(Clone)]
struct MouseTranslationData {
    x_axis_motion_inverted: bool,
    y_axis_motion_inverted: bool,
    x_axis_scroll_inverted: bool,
    y_axis_scroll_inverted: bool,
    viewport_size: Size
}

impl MouseTranslationData {
    fn new(size: Size) -> Self {
        MouseTranslationData {
            x_axis_motion_inverted: false,
            y_axis_motion_inverted: false,
            x_axis_scroll_inverted: false,
            y_axis_scroll_inverted: false,
            viewport_size: size
        }
    }
}

#[derive(Clone)]
struct MouseTranslator {
    data: MouseTranslationData
}

impl MouseTranslator {
    fn new(size: Size) -> Self {
        MouseTranslator {
            data: MouseTranslationData::new(size)
        }
    }

    fn translate(&self, motion: Motion) -> Motion {
        match motion {
            Motion::MouseCursor(x, y) => {
                let (sw, sh) = {
                    let Size {width, height} = self.data.viewport_size;
                    (width as f64, height as f64)
                };

                let cx = if self.data.x_axis_motion_inverted { sw - x } else { x };
                let cy = if self.data.y_axis_motion_inverted { sh - y } else { y };

                Motion::MouseCursor(cx, cy)
            },
            Motion::MouseScroll(x, y) => {
                let mx = if self.data.x_axis_scroll_inverted { -1.0f64 } else { 1.0 };
                let my = if self.data.y_axis_scroll_inverted { -1.0f64 } else { 1.0 };
                Motion::MouseScroll(x * mx, y * my)
            },
            relative => relative
        }
    }
}

#[allow(missing_docs)]
pub struct InputRebind<A: Action> {
    keymap: HashMap<A, ButtonTuple>,
    mouse_data: MouseTranslationData
}

impl<A: Action> InputRebind<A> {
    #[allow(missing_docs)]
    pub fn new(size: Size) -> Self {
        InputRebind {
            keymap: HashMap::new(),
            mouse_data: MouseTranslationData::new(size)
        }
    }

    #[allow(missing_docs)]
    pub fn insert_action(&mut self, action: A) -> Option<ButtonTuple> {
        self.keymap.insert(action, ButtonTuple::new())
    }

    #[allow(missing_docs)]
    pub fn insert_action_with_buttons(&mut self, action: A, buttons: ButtonTuple) -> Option<ButtonTuple> {
        self.keymap.insert(action, buttons)
    }

    #[allow(missing_docs)]
    pub fn get_bindings(&self, action: &A) -> Option<&ButtonTuple> {
        self.keymap.get(action)
    }

    #[allow(missing_docs)]
    pub fn get_bindings_mut(&mut self, action: &mut A) -> Option<&mut ButtonTuple> {
        self.keymap.get_mut(action)
    }

    #[allow(missing_docs)]
    pub fn get_x_scroll_inverted(&self) -> &bool {
        &self.mouse_data.x_axis_scroll_inverted
    }

    #[allow(missing_docs)]
    pub fn get_x_scroll_inverted_mut(&mut self) -> &mut bool {
        &mut self.mouse_data.x_axis_scroll_inverted
    }

    #[allow(missing_docs)]
    pub fn get_y_scroll_inverted(&self) -> &bool {
        &self.mouse_data.y_axis_scroll_inverted
    }

    #[allow(missing_docs)]
    pub fn get_y_scroll_inverted_mut(&mut self) -> &mut bool {
        &mut self.mouse_data.y_axis_scroll_inverted
    }

    #[allow(missing_docs)]
    pub fn get_x_motion_inverted(&self) -> &bool {
        &self.mouse_data.x_axis_motion_inverted
    }

    #[allow(missing_docs)]
    pub fn get_x_motion_inverted_mut(&mut self) -> &mut bool {
        &mut self.mouse_data.x_axis_motion_inverted
    }

    #[allow(missing_docs)]
    pub fn get_y_motion_inverted(&self) -> &bool {
        &self.mouse_data.y_axis_motion_inverted
    }

    #[allow(missing_docs)]
    pub fn get_y_motion_inverted_mut(&mut self) -> &mut bool {
        &mut self.mouse_data.y_axis_motion_inverted
    }

    #[allow(missing_docs)]
    pub fn get_viewport_size(&self) -> &Size {
        &self.mouse_data.viewport_size
    }

    #[allow(missing_docs)]
    pub fn get_viewport_size_mut(&mut self) -> &mut Size {
        &mut self.mouse_data.viewport_size
    }
}

impl<A: Action> Default for InputRebind<A> {
    #[allow(missing_docs)]
    fn default() -> Self {
        InputRebind::new((800, 600).into())
    }
}

impl<A: Action> Into<InputTranslator<A>> for InputRebind<A> {
    #[allow(missing_docs)]
    fn into(self) -> InputTranslator<A> {
        let mut input_translator = InputTranslator::new(self.mouse_data.viewport_size);
        input_translator.mouse_translator.data = self.mouse_data;
        input_translator.keymap = self.keymap.values()
                                             .flat_map(|&bt| bt.into_iter())
                                             .filter_map(|x| x)
                                             .zip(self.keymap.keys().map(|x| *x))
                                             .collect();

        input_translator
    }
}

impl<A: Action> Into<InputRebind<A>> for InputTranslator<A> {
    #[allow(missing_docs, unreachable_code)]
    fn into(self) -> InputRebind<A> {
        unimplemented!();

        let mut input_rebind = InputRebind::new(self.mouse_translator.data.viewport_size);
        input_rebind.mouse_data = self.mouse_translator.data;
        //input_rebind.keymap = self.keymap.btn_map.
        //input_rebind.keymap = self.keymap.btn_map.drain().map(|(k, v)| (v, k)).collect();
        input_rebind
    }
}

impl ButtonTuple {
    fn into_iter(self) -> ButtonTupleIterator {
        ButtonTupleIterator {
            button_tuple: self,
            i: 0
        }
    }
}

struct ButtonTupleIterator {
    button_tuple: ButtonTuple,
    i: usize
}

impl Iterator for ButtonTupleIterator {
    type Item = Option<Button>;

    fn next(&mut self) -> Option<Self::Item> {
        let i = self.i;
        self.i += 1;
        match i {
            0 => Some(self.button_tuple.0),
            1 => Some(self.button_tuple.1),
            2 => Some(self.button_tuple.2),
            _ => None
        }
    }
}

<commit_msg>Improve documentation on InputTranslator::translate()<commit_after>#![warn(missing_docs)]

//! input-map
//! =========
//!
//! A library for binding input keys to actions, and manipulating mouse input

extern crate input;
extern crate piston_window;
extern crate rustc_serialize;
extern crate viewport;

mod builder;

use input::{Input, Button, Motion};
use piston_window::Size;
use std::collections::HashMap;
use std::default::Default;
use std::hash::Hash;
use viewport::Viewport;

pub use builder::InputTranslatorBuilder;

/// Represents a logical action to be bound to a particular button press, e.g.
/// jump, attack, or move forward. Needs to be hashable, as it is used as a
/// lookup key when rebinding an action to a different button.
pub trait Action: Copy + PartialEq + Eq + Hash { }

/// A translated action.
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum Translated<A: Action> {
    /// A keypress event which was bound to an action
    Press(A),

    /// A key release event which was bound to an action
    Release(A),

    /// A translated mouse motion. The logical origin of a translated MouseCursor event
    /// is in the top left corner of the window, and the logical scroll is non-natural.
    /// Relative events are unchanged for now.
    Move(Motion)
}

/// A three-element tuple of Option<Button>. For simplicity, a maximum number of 3
/// buttons can be bound to each action, and this is exposed through the `InputRebind`
/// struct.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct ButtonTuple(pub Option<Button>, pub Option<Button>, pub Option<Button>);

impl ButtonTuple {
    /// Creates a new tuple with no buttons in it (equivalent to `Default::default()`).
    pub fn new() -> Self { Default::default() }

    /// Check if the button is in the tuple.
    pub fn contains(&self, button: Button) -> bool {
        let sbtn = Some(button);
        self.0 == sbtn || self.1 == sbtn || self.2 == sbtn
    }

    /// Insert a button into the tuple if there is room, searching from left to right.
    /// If the button is inserted, returns true. Otherwise, if the button is not inserted,
    /// this function returns false.
    pub fn insert_inplace(&mut self, button: Button) -> bool {
        match self {
            &mut ButtonTuple(a, b, c) if a.is_none() => {*self = ButtonTuple(Some(button), b, c); true},
            &mut ButtonTuple(a, b, c) if b.is_none() => {*self = ButtonTuple(a, Some(button), c); true},
            &mut ButtonTuple(a, b, c) if c.is_none() => {*self = ButtonTuple(a, b, Some(button)); true}
            _ => false
        }
    }
}

impl Default for ButtonTuple {
    /// Creates a new tuple with no buttons in it.
    fn default() -> Self { ButtonTuple(None, None, None) }
}

/// An object which translates piston::input::Input events into input_map::Translated<A> events
#[derive(Clone)]
pub struct InputTranslator<A: Action> {
    keymap: HashMap<Button, A>,
    mouse_translator: MouseTranslator
}

impl<A: Action> InputTranslator<A> {
    /// Creates an empty InputTranslator.
    pub fn new(size: Size) -> Self {
        InputTranslator {
            keymap: HashMap::new(),
            mouse_translator: MouseTranslator::new(size)
        }
    }

    /// Translate an Input into a Translated<A> event. Returns `None` if there is no
    /// action associated with the `Input` variant.
    pub fn translate(&self, input: &Input) -> Option<Translated<A>> {
        macro_rules! translate_button(($but_state:ident, $but_var:ident) => (
            match self.keymap.get(&$but_var).map(|x| *x) {
                Some(act) => Some(Translated::$but_state(act)),
                None => None
            });
        );

        match input {
            &Input::Press(button) => translate_button!(Press, button),
            &Input::Release(button) => translate_button!(Release, button),
            &Input::Move(motion) =>
                Some(Translated::Move(self.mouse_translator.translate(motion))),
            _ => None
        }
    }

    /// Re-set the mouse bounds size used for calculating mouse events
    pub fn set_size(&mut self, size: Size) {
        self.mouse_translator.data.viewport_size = size
    }

    /// Re-set the mouse bounds size from a viewport
    pub fn set_size_from_viewport(&mut self, vp: Viewport) {
        self.set_size(Size::from(vp.draw_size));
    }
}

#[derive(Clone)]
struct MouseTranslationData {
    x_axis_motion_inverted: bool,
    y_axis_motion_inverted: bool,
    x_axis_scroll_inverted: bool,
    y_axis_scroll_inverted: bool,
    viewport_size: Size
}

impl MouseTranslationData {
    fn new(size: Size) -> Self {
        MouseTranslationData {
            x_axis_motion_inverted: false,
            y_axis_motion_inverted: false,
            x_axis_scroll_inverted: false,
            y_axis_scroll_inverted: false,
            viewport_size: size
        }
    }
}

#[derive(Clone)]
struct MouseTranslator {
    data: MouseTranslationData
}

impl MouseTranslator {
    fn new(size: Size) -> Self {
        MouseTranslator {
            data: MouseTranslationData::new(size)
        }
    }

    fn translate(&self, motion: Motion) -> Motion {
        match motion {
            Motion::MouseCursor(x, y) => {
                let (sw, sh) = {
                    let Size {width, height} = self.data.viewport_size;
                    (width as f64, height as f64)
                };

                let cx = if self.data.x_axis_motion_inverted { sw - x } else { x };
                let cy = if self.data.y_axis_motion_inverted { sh - y } else { y };

                Motion::MouseCursor(cx, cy)
            },
            Motion::MouseScroll(x, y) => {
                let mx = if self.data.x_axis_scroll_inverted { -1.0f64 } else { 1.0 };
                let my = if self.data.y_axis_scroll_inverted { -1.0f64 } else { 1.0 };
                Motion::MouseScroll(x * mx, y * my)
            },
            relative => relative
        }
    }
}

#[allow(missing_docs)]
pub struct InputRebind<A: Action> {
    keymap: HashMap<A, ButtonTuple>,
    mouse_data: MouseTranslationData
}

impl<A: Action> InputRebind<A> {
    #[allow(missing_docs)]
    pub fn new(size: Size) -> Self {
        InputRebind {
            keymap: HashMap::new(),
            mouse_data: MouseTranslationData::new(size)
        }
    }

    #[allow(missing_docs)]
    pub fn insert_action(&mut self, action: A) -> Option<ButtonTuple> {
        self.keymap.insert(action, ButtonTuple::new())
    }

    #[allow(missing_docs)]
    pub fn insert_action_with_buttons(&mut self, action: A, buttons: ButtonTuple) -> Option<ButtonTuple> {
        self.keymap.insert(action, buttons)
    }

    #[allow(missing_docs)]
    pub fn get_bindings(&self, action: &A) -> Option<&ButtonTuple> {
        self.keymap.get(action)
    }

    #[allow(missing_docs)]
    pub fn get_bindings_mut(&mut self, action: &mut A) -> Option<&mut ButtonTuple> {
        self.keymap.get_mut(action)
    }

    #[allow(missing_docs)]
    pub fn get_x_scroll_inverted(&self) -> &bool {
        &self.mouse_data.x_axis_scroll_inverted
    }

    #[allow(missing_docs)]
    pub fn get_x_scroll_inverted_mut(&mut self) -> &mut bool {
        &mut self.mouse_data.x_axis_scroll_inverted
    }

    #[allow(missing_docs)]
    pub fn get_y_scroll_inverted(&self) -> &bool {
        &self.mouse_data.y_axis_scroll_inverted
    }

    #[allow(missing_docs)]
    pub fn get_y_scroll_inverted_mut(&mut self) -> &mut bool {
        &mut self.mouse_data.y_axis_scroll_inverted
    }

    #[allow(missing_docs)]
    pub fn get_x_motion_inverted(&self) -> &bool {
        &self.mouse_data.x_axis_motion_inverted
    }

    #[allow(missing_docs)]
    pub fn get_x_motion_inverted_mut(&mut self) -> &mut bool {
        &mut self.mouse_data.x_axis_motion_inverted
    }

    #[allow(missing_docs)]
    pub fn get_y_motion_inverted(&self) -> &bool {
        &self.mouse_data.y_axis_motion_inverted
    }

    #[allow(missing_docs)]
    pub fn get_y_motion_inverted_mut(&mut self) -> &mut bool {
        &mut self.mouse_data.y_axis_motion_inverted
    }

    #[allow(missing_docs)]
    pub fn get_viewport_size(&self) -> &Size {
        &self.mouse_data.viewport_size
    }

    #[allow(missing_docs)]
    pub fn get_viewport_size_mut(&mut self) -> &mut Size {
        &mut self.mouse_data.viewport_size
    }
}

impl<A: Action> Default for InputRebind<A> {
    #[allow(missing_docs)]
    fn default() -> Self {
        InputRebind::new((800, 600).into())
    }
}

impl<A: Action> Into<InputTranslator<A>> for InputRebind<A> {
    #[allow(missing_docs)]
    fn into(self) -> InputTranslator<A> {
        let mut input_translator = InputTranslator::new(self.mouse_data.viewport_size);
        input_translator.mouse_translator.data = self.mouse_data;
        input_translator.keymap = self.keymap.values()
                                             .flat_map(|&bt| bt.into_iter())
                                             .filter_map(|x| x)
                                             .zip(self.keymap.keys().map(|x| *x))
                                             .collect();

        input_translator
    }
}

impl<A: Action> Into<InputRebind<A>> for InputTranslator<A> {
    #[allow(missing_docs, unreachable_code)]
    fn into(self) -> InputRebind<A> {
        unimplemented!();

        let mut input_rebind = InputRebind::new(self.mouse_translator.data.viewport_size);
        input_rebind.mouse_data = self.mouse_translator.data;
        //input_rebind.keymap = self.keymap.btn_map.
        //input_rebind.keymap = self.keymap.btn_map.drain().map(|(k, v)| (v, k)).collect();
        input_rebind
    }
}

impl ButtonTuple {
    fn into_iter(self) -> ButtonTupleIterator {
        ButtonTupleIterator {
            button_tuple: self,
            i: 0
        }
    }
}

struct ButtonTupleIterator {
    button_tuple: ButtonTuple,
    i: usize
}

impl Iterator for ButtonTupleIterator {
    type Item = Option<Button>;

    fn next(&mut self) -> Option<Self::Item> {
        let i = self.i;
        self.i += 1;
        match i {
            0 => Some(self.button_tuple.0),
            1 => Some(self.button_tuple.1),
            2 => Some(self.button_tuple.2),
            _ => None
        }
    }
}

<|endoftext|>


<commit_before>//! A crate that re-exports various types defined in both `libc` and `winapi`.
//!
//! Stop having to write code like this:
//!
//! ```
//! #[cfg(unix)]
//! use libc::some_type;
//!
//! #[cfg(windows)]
//! use winapi::some_type;
//! ```
//!
//! Instead, write code like this:
//!
//! ```
//! use c_types::some_type;
//! ```
#![allow(non_camel_case_types)]

#[cfg(unix)]
mod unix {
  extern crate libc;

  pub type fd_set = libc::fd_set;

  #[repr(C)]
  #[derive(Debug)]
  #[allow(raw_pointer_derive)]
  pub struct hostent {
      pub h_name: *mut libc::c_char,
      pub h_aliases: *mut *mut libc::c_char,
      pub h_addrtype: libc::c_int,
      pub h_length: libc::c_int,
      pub h_addr_list: *mut *mut libc::c_char,
  }

  pub type in_addr = libc::in_addr;
  pub type in6_addr = libc::in6_addr;
  pub type sa_family_t = libc::sa_family_t;
  pub type sockaddr = libc::sockaddr;
  pub type sockaddr_in = libc::sockaddr_in;
  pub type sockaddr_in6 = libc::sockaddr_in6;
  pub type socklen_t = libc::socklen_t;

  pub const AF_INET: i32 = libc::AF_INET;
  pub const AF_INET6: i32 = libc::AF_INET6;
}

#[cfg(windows)]
mod windows {
  extern crate winapi;

  pub type fd_set = winapi::fd_set;
  pub type hostent = winapi::winsock2::hostent;
  pub type in_addr = winapi::in_addr;
  pub type in6_addr = winapi::in6_addr;
  pub type sa_family_t = winapi::ws2def::ADDRESS_FAMILY;
  pub type sockaddr = winapi::SOCKADDR;
  pub type sockaddr_in = winapi::ws2def::SOCKADDR_IN;
  pub type sockaddr_in6 = winapi::ws2ipdef::sockaddr_in6;
  pub type socklen_t = winapi::socklen_t;

  pub const AF_INET: i32 = winapi::ws2def::AF_INET;
  pub const AF_INET6: i32 = winapi::ws2def::AF_INET6;
}

#[cfg(unix)]
pub use self::unix::*;

#[cfg(windows)]
pub use self::windows::*;
<commit_msg>cargo doc is too clever by half<commit_after>//! A crate that re-exports various types defined in both `libc` and `winapi`.
//!
//! Stop having to write code like this:
//!
//! ```ignore
//! #[cfg(unix)]
//! use libc::some_type;
//!
//! #[cfg(windows)]
//! use winapi::some_type;
//! ```
//!
//! Instead, write code like this:
//!
//! ```ignore
//! use c_types::some_type;
//! ```
#![allow(non_camel_case_types)]

#[cfg(unix)]
mod unix {
  extern crate libc;

  pub type fd_set = libc::fd_set;

  #[repr(C)]
  #[derive(Debug)]
  #[allow(raw_pointer_derive)]
  pub struct hostent {
      pub h_name: *mut libc::c_char,
      pub h_aliases: *mut *mut libc::c_char,
      pub h_addrtype: libc::c_int,
      pub h_length: libc::c_int,
      pub h_addr_list: *mut *mut libc::c_char,
  }

  pub type in_addr = libc::in_addr;
  pub type in6_addr = libc::in6_addr;
  pub type sa_family_t = libc::sa_family_t;
  pub type sockaddr = libc::sockaddr;
  pub type sockaddr_in = libc::sockaddr_in;
  pub type sockaddr_in6 = libc::sockaddr_in6;
  pub type socklen_t = libc::socklen_t;

  pub const AF_INET: i32 = libc::AF_INET;
  pub const AF_INET6: i32 = libc::AF_INET6;
}

#[cfg(windows)]
mod windows {
  extern crate winapi;

  pub type fd_set = winapi::fd_set;
  pub type hostent = winapi::winsock2::hostent;
  pub type in_addr = winapi::in_addr;
  pub type in6_addr = winapi::in6_addr;
  pub type sa_family_t = winapi::ws2def::ADDRESS_FAMILY;
  pub type sockaddr = winapi::SOCKADDR;
  pub type sockaddr_in = winapi::ws2def::SOCKADDR_IN;
  pub type sockaddr_in6 = winapi::ws2ipdef::sockaddr_in6;
  pub type socklen_t = winapi::socklen_t;

  pub const AF_INET: i32 = winapi::ws2def::AF_INET;
  pub const AF_INET6: i32 = winapi::ws2def::AF_INET6;
}

#[cfg(unix)]
pub use self::unix::*;

#[cfg(windows)]
pub use self::windows::*;
<|endoftext|>


<commit_before>// Copyright 2017 Rapidity Networks, Inc.
//
// 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.

extern crate hyper;
#[cfg(feature = "with-rustls")]
extern crate hyper_rustls;
#[cfg(feature = "with-openssl")]
extern crate hyper_openssl;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate serde_qs;

mod client;
mod error;
mod resources;
mod params;

pub use client::{Client, Params};
pub use error::{Error, ErrorCode, ErrorType, RequestError};
pub use params::{List, RangeQuery, RangeBounds, Metadata, Timestamp};
pub use resources::*;
<commit_msg>Add crate documentation for rust docs<commit_after>// Copyright 2017 Rapidity Networks, Inc.
//
// 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.

#![doc(html_root_url = "https://docs.rs/stripe-rust/")]

//! This crate provides Rust bindings to the Stripe HTTP API.
//!
//! ## Getting Started
//!
//! To get started, we need to create a client:
//!
//! ```rust
//!   let client = stripe::Client("sk_test_YOUR_STRIPE_SECRET");
//! ```
//!
//! Then we can begin making requests as we'd like.  Most Stripe requests accept
//! many optional parameters, so we usually get the `::default()` params and then
//! set the ones we want from there.
//!
//! Most requests for creating or updating a Stripe object use the same Rust struct,
//! so you may frequently need to refer to the [official API docs](https://stripe.com/docs/api)
//! to determine which fields are required for either request.
//!
//! ```rust
//! /* Creating a Stripe Charge */
//!
//! let token = "TOKEN_FROM_CHECKOUT";
//! let mut params = stripe::ChargeParams::default();
//! // NOTE: Stripe represents currency in the lowest denominations (e.g. cents)
//! params.amount = Some(1095); // e.g. $10.95
//! params.source = Some(stripe::CustomerSource::Token(token));
//!
//! // Example: Override currency to be in Canadian Dollars
//! params.currency = Some(stripe::Currency::CAD);
//! let charge = stripe::Charge::create(&client, params).unwrap();
//! println!("{:?}", charge); // =>  Charge { id: "ch_12345", amount: 1095, .. }
//! ```
//!
//! ```rust
//! /* Listing Stripe Charges */
//!
//! let params = stripe::ChargeListParams::default();
//! let charges = stripe::Charge::list(&client, params).unwrap();
//! println!("{:?}", charges); // =>  List { data: [Charge { id: "ch_12345", .. }] }
//! ```

extern crate hyper;
#[cfg(feature = "with-rustls")]
extern crate hyper_rustls;
#[cfg(feature = "with-openssl")]
extern crate hyper_openssl;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate serde_qs;

mod client;
mod error;
mod resources;
mod params;

pub use client::{Client, Params};
pub use error::{Error, ErrorCode, ErrorType, RequestError};
pub use params::{List, RangeQuery, RangeBounds, Metadata, Timestamp};
pub use resources::*;
<|endoftext|>


<commit_before>use std::ops::*;

pub mod abs;
pub use abs::Abs;

pub mod pi;
pub use pi::PI;

pub mod from_primitive;
pub use from_primitive::FromPrimitive;

pub mod min;
pub use min::Min;

pub mod max;
pub use max::Max;

pub mod signum;
pub use signum::Signum;

pub mod one;
pub use one::One;

pub mod zero;
pub use zero::Zero;

pub mod sqrt;
pub use sqrt::Sqrt;

pub mod trig;
pub use trig::Trig;

pub trait Num:
    Copy + One + Zero + Sqrt
    + Min + Max + Signum
    + Abs
    + PI
    + FromPrimitive
    + Trig
    + PartialEq
    + PartialOrd
    + ToString
    + Add<Self, Output = Self>
    + Mul<Self, Output = Self>
    + Sub<Self, Output = Self>
    + Div<Self, Output = Self>
    + Rem<Self, Output = Self>
    + Neg<Output = Self> {}

impl<T> Num for T where T:
    Copy + One + Zero + Sqrt
    + Min + Max + Signum
    + Abs
    + PI
    + FromPrimitive
    + Trig
    + PartialEq
    + PartialOrd
    + ToString
    + Add<T, Output = T>
    + Mul<T, Output = T>
    + Sub<T, Output = T>
    + Div<T, Output = T>
    + Rem<T, Output = T>
    + Neg<Output = T> {}
<commit_msg>updates<commit_after>use std::ops::*;

pub mod abs;
pub use abs::Abs;

pub mod pi;
pub use pi::PI;

pub mod from_primitive;
pub use from_primitive::FromPrimitive;

pub mod min;
pub use min::Min;

pub mod max;
pub use max::Max;

pub mod signum;
pub use signum::Signum;

pub mod one;
pub use one::One;

pub mod zero;
pub use zero::Zero;

pub mod sqrt;
pub use sqrt::Sqrt;

pub mod trig;
pub use trig::Trig;

pub trait Num:
    Copy + One + Zero + Sqrt
    + Min + Max + Signum
    + Abs
    + PI
    + FromPrimitive
    + Trig
    + PartialEq
    + PartialOrd
    + ToString

    + BitAnd<Self, Output = Self>
    + BitOr<Self, Output = Self>
    + BitXor<Self, Output = Self>
    + Shl<Self, Output = Self>
    + Shr<Self, Output = Self>

    + Add<Self, Output = Self>
    + Mul<Self, Output = Self>
    + Sub<Self, Output = Self>
    + Div<Self, Output = Self>
    + Rem<Self, Output = Self>
    + Neg<Output = Self> {}

impl<T> Num for T where T:
    Copy + One + Zero + Sqrt
    + Min + Max + Signum
    + Abs
    + PI
    + FromPrimitive
    + Trig
    + PartialEq
    + PartialOrd
    + ToString

    + BitAnd<T, Output = T>
    + BitOr<T, Output = T>
    + BitXor<T, Output = T>
    + Shl<T, Output = T>
    + Shr<T, Output = T>

    + Add<T, Output = T>
    + Mul<T, Output = T>
    + Sub<T, Output = T>
    + Div<T, Output = T>
    + Rem<T, Output = T>
    + Neg<Output = T> {}
<|endoftext|>


<commit_before>/*
Copyright 2014 Benjamin Elder from https://github.com/BenTheElder/slack-rs

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

	http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
extern crate hyper;
extern crate websocket;
extern crate "rustc-serialize" as rustc_serialize;
extern crate url;

use rustc_serialize::json::{Json};
use std::sync::mpsc::{Sender,channel};
use std::thread::Thread;
use std::sync::atomic::{AtomicIsize, Ordering};
use websocket::message::WebSocketMessage;
use websocket::handshake::WebSocketRequest;
use url::Url;

///Implement this trait in your code to handle message events
pub trait MessageHandler {
	///When a message is received this will be called with self, the slack client,
	///and the json encoded string payload.
	fn on_receive(&mut self, cli: &mut RtmClient, json_str: &str);

	///Called when a ping is received; you do NOT need to handle the reply pong,
	///but you may use this event to track the connection as a keep-alive.
	fn on_ping(&mut self, cli: &mut RtmClient);

	///Called when the connection is closed for any reason.
	fn on_close(&mut self, cli: &mut RtmClient);
}

///Contains information about the team the bot is logged into.
pub struct Team {
	name : String,
	id : String
}

impl Team {
	///private, create empty team.
	fn new() -> Team {
		Team{name: String::new(), id: String::new()}
	}

	///Returns the team's name as a String
	pub fn get_name(&self) -> String {
		self.name.clone()
	}

	///Returns the team's id as a String
	pub fn get_id(&self) -> String {
		self.id.clone()
	}
}

///The actual messaging client.
pub struct RtmClient {
	name : String,
	id : String,
	team : Team,
	msg_num: AtomicIsize,
	outs : Option<Sender<String>>
}

///Error string. (FIXME: better error return values/ custom error type)
static RTM_INVALID : &'static str = "Invalid data returned from slack (rtm.start)";

impl RtmClient {

	///Creates a new empty client.
	pub fn new() -> RtmClient {
		RtmClient{
			name : String::new(),
			id : String::new(),
			team : Team::new(),
			msg_num: AtomicIsize::new(0),
			outs : None
		}
	}

	///Returns the name of the bot/user connected to the client.
	///Only valid after login.
	pub fn get_name(&self) -> String {
		return self.name.clone();
	}

	///Returns the id of the bot/user connected to the client.
	///Only valid after login.
	pub fn get_id(&self) -> String {
		return self.id.clone();
	}

	///Returns the Team struct of the bot/user connected to the client.
	///Only valid after login.
	pub fn get_team<'a>(&'a self) -> &'a Team {
		&self.team
	}

	///Returns a unique identifier to be used in the 'id' field of a message
	///sent to slack.
	pub fn get_msg_uid(&self) -> isize {
		self.msg_num.fetch_add(1, Ordering::SeqCst)
	}

	///Allows sending a json string message over the websocket connection.
	///Note that this only passes the message over a channel to the
	///Messaging task, and therfore a succesful return value does not
	///mean the message has been actually put on the wire yet.
	///Note that you will need to form a valid json reply yourself if you
	///use this method, and you will also need to retrieve a unique id for
	///the message via RtmClient.get_msg_uid()
	///Only valid after login.
	pub fn send(&mut self, s : &str) -> Result<(),String> {
		let tx = match self.outs {
			Some(ref tx) => tx,
			None => return Err("Failed to get tx!".to_string())
		};
		match tx.send(s.to_string()) {
			Ok(_) => {},
			Err(err) => return Err(format!("{:?}", err))
		}
		Ok(())
	}

	///Allows sending a textual string message over the websocket connection,
	///to the requested channel id. Ideal usage would be EG:
	///extract the channel in on_receive and then send back a message to the channel.
	///Note that this only passes the message over a channel to the
	///Messaging task, and therfore a succesful return value does not
	///mean the message has been actually put on the wire yet.
	///This method also handles getting a unique id and formatting the actual json
	///sent.
	///Only valid after login.
	pub fn send_message(&self, chan: &str, msg: &str) -> Result<(),String>{
		let n = self.get_msg_uid();
		let mstr = "{".to_string()+format!(r#""id": {},"type": "message","channel": "{}","text": "{}""#,n,chan,msg).as_slice()+"}";
		let tx = match self.outs {
			Some(ref tx) => tx,
			None => return Err("Failed to get tx!".to_string())
		};
		match tx.send(mstr) {
			Ok(_) => {},
			Err(err) => return Err(format!("{:?}", err))
		}
		Ok(())
	}

	///Runs the main loop for the client after logging in to slack,
	///returns an error if the process fails at an point, or an Ok(()) on succesful
	///close.
	///Takes a MessageHandler (implemented by the user) to call events handlers on.
	///once the first on_receive() or on_ping is called on the MessageHandler, you
	///can soon the 'Only valid after login' methods are safe to use.
	///Sending is run in a thread in parallel while the receive loop runs on the main thread.
	///Both loops should end on return.
	///Sending should be thread safe as the messages are passed in via a channel in
	///RtmClient.send and RtmClient.send_message
	pub fn login_and_run<T: MessageHandler>(&mut self, handler: &mut T, token : &str) -> Result<(),String> {
		//Slack real time api url
		let url = "https://slack.com/api/rtm.start?token=".to_string()+token;

		//Create http client and send request to slack
		let mut client = hyper::Client::new();
		let mut res = match client.get(url.as_slice()).send() {
			Ok(res) => res,
			Err(err) => return Err(format!("Hyper Error: {:?}", err))
		};

		//Read result string
		let res_str = match res.read_to_string() {
			Ok(res_str) => res_str,
			Err(err) => return Err(format!("{:?}", err))
		};

		//Start parsing json. We do not map to a structure,
		//because slack makes no guarantee that there won't be extra fields.
		let js = match Json::from_str(res_str.as_slice()) {
			Ok(js) => js,
			Err(err) => return Err(format!("{:?}", err))
		};

		if !js.is_object() {
			return Err(format!("{} : json is not an object.", RTM_INVALID))
		}
		let jo = js.as_object().unwrap();

		match jo.get("ok") {
			Some(v) => {
				if !(v.is_boolean() && v.as_boolean().unwrap() == true) {
					return Err(format!("{} : js.get(\"ok\") != true : {:?}", RTM_INVALID, jo))
				}
			},
			None => return Err(format!("{} : jo.get(\"ok\") returned None. : {:?}", RTM_INVALID, jo))
		}

		let wss_url_string = match jo.get("url") {
			Some(wss_url) => {
				if wss_url.is_string() {
					wss_url.as_string().unwrap()
				}else{
					return Err(format!("{} : jo.get(\"url\") failed! : {:?}", RTM_INVALID, jo))
				}
			},
			None => return Err(format!("{} : jo.get(\"url\") returned None. : {:?}", RTM_INVALID, jo))
		};

		let wss_url = match Url::parse(wss_url_string) {
			Ok(url) => url,
			Err(err) => return Err(format!("{:?}", err))
		};

		let jself = match jo.get("self") {
			Some(jself) => {
				if jself.is_object() {
					jself.as_object().unwrap()
				}else{
					return Err(RTM_INVALID.to_string())
				}
			},
			None => return Err(RTM_INVALID.to_string())
		};
		match jself.get("name") {
			Some(jname) => {
				if jname.is_string() {
					self.name = jname.as_string().unwrap().to_string();
				}else{
					return Err(RTM_INVALID.to_string())
				}
			},
			None => return Err(RTM_INVALID.to_string())
		}
		match jself.get("id") {
			Some(jid) => {
				if jid.is_string() {
					self.id = jid.as_string().unwrap().to_string();
				}else{
					return Err(RTM_INVALID.to_string())
				}
			},
			None => return Err(RTM_INVALID.to_string())
		}

		let jteam = match jo.get("team") {
			Some(jteam) => {
				if jteam.is_object() {
					jteam.as_object().unwrap()
				}else{
					return Err(RTM_INVALID.to_string())
				}
			},
			None => return Err(RTM_INVALID.to_string())
		};
		match jteam.get("name") {
			Some(jtname) => {
				if jtname.is_string() {
					self.team.name = jtname.as_string().unwrap().to_string();
				}else{
					return Err(RTM_INVALID.to_string())
				}
			}
			None => return Err(RTM_INVALID.to_string())
		}
		match jteam.get("id") {
			Some(jtid) => {
				if jtid.is_string() {
					self.team.id = jtid.as_string().unwrap().to_string();
				}else{
					return Err(RTM_INVALID.to_string())
				}
			}
			None => return Err(RTM_INVALID.to_string())
		}

		//Make websocket request
		let req = match WebSocketRequest::connect(wss_url.clone()) {
			Ok(req) => req,
			Err(err) => return Err(format!("{:?} : WebSocketRequest::connect(wss_url): wss_url{:?}", err, wss_url))
		};

		//Get the key so we can verify it later.
		let key = match req.key() {
			Some(key) => key.clone(),
			None => return Err("Request host key match failed.".to_string())
		};

		//Connect via tls, do websocket handshake.
		let res = match req.send() {
			Ok(res) => res,
			Err(err) => return Err(format!("{:?}, Websocket request to `{:?}` failed", err, wss_url))
		};

		match res.validate(&key) {
			Ok(()) => { }
			Err(err) => return Err(format!("Error: res.validate(&key): {:?} : key: {:?}", err, key))
		}

		let mut client = res.begin();

		//for sending messages
		let (tx,rx) = channel::<String>();
		self.outs = Some(tx);

		let mut captured_client = client.clone();

		//websocket send loop
		let guard = Thread::spawn(move || -> () {
			loop {
				let m = match rx.recv() {
					Ok(m) => m,
					Err(err) => panic!(format!("{:?}", err))
				};
			 	let msg = WebSocketMessage::Text(m);
			 	match captured_client.send_message(msg) {
			 		Ok(_) => {},
			 		Err(err) => panic!(format!("{:?}", err))
			 	}
			 }
		});

		let mut sending_client = client.clone();

		//receive loop
		for message in client.incoming_messages() {
			let message = match message {
				Ok(message) => message,
				Err(err) => return Err(format!("{:?}", err))
			};

			match message {
				WebSocketMessage::Text(data) => {
					handler.on_receive(self, data.as_slice());
				},
				WebSocketMessage::Ping(data) => {
					handler.on_ping(self);
					let message = WebSocketMessage::Pong(data);
					match sending_client.send_message(message) {
						Ok(_) => {},
						Err(err) => { return Err(format!("{:?}", err)); }
					}
				},
				WebSocketMessage::Close(data) => {
					handler.on_close(self);
					let message = WebSocketMessage::Close(data);
					match sending_client.send_message(message) {
						Ok(_) => {},
						Err(err) => { return Err(format!("{:?}", err)); }
					}
					return Ok(());
				},
				_ => {}
			}
		}

		Ok(())
	}
}
<commit_msg>temporarily remove validation on websocket.<commit_after>/*
Copyright 2014 Benjamin Elder from https://github.com/BenTheElder/slack-rs

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

	http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
extern crate hyper;
extern crate websocket;
extern crate "rustc-serialize" as rustc_serialize;
extern crate url;

use rustc_serialize::json::{Json};
use std::sync::mpsc::{Sender,channel};
use std::thread::Thread;
use std::sync::atomic::{AtomicIsize, Ordering};
use websocket::message::WebSocketMessage;
use websocket::handshake::WebSocketRequest;
use url::Url;

///Implement this trait in your code to handle message events
pub trait MessageHandler {
	///When a message is received this will be called with self, the slack client,
	///and the json encoded string payload.
	fn on_receive(&mut self, cli: &mut RtmClient, json_str: &str);

	///Called when a ping is received; you do NOT need to handle the reply pong,
	///but you may use this event to track the connection as a keep-alive.
	fn on_ping(&mut self, cli: &mut RtmClient);

	///Called when the connection is closed for any reason.
	fn on_close(&mut self, cli: &mut RtmClient);
}

///Contains information about the team the bot is logged into.
pub struct Team {
	name : String,
	id : String
}

impl Team {
	///private, create empty team.
	fn new() -> Team {
		Team{name: String::new(), id: String::new()}
	}

	///Returns the team's name as a String
	pub fn get_name(&self) -> String {
		self.name.clone()
	}

	///Returns the team's id as a String
	pub fn get_id(&self) -> String {
		self.id.clone()
	}
}

///The actual messaging client.
pub struct RtmClient {
	name : String,
	id : String,
	team : Team,
	msg_num: AtomicIsize,
	outs : Option<Sender<String>>
}

///Error string. (FIXME: better error return values/ custom error type)
static RTM_INVALID : &'static str = "Invalid data returned from slack (rtm.start)";

impl RtmClient {

	///Creates a new empty client.
	pub fn new() -> RtmClient {
		RtmClient{
			name : String::new(),
			id : String::new(),
			team : Team::new(),
			msg_num: AtomicIsize::new(0),
			outs : None
		}
	}

	///Returns the name of the bot/user connected to the client.
	///Only valid after login.
	pub fn get_name(&self) -> String {
		return self.name.clone();
	}

	///Returns the id of the bot/user connected to the client.
	///Only valid after login.
	pub fn get_id(&self) -> String {
		return self.id.clone();
	}

	///Returns the Team struct of the bot/user connected to the client.
	///Only valid after login.
	pub fn get_team<'a>(&'a self) -> &'a Team {
		&self.team
	}

	///Returns a unique identifier to be used in the 'id' field of a message
	///sent to slack.
	pub fn get_msg_uid(&self) -> isize {
		self.msg_num.fetch_add(1, Ordering::SeqCst)
	}

	///Allows sending a json string message over the websocket connection.
	///Note that this only passes the message over a channel to the
	///Messaging task, and therfore a succesful return value does not
	///mean the message has been actually put on the wire yet.
	///Note that you will need to form a valid json reply yourself if you
	///use this method, and you will also need to retrieve a unique id for
	///the message via RtmClient.get_msg_uid()
	///Only valid after login.
	pub fn send(&mut self, s : &str) -> Result<(),String> {
		let tx = match self.outs {
			Some(ref tx) => tx,
			None => return Err("Failed to get tx!".to_string())
		};
		match tx.send(s.to_string()) {
			Ok(_) => {},
			Err(err) => return Err(format!("{:?}", err))
		}
		Ok(())
	}

	///Allows sending a textual string message over the websocket connection,
	///to the requested channel id. Ideal usage would be EG:
	///extract the channel in on_receive and then send back a message to the channel.
	///Note that this only passes the message over a channel to the
	///Messaging task, and therfore a succesful return value does not
	///mean the message has been actually put on the wire yet.
	///This method also handles getting a unique id and formatting the actual json
	///sent.
	///Only valid after login.
	pub fn send_message(&self, chan: &str, msg: &str) -> Result<(),String>{
		let n = self.get_msg_uid();
		let mstr = "{".to_string()+format!(r#""id": {},"type": "message","channel": "{}","text": "{}""#,n,chan,msg).as_slice()+"}";
		let tx = match self.outs {
			Some(ref tx) => tx,
			None => return Err("Failed to get tx!".to_string())
		};
		match tx.send(mstr) {
			Ok(_) => {},
			Err(err) => return Err(format!("{:?}", err))
		}
		Ok(())
	}

	///Runs the main loop for the client after logging in to slack,
	///returns an error if the process fails at an point, or an Ok(()) on succesful
	///close.
	///Takes a MessageHandler (implemented by the user) to call events handlers on.
	///once the first on_receive() or on_ping is called on the MessageHandler, you
	///can soon the 'Only valid after login' methods are safe to use.
	///Sending is run in a thread in parallel while the receive loop runs on the main thread.
	///Both loops should end on return.
	///Sending should be thread safe as the messages are passed in via a channel in
	///RtmClient.send and RtmClient.send_message
	pub fn login_and_run<T: MessageHandler>(&mut self, handler: &mut T, token : &str) -> Result<(),String> {
		//Slack real time api url
		let url = "https://slack.com/api/rtm.start?token=".to_string()+token;

		//Create http client and send request to slack
		let mut client = hyper::Client::new();
		let mut res = match client.get(url.as_slice()).send() {
			Ok(res) => res,
			Err(err) => return Err(format!("Hyper Error: {:?}", err))
		};

		//Read result string
		let res_str = match res.read_to_string() {
			Ok(res_str) => res_str,
			Err(err) => return Err(format!("{:?}", err))
		};

		//Start parsing json. We do not map to a structure,
		//because slack makes no guarantee that there won't be extra fields.
		let js = match Json::from_str(res_str.as_slice()) {
			Ok(js) => js,
			Err(err) => return Err(format!("{:?}", err))
		};

		if !js.is_object() {
			return Err(format!("{} : json is not an object.", RTM_INVALID))
		}
		let jo = js.as_object().unwrap();

		match jo.get("ok") {
			Some(v) => {
				if !(v.is_boolean() && v.as_boolean().unwrap() == true) {
					return Err(format!("{} : js.get(\"ok\") != true : {:?}", RTM_INVALID, jo))
				}
			},
			None => return Err(format!("{} : jo.get(\"ok\") returned None. : {:?}", RTM_INVALID, jo))
		}

		let wss_url_string = match jo.get("url") {
			Some(wss_url) => {
				if wss_url.is_string() {
					wss_url.as_string().unwrap()
				}else{
					return Err(format!("{} : jo.get(\"url\") failed! : {:?}", RTM_INVALID, jo))
				}
			},
			None => return Err(format!("{} : jo.get(\"url\") returned None. : {:?}", RTM_INVALID, jo))
		};

		let wss_url = match Url::parse(wss_url_string) {
			Ok(url) => url,
			Err(err) => return Err(format!("{:?}", err))
		};

		let jself = match jo.get("self") {
			Some(jself) => {
				if jself.is_object() {
					jself.as_object().unwrap()
				}else{
					return Err(RTM_INVALID.to_string())
				}
			},
			None => return Err(RTM_INVALID.to_string())
		};
		match jself.get("name") {
			Some(jname) => {
				if jname.is_string() {
					self.name = jname.as_string().unwrap().to_string();
				}else{
					return Err(RTM_INVALID.to_string())
				}
			},
			None => return Err(RTM_INVALID.to_string())
		}
		match jself.get("id") {
			Some(jid) => {
				if jid.is_string() {
					self.id = jid.as_string().unwrap().to_string();
				}else{
					return Err(RTM_INVALID.to_string())
				}
			},
			None => return Err(RTM_INVALID.to_string())
		}

		let jteam = match jo.get("team") {
			Some(jteam) => {
				if jteam.is_object() {
					jteam.as_object().unwrap()
				}else{
					return Err(RTM_INVALID.to_string())
				}
			},
			None => return Err(RTM_INVALID.to_string())
		};
		match jteam.get("name") {
			Some(jtname) => {
				if jtname.is_string() {
					self.team.name = jtname.as_string().unwrap().to_string();
				}else{
					return Err(RTM_INVALID.to_string())
				}
			}
			None => return Err(RTM_INVALID.to_string())
		}
		match jteam.get("id") {
			Some(jtid) => {
				if jtid.is_string() {
					self.team.id = jtid.as_string().unwrap().to_string();
				}else{
					return Err(RTM_INVALID.to_string())
				}
			}
			None => return Err(RTM_INVALID.to_string())
		}

		//Make websocket request
		let req = match WebSocketRequest::connect(wss_url.clone()) {
			Ok(req) => req,
			Err(err) => return Err(format!("{:?} : WebSocketRequest::connect(wss_url): wss_url{:?}", err, wss_url))
		};

		//Get the key so we can verify it later.
		let key = match req.key() {
			Some(key) => key.clone(),
			None => return Err("Request host key match failed.".to_string())
		};

		//Connect via tls, do websocket handshake.
		let res = match req.send() {
			Ok(res) => res,
			Err(err) => return Err(format!("{:?}, Websocket request to `{:?}` failed", err, wss_url))
		};

		//FIXME: Ignore validation for now, it seems to be bugged again.
		/*
		match res.validate(&key) {
			Ok(()) => { }
			Err(err) => return Err(format!("Error: res.validate(&key): {:?} : key: {:?}", err, key))
		}
		*/

		let mut client = res.begin();

		//for sending messages
		let (tx,rx) = channel::<String>();
		self.outs = Some(tx);

		let mut captured_client = client.clone();

		//websocket send loop
		let guard = Thread::spawn(move || -> () {
			loop {
				let m = match rx.recv() {
					Ok(m) => m,
					Err(err) => panic!(format!("{:?}", err))
				};
			 	let msg = WebSocketMessage::Text(m);
			 	match captured_client.send_message(msg) {
			 		Ok(_) => {},
			 		Err(err) => panic!(format!("{:?}", err))
			 	}
			 }
		});

		let mut sending_client = client.clone();

		//receive loop
		for message in client.incoming_messages() {
			let message = match message {
				Ok(message) => message,
				Err(err) => return Err(format!("{:?}", err))
			};

			match message {
				WebSocketMessage::Text(data) => {
					handler.on_receive(self, data.as_slice());
				},
				WebSocketMessage::Ping(data) => {
					handler.on_ping(self);
					let message = WebSocketMessage::Pong(data);
					match sending_client.send_message(message) {
						Ok(_) => {},
						Err(err) => { return Err(format!("{:?}", err)); }
					}
				},
				WebSocketMessage::Close(data) => {
					handler.on_close(self);
					let message = WebSocketMessage::Close(data);
					match sending_client.send_message(message) {
						Ok(_) => {},
						Err(err) => { return Err(format!("{:?}", err)); }
					}
					return Ok(());
				},
				_ => {}
			}
		}

		Ok(())
	}
}
<|endoftext|>


<commit_before>//! Securely create and manage temporary files. Temporary files created by this create are
//! automatically deleted.
//!
//! This crate provides two temporary file variants: a `tempfile()` function that returns standard
//! `File` objects and `NamedTempFile`. When choosing between the variants, prefer `tempfile()`
//! unless you either need to know the file's path or to be able to persist it.
//!
//! # Differences
//!
//! ## Resource Leaking
//!
//! `tempfile()` will (almost) never fail to cleanup temporary files but `NamedTempFile` will if
//! its destructor doesn't run. This is because `tempfile()` relies on the OS to cleanup the
//! underlying file so the file while `NamedTempFile` relies on its destructor to do so.
//!
//! ## Security
//!
//! In the presence of pathological temporary file cleaner, relying on file paths is unsafe because
//! a temporary file cleaner could delete the temporary file which an attacker could then replace.
//!
//! `tempfile()` doesn't rely on file paths so this isn't an issue. However, `NamedTempFile` does
//! rely on file paths.
//!

#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]

extern crate libc;
extern crate rand;

#[cfg(windows)]
extern crate winapi;

#[cfg(windows)]
extern crate kernel32;

const NUM_RETRIES: u32 = 1 << 31;
const NUM_RAND_CHARS: usize = 6;

mod util;
mod imp;
mod named;
mod unnamed;

pub use named::{NamedTempFile, NamedTempFileOptions};
pub use unnamed::{tempfile, tempfile_in};
<commit_msg>Expose PersistError<commit_after>//! Securely create and manage temporary files. Temporary files created by this create are
//! automatically deleted.
//!
//! This crate provides two temporary file variants: a `tempfile()` function that returns standard
//! `File` objects and `NamedTempFile`. When choosing between the variants, prefer `tempfile()`
//! unless you either need to know the file's path or to be able to persist it.
//!
//! # Differences
//!
//! ## Resource Leaking
//!
//! `tempfile()` will (almost) never fail to cleanup temporary files but `NamedTempFile` will if
//! its destructor doesn't run. This is because `tempfile()` relies on the OS to cleanup the
//! underlying file so the file while `NamedTempFile` relies on its destructor to do so.
//!
//! ## Security
//!
//! In the presence of pathological temporary file cleaner, relying on file paths is unsafe because
//! a temporary file cleaner could delete the temporary file which an attacker could then replace.
//!
//! `tempfile()` doesn't rely on file paths so this isn't an issue. However, `NamedTempFile` does
//! rely on file paths.
//!

#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]

extern crate libc;
extern crate rand;

#[cfg(windows)]
extern crate winapi;

#[cfg(windows)]
extern crate kernel32;

const NUM_RETRIES: u32 = 1 << 31;
const NUM_RAND_CHARS: usize = 6;

mod util;
mod imp;
mod named;
mod unnamed;

pub use named::{NamedTempFile, NamedTempFileOptions, PersistError};
pub use unnamed::{tempfile, tempfile_in};
<|endoftext|>


<commit_before>// Copyright 2012-2015 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.
//! libc - Raw FFI bindings to platforms' system libraries
#![crate_name = "libc"]
#![crate_type = "rlib"]
#![cfg_attr(not(feature = "rustc-dep-of-std"), deny(warnings))]
#![allow(bad_style, overflowing_literals, improper_ctypes, unknown_lints)]
// Attributes needed when building as part of the standard library
#![cfg_attr(
    feature = "rustc-dep-of-std",
    feature(cfg_target_vendor, link_cfg, no_core)
)]
// Enable extra lints:
#![cfg_attr(feature = "extra_traits", deny(missing_debug_implementations))]
#![deny(missing_copy_implementations, safe_packed_borrows)]
#![no_std]
#![cfg_attr(feature = "rustc-dep-of-std", no_core)]

#[macro_use]
mod macros;

cfg_if! {
    if #[cfg(feature = "rustc-dep-of-std")] {
        extern crate rustc_std_workspace_core as core;
        #[allow(unused_imports)]
        use core::iter;
        #[allow(unused_imports)]
        use core::option;
    }
}

cfg_if! {
    if #[cfg(libc_priv_mod_use)] {
        #[cfg(libc_core_cvoid)]
        #[allow(unused_imports)]
        use core::ffi;
        #[allow(unused_imports)]
        use core::fmt;
        #[allow(unused_imports)]
        use core::hash;
        #[allow(unused_imports)]
        use core::num;
        #[allow(unused_imports)]
        use core::mem;
        #[doc(hidden)]
        #[allow(unused_imports)]
        use core::clone::Clone;
        #[doc(hidden)]
        #[allow(unused_imports)]
        use core::marker::Copy;
        #[doc(hidden)]
        #[allow(unused_imports)]
        use core::option::Option;
    } else {
        #[doc(hidden)]
        #[allow(unused_imports)]
        pub use core::fmt;
        #[doc(hidden)]
        #[allow(unused_imports)]
        pub use core::hash;
        #[doc(hidden)]
        #[allow(unused_imports)]
        pub use core::num;
        #[doc(hidden)]
        #[allow(unused_imports)]
        pub use core::mem;
        #[doc(hidden)]
        #[allow(unused_imports)]
        pub use core::clone::Clone;
        #[doc(hidden)]
        #[allow(unused_imports)]
        pub use core::marker::Copy;
        #[doc(hidden)]
        #[allow(unused_imports)]
        pub use core::option::Option;
    }
}

cfg_if! {
    if #[cfg(windows)] {
        mod windows;
        pub use windows::*;
    } else if #[cfg(target_os = "redox")] {
        mod redox;
        pub use redox::*;
    } else if #[cfg(target_os = "cloudabi")] {
        mod cloudabi;
        pub use cloudabi::*;
    } else if #[cfg(target_os = "fuchsia")] {
        mod fuchsia;
        pub use fuchsia::*;
    } else if #[cfg(target_os = "switch")] {
        mod switch;
        pub use switch::*;
    } else if #[cfg(unix)] {
        mod unix;
        pub use unix::*;
    } else if #[cfg(target_env = "sgx")] {
        mod sgx;
        pub use sgx::*;
    }  else {
        // non-supported targets: empty...
    }
}
<commit_msg>Link docs for other platforms in docs.rs<commit_after>// Copyright 2012-2015 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.
//! libc - Raw FFI bindings to platforms' system libraries
//!
//! [Documentation for other platforms][pd].
//!
//! [pd]: https://rust-lang.github.io/libc/#platform-specific-documentation
#![crate_name = "libc"]
#![crate_type = "rlib"]
#![cfg_attr(not(feature = "rustc-dep-of-std"), deny(warnings))]
#![allow(bad_style, overflowing_literals, improper_ctypes, unknown_lints)]
// Attributes needed when building as part of the standard library
#![cfg_attr(
    feature = "rustc-dep-of-std",
    feature(cfg_target_vendor, link_cfg, no_core)
)]
// Enable extra lints:
#![cfg_attr(feature = "extra_traits", deny(missing_debug_implementations))]
#![deny(missing_copy_implementations, safe_packed_borrows)]
#![no_std]
#![cfg_attr(feature = "rustc-dep-of-std", no_core)]

#[macro_use]
mod macros;

cfg_if! {
    if #[cfg(feature = "rustc-dep-of-std")] {
        extern crate rustc_std_workspace_core as core;
        #[allow(unused_imports)]
        use core::iter;
        #[allow(unused_imports)]
        use core::option;
    }
}

cfg_if! {
    if #[cfg(libc_priv_mod_use)] {
        #[cfg(libc_core_cvoid)]
        #[allow(unused_imports)]
        use core::ffi;
        #[allow(unused_imports)]
        use core::fmt;
        #[allow(unused_imports)]
        use core::hash;
        #[allow(unused_imports)]
        use core::num;
        #[allow(unused_imports)]
        use core::mem;
        #[doc(hidden)]
        #[allow(unused_imports)]
        use core::clone::Clone;
        #[doc(hidden)]
        #[allow(unused_imports)]
        use core::marker::Copy;
        #[doc(hidden)]
        #[allow(unused_imports)]
        use core::option::Option;
    } else {
        #[doc(hidden)]
        #[allow(unused_imports)]
        pub use core::fmt;
        #[doc(hidden)]
        #[allow(unused_imports)]
        pub use core::hash;
        #[doc(hidden)]
        #[allow(unused_imports)]
        pub use core::num;
        #[doc(hidden)]
        #[allow(unused_imports)]
        pub use core::mem;
        #[doc(hidden)]
        #[allow(unused_imports)]
        pub use core::clone::Clone;
        #[doc(hidden)]
        #[allow(unused_imports)]
        pub use core::marker::Copy;
        #[doc(hidden)]
        #[allow(unused_imports)]
        pub use core::option::Option;
    }
}

cfg_if! {
    if #[cfg(windows)] {
        mod windows;
        pub use windows::*;
    } else if #[cfg(target_os = "redox")] {
        mod redox;
        pub use redox::*;
    } else if #[cfg(target_os = "cloudabi")] {
        mod cloudabi;
        pub use cloudabi::*;
    } else if #[cfg(target_os = "fuchsia")] {
        mod fuchsia;
        pub use fuchsia::*;
    } else if #[cfg(target_os = "switch")] {
        mod switch;
        pub use switch::*;
    } else if #[cfg(unix)] {
        mod unix;
        pub use unix::*;
    } else if #[cfg(target_env = "sgx")] {
        mod sgx;
        pub use sgx::*;
    }  else {
        // non-supported targets: empty...
    }
}
<|endoftext|>


<commit_before>extern crate toml;
extern crate rustc_serialize;
extern crate argparse;
#[macro_use] extern crate nickel;

use nickel::Nickel;
use self::routing::*;
use self::configuration:: { Configuration };
use argparse::{ ArgumentParser, Store };
use std::path:: { Path };
use std::env:: { current_dir };

mod routing;
mod middleware;
mod configuration;

pub fn server() {
    let mut config = Configuration::default();
    let mut config_file = "".to_string();
    {
        let mut ap = ArgumentParser::new();
        ap.set_description("Nickel application server");
        ap.refer(&mut config_file).add_option(&["-c", "--config"], Store, "configuration file");
        ap.parse_args_or_exit();
    }

    if !config_file.is_empty() {
        let cwd = current_dir().unwrap();
        let config_path = Path::new(&cwd).join(config_file);
        println!("{:?}", config_path);
        config = Configuration::from(config_path.as_path());
    }

    let mut server = Nickel::new();
    server.utilize(router());
    server.listen(config.server_address());
}
<commit_msg>Remove debug code<commit_after>extern crate toml;
extern crate rustc_serialize;
extern crate argparse;
#[macro_use] extern crate nickel;

use nickel::Nickel;
use self::routing::*;
use self::configuration:: { Configuration };
use argparse::{ ArgumentParser, Store };
use std::path:: { Path };
use std::env:: { current_dir };

mod routing;
mod middleware;
mod configuration;

pub fn server() {
    let mut config = Configuration::default();
    let mut config_file = "".to_string();
    {
        let mut ap = ArgumentParser::new();
        ap.set_description("Nickel application server");
        ap.refer(&mut config_file).add_option(&["-c", "--config"], Store, "configuration file");
        ap.parse_args_or_exit();
    }

    if !config_file.is_empty() {
        let cwd = current_dir().unwrap();
        let config_path = Path::new(&cwd).join(config_file);
        config = Configuration::from(config_path.as_path());
    }

    let mut server = Nickel::new();
    server.utilize(router());
    server.listen(config.server_address());
}
<|endoftext|>


<commit_before>#![feature(collections, unboxed_closures, core)]
extern crate itertools;
extern crate rustc_serialize;

use std::collections::{BTreeMap, HashMap, HashSet};
use std::collections::hash_map::Entry::*;
use std::hash::{Hash, Hasher};
use std::sync::Arc;
use std::iter;
use std::ops;

use itertools::{GroupBy, Itertools};

/// A Document contains an id and content.
/// Hashing and equality are based only on the id field.
#[derive(Clone, Eq, Debug, RustcEncodable, RustcDecodable)]
pub struct Document {
    id: String,
    content: String,
}

impl Document {
    /// Construct a new Document from an id and content.
    /// Both two arguments can be anything that can be turned into a String.
    pub fn new<S, T>(id: S, content: T) -> Document
        where S: Into<String>,
              T: Into<String>
    {
        Document { id: id.into(), content: content.into(), }
    }

    pub fn id(&self) -> &str {
        &self.id
    }

    pub fn content(&self) -> &str {
        &self.content
    }
}

impl Hash for Document {
    // Documents are unique only upon their id
    fn hash<H>(&self, state: &mut H) where H: Hasher {
        self.id.hash(state);
    }
}

impl PartialEq<Document> for Document {
    // Documents are unique only upon their id
    fn eq(&self, other: &Document) -> bool {
        self.id == other.id
    }
}

#[derive(Clone, Eq, PartialEq, Hash, Debug)]
struct IndexedDocument {
    pub doc: Arc<Document>,
    pub highlighted: (usize, usize),
}

/// A SearchResult is the representation of a Document returned for a specific set of search
/// terms. It is unique upon the document and the vec of highlight indices. It also contains a
/// score and a String of the document's content post-highlighting.
#[derive(Clone, Debug, RustcEncodable)]
pub struct SearchResult {
    doc: Arc<Document>,
    highlights: Vec<(usize, usize)>,
    score: f32,
}

impl PartialEq for SearchResult {
    // SearchResult is unique upon its document and highlights
    fn eq(&self, other: &SearchResult) -> bool {
        self.doc == other.doc && self.highlights == other.highlights
    }
}

impl Eq for SearchResult {}

impl Hash for SearchResult {
    // SearchResult is unique upon its document and highlights
    fn hash<H>(&self, state: &mut H) where H: Hasher {
        self.doc.hash(state);
        self.highlights.hash(state);
    }
}

impl SearchResult {
    fn new(doc: Arc<Document>, mut highlights: Vec<(usize, usize)>) -> SearchResult {
        SearchResult {
            score: highlights.iter()
                .map(|&(begin, end)| end - begin)
                .sum::<usize>() as f32 / (doc.content.len() as f32).sqrt(),
            doc: doc,
            highlights: { highlights.sort(); highlights },
        }
    }

    /// Returns the document
    pub fn doc(&self) -> &Arc<Document> {
        &self.doc
    }

    /// Returns the highlighted indices.
    ///
    /// Each `(usize, usize)` indicates the start and end of a term in the document's content
    /// that should be highlighted.
    pub fn highlights(&self) -> &Vec<(usize, usize)> {
        &self.highlights
    }

    /// Returns the search result's score.
    ///
    /// Score is computed by the product of the summed length of the matching terms and the inverse
    /// square root of the length of the document. Taking the square root of the document's length
    /// helps to combat bias toward short content.
    pub fn score(&self) -> f32 {
        self.score
    }

    pub fn highlight(&self, before: &str, after: &str) -> String {
        let mut begin_idx = 0;
        let mut parts = String::new();
        for &(begin, end) in &self.highlights {
            parts.push_str(&self.doc.content[begin_idx..begin]);
            parts.push_str(before);
            parts.push_str(&self.doc.content[begin..end]);
            parts.push_str(after);
            begin_idx = end;
        }
        parts.push_str(&self.doc.content[begin_idx..]);
        parts
    }
}

/// A basic implementation of an `Index`, the inverted index is a data structure that maps
/// from words to sets of Documents.
#[derive(Debug)]
pub struct InvertedIndex {
    index: BTreeMap<String, HashSet<IndexedDocument>>,
    docs: BTreeMap<String, Arc<Document>>,
}

impl InvertedIndex {
    pub fn new() -> InvertedIndex {
        InvertedIndex {
            index: BTreeMap::new(),
            docs: BTreeMap::new(),
        }
    }

    /// A basic implementation of index, splits the document's content into whitespace-separated
    /// words, and inserts each word-document pair into the map.
    pub fn index(&mut self, doc: Document) {
        let doc = Arc::new(doc);
        let analyzed  = analyze_doc(doc.content());
        let previous_version = self.docs.insert(doc.id.clone(), doc.clone());
        if let Some(previous_version) = previous_version {
            let previous_analyzed = analyze_doc(previous_version.content());
            for (ngram, highlights) in previous_analyzed {
                let is_empty = {
                    let docs_for_ngram = self.index.get_mut(&ngram).unwrap();
                    docs_for_ngram.remove(&IndexedDocument { doc: doc.clone(), highlighted: highlights });
                    docs_for_ngram.is_empty()
                };
                if is_empty {
                    self.index.remove(&ngram);
                }
            }
        }

        for (ngram, highlighted) in analyzed {
            self.index.entry(ngram).or_insert_with(|| HashSet::new())
                .insert(IndexedDocument { doc: doc.clone(), highlighted: highlighted });
        }
    }

    /// A basic search implementation that splits the query's content into whitespace-separated
    /// words, looks up the set of Documents for each word, and then concatenates the sets.
    pub fn search(&self, query: &str) -> Vec<SearchResult> {
        let unique_terms: HashSet<_> = query.split_whitespace().collect();
        let map = unique_terms.into_iter()
            .flat_map(|word| self.index.get(&word.to_lowercase()))
            .flat_map(|doc| doc)
            .cloned()
            .fold(HashMap::new(), |mut map, IndexedDocument { doc, highlighted: (begin, end) }| {
                match map.entry(doc) {
                    Vacant(entry) => {
                        let mut indices = HashMap::new();
                        indices.insert(begin, end);
                        entry.insert(indices);
                    }
                    Occupied(mut entry) => {
                        match entry.get_mut().entry(begin) {
                            Vacant(entry) => { entry.insert(end); }
                            Occupied(mut entry) => {
                                let current = entry.get_mut();
                                *current = std::cmp::max(*current, end);
                            }
                        }
                    }
                }
                map
            });
        let mut results: Vec<_> = map.into_iter()
            .map(|(doc, index_map)| SearchResult::new(doc, index_map.into_iter().collect()))
            .collect();
        results.sort_by(|result1, result2| result2.score.partial_cmp(&result1.score).unwrap());
        results
    }
}

fn analyze_doc(doc: &str)
-> iter::FlatMap<
    iter::Filter<
        GroupBy<bool, std::str::CharIndices, fn(&(usize, char)) -> bool>,
        fn(&(bool, Vec<(usize, char)>)) -> bool>,
    iter::Map<ops::Range<usize>, Ngrams>,
    fn((bool, Vec<(usize, char)>)) -> iter::Map<ops::Range<usize>, Ngrams>>
{
    doc.char_indices()
        .group_by(is_whitespace as fn(&(usize, char)) -> bool)
        .filter(not_whitespace as fn(&(bool, Vec<(usize, char)>)) -> bool)
        .flat_map(ngrams)
}

fn ngrams((_, chars): (bool, Vec<(usize, char)>)) -> iter::Map<ops::Range<usize>, Ngrams> {
    (1..chars.len() + 1).map(Ngrams::new(chars))
}

fn not_whitespace(&(is_whitespace, _): &(bool, Vec<(usize, char)>)) -> bool {
    !is_whitespace
}

fn is_whitespace(&(_, c): &(usize, char)) -> bool {
    c.is_whitespace()
}

struct Ngrams {
    chars: Vec<(usize, char)>
}

impl Ngrams {
    fn new(chars: Vec<(usize, char)>) -> Ngrams {
        Ngrams { chars: chars }
    }
}

impl Fn<(usize,)> for Ngrams {
    extern "rust-call" fn call(&self, (to,): (usize,)) -> (String, (usize, usize)) {
        let word = self.chars[..to].iter().flat_map(|&(_, c)| c.to_lowercase()).collect();
        let start = self.chars[0].0;
        let (last_idx, last_char) = self.chars[to - 1];
        let finish = last_idx + last_char.len_utf8();
        (word, (start, finish))
    }
}

impl FnMut<(usize,)> for Ngrams {
    extern "rust-call" fn call_mut(&mut self, to: (usize,)) -> (String, (usize, usize)) {
        self.call(to)
    }
}

impl FnOnce<(usize,)> for Ngrams {
    type Output = (String, (usize, usize));
    extern "rust-call" fn call_once(self, to: (usize,)) -> (String, (usize, usize)) {
        self.call(to)
    }
}

#[test]
fn test_search1() {
    let mut index = InvertedIndex::new();
    let doc1 = Document::new("1", "learn to program in rust today");
    index.index(doc1.clone());
    let doc2 = Document::new("2", "what did you today do");
    index.index(doc2.clone());
    let search_results = index.search("to");
    let expected = [
        SearchResult::new(Arc::new(doc1), vec![(6, 8), (25, 27)]),
        SearchResult::new(Arc::new(doc2), vec![(13, 15)])
    ];
    assert_eq!(search_results, expected.iter().cloned().collect::<Vec<_>>());
    assert_eq!("learn to program in rust today",
               expected[0].highlight("", ""));
}

#[test]
fn test_ngrams() {
    let mut index = InvertedIndex::new();
    let doc1 = Document::new("1", "learn to program in rust today");
    let doc2 = Document::new("2", "what did you today do");
    index.index(doc1.clone());
    index.index(doc2.clone());
    let search_results = index.search("to");
    let expected = [
        SearchResult::new(Arc::new(doc1), vec![(6, 8), (25, 27)]),
        SearchResult::new(Arc::new(doc2), vec![(13, 15)]),
    ];
    assert_eq!(search_results, expected.iter().cloned().collect::<Vec<_>>());
    assert_eq!("learn to program in rust today", expected[0].highlight("", ""));

}

#[test]
fn test_search2() {
    let mut index = InvertedIndex::new();
    let doc1 = Document::new("2", "what to do today");
    let doc2 = Document::new("3", "hey today");
    index.index(doc1.clone());
    index.index(doc2.clone());
    let search_results = index.search("to");
    let expected = [
        SearchResult::new(Arc::new(doc1), vec![(5, 7), (11, 13)]),
        SearchResult::new(Arc::new(doc2), vec![(4, 6)]),
    ];
    assert_eq!(search_results, expected.iter().cloned().collect::<Vec<_>>());
    assert_eq!("what to do today",
               expected[0].highlight("", ""));
}

#[test]
fn test_unicode() {
    let mut index = InvertedIndex::new();
    let doc = Document::new("0", "嗨, 您好");
    index.index(doc.clone());
    let to_search = "您";
    let search_results = index.search(to_search);
    let &SearchResult { ref doc, ref highlights, .. } = search_results.iter().next().unwrap();
    let (begin, end) = highlights[0];
    assert_eq!(&doc.content()[begin..end], to_search);
}

#[test]
fn test_update_doc() {
    let mut index = InvertedIndex::new();
    let doc = Document::new("0", "abc åäö");
    index.index(doc);
    let doc = Document::new("0", "different");
    index.index(doc);
    let search_results = index.search("å");
    assert!(search_results.is_empty());
    assert_eq!(index.docs.len(), 1);
}

#[test]
fn test_ranking() {
    let mut index = InvertedIndex::new();
    let doc = Document::new("0", "beat");
    index.index(doc.clone());
    let doc2 = Document::new("1", "beast");
    index.index(doc2.clone());
    let search_results = index.search("be");
    assert_eq!(index.docs.len(), 2);
    assert_eq!(&*search_results.into_iter().next().unwrap().doc, &doc);
}

#[test]
fn test_duplicate_term() {
    let mut index = InvertedIndex::new();
    let doc = Document::new("0", "beat");
    index.index(doc.clone());
    let search_results = index.search("be be");
    assert_eq!(search_results.len(), 1);
}

#[test]
fn test_duplicate_term2() {
    let mut index = InvertedIndex::new();
    let doc = Document::new("0", "beat");
    index.index(doc.clone());
    let search_results = index.search("be b");
    assert_eq!(search_results.len(), 1);
    assert_eq!(search_results[0], SearchResult::new(Arc::new(doc), vec![(0, 2)]));
}
<commit_msg>Add some bench tests and scope under a test module<commit_after>#![feature(collections, unboxed_closures, core, test)]
extern crate itertools;
extern crate rustc_serialize;

use std::collections::{BTreeMap, HashMap, HashSet};
use std::collections::hash_map::Entry::*;
use std::hash::{Hash, Hasher};
use std::sync::Arc;
use std::iter;
use std::ops;
use itertools::{GroupBy, Itertools};

/// A Document contains an id and content.
/// Hashing and equality are based only on the id field.
#[derive(Clone, Eq, Debug, RustcEncodable, RustcDecodable)]
pub struct Document {
    id: String,
    content: String,
}

impl Document {
    /// Construct a new Document from an id and content.
    /// Both two arguments can be anything that can be turned into a String.
    pub fn new<S, T>(id: S, content: T) -> Document
        where S: Into<String>,
              T: Into<String>
    {
        Document { id: id.into(), content: content.into(), }
    }

    pub fn id(&self) -> &str {
        &self.id
    }

    pub fn content(&self) -> &str {
        &self.content
    }
}

impl Hash for Document {
    // Documents are unique only upon their id
    fn hash<H>(&self, state: &mut H) where H: Hasher {
        self.id.hash(state);
    }
}

impl PartialEq<Document> for Document {
    // Documents are unique only upon their id
    fn eq(&self, other: &Document) -> bool {
        self.id == other.id
    }
}

#[derive(Clone, Eq, PartialEq, Hash, Debug)]
struct IndexedDocument {
    pub doc: Arc<Document>,
    pub highlighted: (usize, usize),
}

/// A SearchResult is the representation of a Document returned for a specific set of search
/// terms. It is unique upon the document and the vec of highlight indices. It also contains a
/// score and a String of the document's content post-highlighting.
#[derive(Clone, Debug, RustcEncodable)]
pub struct SearchResult {
    doc: Arc<Document>,
    highlights: Vec<(usize, usize)>,
    score: f32,
}

impl PartialEq for SearchResult {
    // SearchResult is unique upon its document and highlights
    fn eq(&self, other: &SearchResult) -> bool {
        self.doc == other.doc && self.highlights == other.highlights
    }
}

impl Eq for SearchResult {}

impl Hash for SearchResult {
    // SearchResult is unique upon its document and highlights
    fn hash<H>(&self, state: &mut H) where H: Hasher {
        self.doc.hash(state);
        self.highlights.hash(state);
    }
}

impl SearchResult {
    fn new(doc: Arc<Document>, mut highlights: Vec<(usize, usize)>) -> SearchResult {
        SearchResult {
            score: highlights.iter()
                .map(|&(begin, end)| end - begin)
                .sum::<usize>() as f32 / (doc.content.len() as f32).sqrt(),
            doc: doc,
            highlights: { highlights.sort(); highlights },
        }
    }

    /// Returns the document
    pub fn doc(&self) -> &Arc<Document> {
        &self.doc
    }

    /// Returns the highlighted indices.
    ///
    /// Each `(usize, usize)` indicates the start and end of a term in the document's content
    /// that should be highlighted.
    pub fn highlights(&self) -> &Vec<(usize, usize)> {
        &self.highlights
    }

    /// Returns the search result's score.
    ///
    /// Score is computed by the product of the summed length of the matching terms and the inverse
    /// square root of the length of the document. Taking the square root of the document's length
    /// helps to combat bias toward short content.
    pub fn score(&self) -> f32 {
        self.score
    }

    pub fn highlight(&self, before: &str, after: &str) -> String {
        let mut begin_idx = 0;
        let mut parts = String::new();
        for &(begin, end) in &self.highlights {
            parts.push_str(&self.doc.content[begin_idx..begin]);
            parts.push_str(before);
            parts.push_str(&self.doc.content[begin..end]);
            parts.push_str(after);
            begin_idx = end;
        }
        parts.push_str(&self.doc.content[begin_idx..]);
        parts
    }
}

/// A basic implementation of an `Index`, the inverted index is a data structure that maps
/// from words to sets of Documents.
#[derive(Debug)]
pub struct InvertedIndex {
    index: BTreeMap<String, HashSet<IndexedDocument>>,
    docs: BTreeMap<String, Arc<Document>>,
}

impl InvertedIndex {
    pub fn new() -> InvertedIndex {
        InvertedIndex {
            index: BTreeMap::new(),
            docs: BTreeMap::new(),
        }
    }

    /// A basic implementation of index, splits the document's content into whitespace-separated
    /// words, and inserts each word-document pair into the map.
    pub fn index(&mut self, doc: Document) {
        let doc = Arc::new(doc);
        let analyzed  = analyze_doc(doc.content());
        let previous_version = self.docs.insert(doc.id.clone(), doc.clone());
        if let Some(previous_version) = previous_version {
            let previous_analyzed = analyze_doc(previous_version.content());
            for (ngram, highlights) in previous_analyzed {
                let is_empty = {
                    let docs_for_ngram = self.index.get_mut(&ngram).unwrap();
                    docs_for_ngram.remove(&IndexedDocument { doc: doc.clone(), highlighted: highlights });
                    docs_for_ngram.is_empty()
                };
                if is_empty {
                    self.index.remove(&ngram);
                }
            }
        }

        for (ngram, highlighted) in analyzed {
            self.index.entry(ngram).or_insert_with(|| HashSet::new())
                .insert(IndexedDocument { doc: doc.clone(), highlighted: highlighted });
        }
    }

    /// A basic search implementation that splits the query's content into whitespace-separated
    /// words, looks up the set of Documents for each word, and then concatenates the sets.
    pub fn search(&self, query: &str) -> Vec<SearchResult> {
        let unique_terms: HashSet<_> = query.split_whitespace().map(str::to_lowercase).collect();
        let map = unique_terms.into_iter()
            .flat_map(|word| self.index.get(&word))
            .flat_map(|doc| doc)
            .cloned()
            .fold(HashMap::new(), |mut map, IndexedDocument { doc, highlighted: (begin, end) }| {
                match map.entry(doc) {
                    Vacant(entry) => {
                        let mut indices = HashMap::new();
                        indices.insert(begin, end);
                        entry.insert(indices);
                    }
                    Occupied(mut entry) => {
                        match entry.get_mut().entry(begin) {
                            Vacant(entry) => { entry.insert(end); }
                            Occupied(mut entry) => {
                                let current = entry.get_mut();
                                *current = std::cmp::max(*current, end);
                            }
                        }
                    }
                }
                map
            });
        let mut results: Vec<_> = map.into_iter()
            .map(|(doc, index_map)| SearchResult::new(doc, index_map.into_iter().collect()))
            .collect();
        results.sort_by(|result1, result2| result2.score.partial_cmp(&result1.score).unwrap());
        results
    }
}

fn analyze_doc(doc: &str)
-> iter::FlatMap<
    iter::Filter<
        GroupBy<bool, std::str::CharIndices, fn(&(usize, char)) -> bool>,
        fn(&(bool, Vec<(usize, char)>)) -> bool>,
    iter::Map<ops::Range<usize>, Ngrams>,
    fn((bool, Vec<(usize, char)>)) -> iter::Map<ops::Range<usize>, Ngrams>>
{
    doc.char_indices()
        .group_by(is_whitespace as fn(&(usize, char)) -> bool)
        .filter(not_whitespace as fn(&(bool, Vec<(usize, char)>)) -> bool)
        .flat_map(ngrams)
}

fn ngrams((_, chars): (bool, Vec<(usize, char)>)) -> iter::Map<ops::Range<usize>, Ngrams> {
    (1..chars.len() + 1).map(Ngrams::new(chars))
}

fn not_whitespace(&(is_whitespace, _): &(bool, Vec<(usize, char)>)) -> bool {
    !is_whitespace
}

fn is_whitespace(&(_, c): &(usize, char)) -> bool {
    c.is_whitespace()
}

struct Ngrams {
    chars: Vec<(usize, char)>
}

impl Ngrams {
    fn new(chars: Vec<(usize, char)>) -> Ngrams {
        Ngrams { chars: chars }
    }
}

impl Fn<(usize,)> for Ngrams {
    extern "rust-call" fn call(&self, (to,): (usize,)) -> (String, (usize, usize)) {
        let word = self.chars[..to].iter().flat_map(|&(_, c)| c.to_lowercase()).collect();
        let start = self.chars[0].0;
        let (last_idx, last_char) = self.chars[to - 1];
        let finish = last_idx + last_char.len_utf8();
        (word, (start, finish))
    }
}

impl FnMut<(usize,)> for Ngrams {
    extern "rust-call" fn call_mut(&mut self, to: (usize,)) -> (String, (usize, usize)) {
        self.call(to)
    }
}

impl FnOnce<(usize,)> for Ngrams {
    type Output = (String, (usize, usize));
    extern "rust-call" fn call_once(self, to: (usize,)) -> (String, (usize, usize)) {
        self.call(to)
    }
}

#[cfg(test)]
mod bench {
    extern crate test;
    use super::*;
    use self::test::Bencher;
    use std::sync::Arc;

    #[test]
    fn test_search1() {
        let mut index = InvertedIndex::new();
        let doc1 = Document::new("1", "learn to program in rust today");
        index.index(doc1.clone());
        let doc2 = Document::new("2", "what did you today do");
        index.index(doc2.clone());
        let search_results = index.search("to");
        let expected = [
            SearchResult::new(Arc::new(doc1), vec![(6, 8), (25, 27)]),
            SearchResult::new(Arc::new(doc2), vec![(13, 15)])
        ];
        assert_eq!(search_results, expected.iter().cloned().collect::<Vec<_>>());
        assert_eq!("learn to program in rust today",
                   expected[0].highlight("", ""));
    }

    #[test]
    fn test_ngrams() {
        let mut index = InvertedIndex::new();
        let doc1 = Document::new("1", "learn to program in rust today");
        let doc2 = Document::new("2", "what did you today do");
        index.index(doc1.clone());
        index.index(doc2.clone());
        let search_results = index.search("to");
        let expected = [
            SearchResult::new(Arc::new(doc1), vec![(6, 8), (25, 27)]),
            SearchResult::new(Arc::new(doc2), vec![(13, 15)]),
        ];
        assert_eq!(search_results, expected.iter().cloned().collect::<Vec<_>>());
        assert_eq!("learn to program in rust today", expected[0].highlight("", ""));

    }

    #[test]
    fn test_search2() {
        let mut index = InvertedIndex::new();
        let doc1 = Document::new("2", "what to do today");
        let doc2 = Document::new("3", "hey today");
        index.index(doc1.clone());
        index.index(doc2.clone());
        let search_results = index.search("to");
        let expected = [
            SearchResult::new(Arc::new(doc1), vec![(5, 7), (11, 13)]),
            SearchResult::new(Arc::new(doc2), vec![(4, 6)]),
        ];
        assert_eq!(search_results, expected.iter().cloned().collect::<Vec<_>>());
        assert_eq!("what to do today",
                   expected[0].highlight("", ""));
    }

    #[test]
    fn test_unicode() {
        let mut index = InvertedIndex::new();
        let doc = Document::new("0", "嗨, 您好");
        index.index(doc.clone());
        let to_search = "您";
        let search_results = index.search(to_search);
        let &SearchResult { ref doc, ref highlights, .. } = search_results.iter().next().unwrap();
        let (begin, end) = highlights[0];
        assert_eq!(&doc.content()[begin..end], to_search);
    }

    #[test]
    fn test_update_doc() {
        let mut index = InvertedIndex::new();
        let doc = Document::new("0", "abc åäö");
        index.index(doc);
        let doc = Document::new("0", "different");
        index.index(doc);
        let search_results = index.search("å");
        assert!(search_results.is_empty());
        assert_eq!(index.docs.len(), 1);
    }

    #[test]
    fn test_ranking() {
        let mut index = InvertedIndex::new();
        let doc = Document::new("0", "beat");
        index.index(doc.clone());
        let doc2 = Document::new("1", "beast");
        index.index(doc2.clone());
        let search_results = index.search("be");
        assert_eq!(index.docs.len(), 2);
        assert_eq!(&*search_results.into_iter().next().unwrap().doc, &doc);
    }

    #[test]
    fn test_duplicate_term() {
        let mut index = InvertedIndex::new();
        let doc = Document::new("0", "beat");
        index.index(doc.clone());
        let search_results = index.search("be be");
        assert_eq!(search_results.len(), 1);
    }

    #[test]
    fn test_duplicate_term2() {
        let mut index = InvertedIndex::new();
        let doc = Document::new("0", "beat");
        index.index(doc.clone());
        let search_results = index.search("be b");
        assert_eq!(search_results.len(), 1);
        assert_eq!(search_results[0], SearchResult::new(Arc::new(doc), vec![(0, 2)]));
    }

    #[test]
    fn test_lowercase_search() {
        let mut index = InvertedIndex::new();
        let doc = Document::new("0", "BeAt");
        index.index(doc.clone());
        let search_results = index.search("bE");
        assert_eq!(search_results.len(), 1);
        assert_eq!(search_results[0], SearchResult::new(Arc::new(doc), vec![(0, 2)]));
    }

    #[test]
    fn test_lowercase_index() {
        let mut index = InvertedIndex::new();
        let doc = Document::new("0", "BeAt");
        index.index(doc.clone());
        let search_results = index.search("be");
        assert_eq!(search_results.len(), 1);
        assert_eq!(search_results[0], SearchResult::new(Arc::new(doc), vec![(0, 2)]));
    }

    #[bench]
    fn bench_search(b: &mut Bencher) {
        let mut index = InvertedIndex::new();
        for i in 0..26 {
            index.index(Document::new(i.to_string(), (0..26).map(|j| ((i + j) % 26) as u8 as char).collect::<String>()));
        }
        b.iter(|| index.search("a"))
    }

    #[bench]
    fn bench_index(b: &mut Bencher) {
        let doc = Document::new("1", "Let's see you index this");
        b.iter(|| InvertedIndex::new().index(doc.clone()));
    }
}
<|endoftext|>


<commit_before>// Copyright 2017 Jeremy Wall <jeremy@marzhillstudios.com>
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
// #![feature(trace_macros,log_syntax)]

#[macro_use]
extern crate nom;
#[macro_use]
extern crate nom_locate;
extern crate serde_json;

#[macro_use]
pub mod ast;
#[macro_use]
pub mod tokenizer;
pub mod parse;
pub mod build;
pub mod convert;
pub mod error;

mod format;

pub use ast::Value;
pub use ast::Expression;
pub use ast::Statement;

pub use parse::parse;
pub use build::Builder;
<commit_msg>Basic language reference in the ucglib docs.<commit_after>// Copyright 2017 Jeremy Wall <jeremy@marzhillstudios.com>
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
// #![feature(trace_macros,log_syntax)]

//! # ucg, A universal configuration grammar.
//!
//! Ucg defines a common grammar for describing a collection of configuration values.
//! ucg allows you to specify configuration values with a syntax that that is immutable,
//! comoposable with copy-on-write semantics, and safe.
//!
//! ## Example
//!
//! ```ucg
//! // named bindings
//! let host = "mysql.internal.net";
//! let port = 8080
//!
//! // format strings
//! let connstr = "mysql://@:@" % (host, port);
//!
//! // tuples
//! let dbconf = {
//!     connstr = connstr,
//!     database = "mydb",
//!     // lists
//!     tables = ["posts", "comments", "users"],
//! };
//! ```
//!
//! ## Syntax
//!
//! ucg is a safe language with type inference that tries to guarantee that it will halt.
//! A valid ucg file is composesed of a series of statements. Statements are any valid
//! ucg expression terminated by a semicolon.
//!
//! ### Reserved words
//!
//! The following words are reserved in ucg and can't be used as named bindings.
//!
//! * let
//! * import
//! * as
//! * select
//! * macro
//!
//! ### Primitive types
//!
//! ucg has a relatively simple syntax with 3 primitive types, Int, Float, and String.
//!
//! * Int, is any integer number.
//! * Float is any number with a decimal point.
//!
//! ```ucg
//! 1.0; // A typical float.
//! 1. // You can leave off the 0 after the decimal point.
//! .1 // the leading 0 is also optional.
//! ```
//!
//! * String is any quoted text.
//!
//! ``` ucg
//! "foo"; // a smiple string
//! "I'm a \"fine\" looking string"; // escaped quotes in a string.
//! ```
//!
//! ### Complex types
//!
//! ucg has two complex data types, Lists and Tuples.
//!
//! * List's start are surrounded with square brackets `[ ]` and have comma separated elements.
//!
//! ```ucg
//! [1, 2, 3]; // A simple list of numbers.
//!
//! [[1, 2], [3, 4]] // A deep list with embedded lists inside.
//! ```
//!
//! Lists are 0 indexed and you can index into them using the dotted selector syntax.
//!
//! ```ucg
//! let mylist = [0, 1, 2, 3];
//!
//! let zero = mylist.0;
//! ```
//!
//! * Tuple's are an ordered collection of name, value pairs. They are bounded by curly braces `{ }`
//! and contain name = value pairs separated by commas. Trailing commas are permitted. The name must
//! be a bareword without quotes.
//!
//! ```ucg
//! let mytuple = {
//!     field1 = "value1",
//!     field2 = "value2",
//! };
//! ```
//!
//! Tuples can be indexed using dotted selector syntax.
//!
//! ```ucg
//! let field = mytuple.fields1;
//! ```
//!
//! ### Expressions
//!
//! #### Selectors
//!
//! Selectors are references to a bound value in ucg. They can index arbitrarily deep into either tuples or lists.
//! The head of a selector can be any expression that resolves to a tuple or list. Optionally a selector can also be
//! followed by either a bareword to index a tuple field or an integer to index a list position.
//!
//! The simplest selector is just a reference to a bound value by name.
//!
//! ```ucg
//! let mytuple = {
//!     field1 = "a string",
//!     field2 = [{
//!         subfield1 = 1,
//!     }];
//! };
//!
//! mytuple.field2.0; // descend into a deeply nested tuple and array.
//! ```
//!
//! #### Binary operators
//!
//! ucg supports the following operators, +, -, *, /; Each one is type safe and infers the types from the values they operate on.
//! The operators expect both the left and right operands to be of the same type. All of the operators are valid on integers and floats.
//! The + operator can additionally concatenate strings or arrays.
//!
//! ```ucg
//! 1 + 1; // result is 2
//! "foo " + "bar" // result is "foo bar"
//! [1,2] + [3,4]; // result is [1,2,3,4]
//! ```
//!
//! #### Conditional data.
//!
//! ucg supports a limited form of conditional selection of data using the select expression. A select expression starts with the select
//! keyword and is followed by a an expression resolving to a string naming the field to select, an expression resolving to the default value,
//! and a tuple to select the field from. If the field selected is not in the tuple then the default value will be used.
//!
//! ```ucg
//! let want = "baz";
//!
//! //     field  default
//! select want, "quux", {
//!     baz = "foo",
//!     fuzz = "bang",
//! }; // result will be "foo"
//!
//! //     field    default
//! select "quack", "quux", {
//!     baz = "foo",
//!     fuzz = "bang",
//! }; // result will be "quux"
//! ```
//!
//! #### Macros
//!
//! Macros look like functions but they are resolved at compile time and configurations don't execute so they never appear in output.
//! They are useful for constructing tuples of a certain shape or otherwise promoting data reuse. You define a macro with the macro
//! keyword followed by the arguments in parentheses and then a tuple.
//!
//! ```ucg
//! let myfunc = macro (arg1, arg2) {
//!     host = arg1,
//!     port = arg2,
//!     connstr = "couchdb://@:@" % (arg1, arg2),
//! }
//!
//! let my dbconf = myfunc("couchdb.example.org", "9090");
//!
//! let my dbhost = dbconf.host;
//! ```
//!
//! macros always resolve to a tuple. If you want to get a single value out you can use selector syntax to retrieve it.
//!
//! ### Statements
//!
//! There are 3 kinds of statements in a ucg configuration file. expression statements, let statements, and import statements.
//!
//! * expression statements
//! The simplest and least useful is the expression statements. It is any valid expression followed by a semicolon.
//!
//! ```ucg
//! 1;
//! 4 / 2;
//! "foo";
//! "foo" + "bar";
//! ```
//!
//! Despite the fact that these are valid the results are thrown away and can essentially be considered a noop. If we
//! ever create a repl for ucg statements they may prove more useful.
//!
//!  * Let statements
//! The let expression binds the result of any valid expression to a name. It starts with the let keyword and is followed by
//! the name of the binding, =, and a valid ucg expression.
//!
//! ```ucg
//! let name = "foo";
//! ```
//!
//! * Import statement
//! The import statement imports the contents of another ucg file into the current file with a name. The imported files bound
//! values are exposed as a tuple in the referencing file. It starts with the import keyword and is followed by a quoted path
//! to the ucg file, they keyword as, and a name for the imported values.
//!
//! ```ucg
//! import "dbconfigs.ucg" as dbconfigs;
//!
//! let mysqlconf = dbconfigs.mysql;
//! ```
#[macro_use]
extern crate nom;
#[macro_use]
extern crate nom_locate;
extern crate serde_json;

#[macro_use]
pub mod ast;
#[macro_use]
pub mod tokenizer;
pub mod parse;
pub mod build;
pub mod convert;
pub mod error;

mod format;

pub use ast::Value;
pub use ast::Expression;
pub use ast::Statement;

pub use parse::parse;
pub use build::Builder;
<|endoftext|>