<commit_before>#![allow(dead_code)]
#![allow(unused_variables)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(non_upper_case_globals)]

extern crate libc;
extern crate rustc_serialize;

use rustc_serialize::json;

use std::ffi::CString;
mod postgres;

use postgres::{
    LogicalDecodingContext,
    OutputPluginOptions,
    ReorderBufferTXN,
    Relation,
    ReorderBufferChange,
    TupleDesc,
    HeapTuple,
    Struct_HeapTupleData,
    OUTPUT_PLUGIN_TEXTUAL_OUTPUT,
    REORDER_BUFFER_CHANGE_INSERT,
    REORDER_BUFFER_CHANGE_UPDATE,
    REORDER_BUFFER_CHANGE_DELETE,
};

#[derive(RustcDecodable, RustcEncodable)]
pub struct WrappedPG {
    pub num_attributes: i32,
    pub fields:         Vec<Field>,
}

#[derive(RustcDecodable, RustcEncodable)]
pub struct Field {
    pub name:  String,
    pub value: String,
}

extern {
  fn OutputPluginPrepareWrite(ctx: &LogicalDecodingContext, last_write: bool);
  fn OutputPluginWrite(ctx: &LogicalDecodingContext, last_write: bool);
  fn appendStringInfoString(str: *mut postgres::Struct_StringInfoData, s: *const i8);
}

#[no_mangle]
pub extern fn pg_decode_startup(ctx: &LogicalDecodingContext, opt: &mut OutputPluginOptions, is_init: bool) {
	opt.output_type = OUTPUT_PLUGIN_TEXTUAL_OUTPUT;
}

#[no_mangle]
pub extern fn pg_decode_change(ctx:      &LogicalDecodingContext,
                               txn:      &ReorderBufferTXN,
                               relation: Relation,
                               change:   &mut ReorderBufferChange) {

    let action_string = match change.action {
        REORDER_BUFFER_CHANGE_INSERT => { "Insert " },
        REORDER_BUFFER_CHANGE_UPDATE => { "Update " },
        REORDER_BUFFER_CHANGE_DELETE => { "Delete " },
        _                            => { "Other " },
    };

    let c_action_string = CString::new(action_string).unwrap();

    unsafe {
        OutputPluginPrepareWrite(ctx, true);
        appendStringInfoString(ctx.out, c_action_string.as_ptr());
    }

    match change.action {
        REORDER_BUFFER_CHANGE_INSERT => {
            let tuple = pg_tuple_to_rspgod_tuple(
                unsafe { (*relation).rd_att },
                unsafe { (*(*change.data.tp()).newtuple).tuple }
            );

            let output         = format!("{}", json::encode(&tuple).unwrap());
            let c_tuple_string = CString::new(&output[..]).unwrap();

            unsafe {
                appendStringInfoString(ctx.out, c_tuple_string.as_ptr());
            }
        },
        // REORDER_BUFFER_CHANGE_UPDATE => { "Update".to_string() },
        // REORDER_BUFFER_CHANGE_DELETE => { "Delete".to_string() },
        _                            => {},
    };

    unsafe { OutputPluginWrite(ctx, true); }
}

pub fn extract_string(i8str:[::libc::c_char; 64usize]) -> String {
    let u8str:[u8; 64usize] = unsafe { std::mem::transmute(i8str) };
    let mut str = String::from_utf8(u8str.to_vec()).unwrap(); // unwrap = danger!
    str.chars().take_while(|c| *c != '\u{0}').collect()
}

// type TupleDesc = *mut Struct_tupleDesc
//   which is: struct Struct_tupleDesc {
//                      natts: ::libc::c_int,
//                      attrs: *mut Form_pg_attribute,
//                      tdtypeid: Oid,
pub fn pg_tuple_to_rspgod_tuple(description:TupleDesc, tuple:Struct_HeapTupleData) -> WrappedPG {
    let raw_desc       = unsafe { *description };
    let num_attributes = raw_desc.natts;
    let mut fields     = vec![];

    for n in 0..num_attributes {
        let pg_attribute = unsafe { **raw_desc.attrs.offset(n as isize) };
        let name         = extract_string(pg_attribute.attname.data);
        fields.push(Field { name: name.clone(), value: name.clone() });
    }

    WrappedPG {
        num_attributes: num_attributes,
        fields:         fields,
    }
}
    // tuple_to_avro_row (from bottledwater)
    //   this works by modifying a pointer to the (avro_value_t *output_value)
    //   or returning (at the end, or via the `check` macro) an error int
    // -----------------------------------------------------
    // int err = 0, field = 0;
    // check(err, avro_value_reset(output_val));
    // for (int i = 0; i < tupdesc->natts; i++) {
    //     avro_value_t field_val;
    //     bool isnull;
    //     Datum datum;
    //     Form_pg_attribute attr = tupdesc->attrs[i];
    //     if (attr->attisdropped) continue; /* skip dropped columns */
    //     check(err, avro_value_get_by_index(output_val, field, &field_val, NULL));
    //     datum = heap_getattr(tuple, i + 1, tupdesc, &isnull);
    //     if (isnull) {
    //         check(err, avro_value_set_branch(&field_val, 0, NULL));
    //     } else {
    //         check(err, update_avro_with_datum(&field_val, attr->atttypid, datum));
    //     }
    //     field++;
    // }
    // return err;

    // /* ----------------
    //  *		heap_getattr
    //  *
    //  *		Extract an attribute of a heap tuple and return it as a Datum.
    //  *		This works for either system or user attributes.  The given attnum
    //  *		is properly range-checked.
    //  *
    //  *		If the field in question has a NULL value, we return a zero Datum
    //  *		and set *isnull == true.  Otherwise, we set *isnull == false.
    //  *
    //  *		<tup> is the pointer to the heap tuple.  <attnum> is the attribute
    //  *		number of the column (field) caller wants.  <tupleDesc> is a
    //  *		pointer to the structure describing the row and all its fields.
    //  * ----------------
    //  */
    // #define heap_getattr(tup, attnum, tupleDesc, isnull) \
    // 	( \
    // 		((attnum) > 0) ? \
    // 		( \
    // 			((attnum) > (int) HeapTupleHeaderGetNatts((tup)->t_data)) ? \
    // 			( \
    // 				(*(isnull) = true), \
    // 				(Datum)NULL \
    // 			) \
    // 			: \
    // 				fastgetattr((tup), (attnum), (tupleDesc), (isnull)) \
    // 		) \
    // 		: \
    // 			heap_getsysattr((tup), (attnum), (tupleDesc), (isnull)) \
    // 	)
//
    // /* ----------------
    //  *		fastgetattr
    //  *
    //  *		Fetch a user attribute's value as a Datum (might be either a
    //  *		value, or a pointer into the data area of the tuple).
    //  *
    //  *		This must not be used when a system attribute might be requested.
    //  *		Furthermore, the passed attnum MUST be valid.  Use heap_getattr()
    //  *		instead, if in doubt.
    //  *
    //  *		This gets called many times, so we macro the cacheable and NULL
    //  *		lookups, and call nocachegetattr() for the rest.
    //  * ----------------
    //  */
    //
    // #if !defined(DISABLE_COMPLEX_MACRO)
    //
    // #define fastgetattr(tup, attnum, tupleDesc, isnull)					\
    // (																	\
    // 	AssertMacro((attnum) > 0),										\
    // 	(*(isnull) = false),											\
    // 	HeapTupleNoNulls(tup) ?											\
    // 	(																\
    // 		(tupleDesc)->attrs[(attnum)-1]->attcacheoff >= 0 ?			\
    // 		(															\
    // 			fetchatt((tupleDesc)->attrs[(attnum)-1],				\
    // 				(char *) (tup)->t_data + (tup)->t_data->t_hoff +	\
    // 					(tupleDesc)->attrs[(attnum)-1]->attcacheoff)	\
    // 		)															\
    // 		:															\
    // 			nocachegetattr((tup), (attnum), (tupleDesc))			\
    // 	)																\
    // 	:																\
    // 	(																\
    // 		att_isnull((attnum)-1, (tup)->t_data->t_bits) ?				\
    // 		(															\
    // 			(*(isnull) = true),										\
    // 			(Datum)NULL												\
    // 		)															\
    // 		:															\
    // 		(															\
    // 			nocachegetattr((tup), (attnum), (tupleDesc))			\
    // 		)															\
    // 	)																\
    // )
    // #else							/* defined(DISABLE_COMPLEX_MACRO) */
    //
    // extern Datum fastgetattr(HeapTuple tup, int attnum, TupleDesc tupleDesc,
    // 			bool *isnull);
    // #endif   /* defined(DISABLE_COMPLEX_MACRO) */

#[no_mangle]
pub extern fn pg_decode_commit_txn(ctx: &LogicalDecodingContext, txn: &ReorderBufferTXN, commit_lsn: u64) {
}

#[no_mangle]
pub extern fn pg_decode_begin_txn(ctx: &LogicalDecodingContext, txn: &ReorderBufferTXN) {
}

#[no_mangle]
pub extern fn pg_decode_shutdown(ctx: &LogicalDecodingContext) {
}

<commit_msg>Partially implement some of these macros<commit_after>#![allow(dead_code)]
#![allow(unused_variables)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(non_upper_case_globals)]

extern crate libc;
extern crate rustc_serialize;

use rustc_serialize::json;

use std::ffi::CString;
mod postgres;

use postgres::{
    heap_getsysattr,
    LogicalDecodingContext,
    OutputPluginOptions,
    ReorderBufferTXN,
    Relation,
    ReorderBufferChange,
    TupleDesc,
    HeapTuple,
    HeapTupleHeader,
    Datum,
    Struct_HeapTupleData,
    OUTPUT_PLUGIN_TEXTUAL_OUTPUT,
    REORDER_BUFFER_CHANGE_INSERT,
    REORDER_BUFFER_CHANGE_UPDATE,
    REORDER_BUFFER_CHANGE_DELETE,
};

#[derive(RustcDecodable, RustcEncodable)]
pub struct WrappedPG {
    pub num_attributes: u32,
    pub fields:         Vec<Field>,
}

#[derive(RustcDecodable, RustcEncodable)]
pub struct Field {
    pub name:  String,
    pub value: String,
}

extern {
  fn OutputPluginPrepareWrite(ctx: &LogicalDecodingContext, last_write: bool);
  fn OutputPluginWrite(ctx: &LogicalDecodingContext, last_write: bool);
  fn appendStringInfoString(str: *mut postgres::Struct_StringInfoData, s: *const i8);
}

#[no_mangle]
pub extern fn pg_decode_startup(ctx: &LogicalDecodingContext, opt: &mut OutputPluginOptions, is_init: bool) {
	opt.output_type = OUTPUT_PLUGIN_TEXTUAL_OUTPUT;
}

#[no_mangle]
pub extern fn pg_decode_change(ctx:      &LogicalDecodingContext,
                               txn:      &ReorderBufferTXN,
                               relation: Relation,
                               change:   &mut ReorderBufferChange) {

    let action_string = match change.action {
        REORDER_BUFFER_CHANGE_INSERT => { "Insert " },
        REORDER_BUFFER_CHANGE_UPDATE => { "Update " },
        REORDER_BUFFER_CHANGE_DELETE => { "Delete " },
        _                            => { "Other " },
    };

    let c_action_string = CString::new(action_string).unwrap();

    unsafe {
        OutputPluginPrepareWrite(ctx, true);
        appendStringInfoString(ctx.out, c_action_string.as_ptr());
    }

    match change.action {
        REORDER_BUFFER_CHANGE_INSERT => {
            let tuple = pg_tuple_to_rspgod_tuple(
                unsafe { (*relation).rd_att },
                unsafe { &mut(*(*change.data.tp()).newtuple).tuple }
            );

            let output         = format!("{}", json::encode(&tuple).unwrap());
            let c_tuple_string = CString::new(&output[..]).unwrap();

            unsafe {
                appendStringInfoString(ctx.out, c_tuple_string.as_ptr());
            }
        },
        // REORDER_BUFFER_CHANGE_UPDATE => { "Update".to_string() },
        // REORDER_BUFFER_CHANGE_DELETE => { "Delete".to_string() },
        _                            => {},
    };

    unsafe { OutputPluginWrite(ctx, true); }
}

pub fn extract_string(i8str:[::libc::c_char; 64usize]) -> String {
    let u8str:[u8; 64usize] = unsafe { std::mem::transmute(i8str) };
    let mut str = String::from_utf8(u8str.to_vec()).unwrap(); // unwrap = danger!
    str.chars().take_while(|c| *c != '\u{0}').collect()
}

// type TupleDesc = *mut Struct_tupleDesc
//   which is: struct Struct_tupleDesc {
//                      natts: ::libc::c_int,
//                      attrs: *mut Form_pg_attribute,
//                      tdtypeid: Oid,

// pub struct Struct_HeapTupleData {
//     pub t_len: uint32,
//     pub t_self: ItemPointerData,
//     pub t_tableOid: Oid,
//     pub t_data: HeapTupleHeader,
// }

// pub fn heap_getsysattr(tup: HeapTuple, attnum: ::libc::c_int, tupleDesc: TupleDesc, isnull: *mut _bool) -> Datum;
// pub fn nocachegetattr(tup: HeapTuple, attnum: ::libc::c_int, att: TupleDesc) -> Datum;
// #define TextDatumGetCString(d) text_to_cstring((text *) DatumGetPointer(d))
// src/include/access/htup_details.h
pub fn pg_tuple_to_rspgod_tuple(description:TupleDesc, tuple:HeapTuple) -> WrappedPG {
    let raw_desc         = unsafe { *description };
    let num_attributes   = raw_desc.natts as u32;
    let mut fields       = vec![];

    for n in 0..num_attributes {
        let pg_attribute = unsafe { **raw_desc.attrs.offset(n as isize) };
        let name         = extract_string(pg_attribute.attname.data);

        match heap_getattr(tuple, n + 1, description) {
            Some(datum) => {},
            None        => {},
        }
        fields.push(Field { name: name.clone(), value: name.clone() });
    }

    WrappedPG {
        num_attributes: num_attributes,
        fields:         fields,
    }
}
    // tuple_to_avro_row (from bottledwater)
    //   this works by modifying a pointer to the (avro_value_t *output_value)
    //   or returning (at the end, or via the `check` macro) an error int
    // -----------------------------------------------------
    // int err = 0, field = 0;
    // check(err, avro_value_reset(output_val));
    // for (int i = 0; i < tupdesc->natts; i++) {
    //     avro_value_t field_val;
    //     bool isnull;
    //     Datum datum;
    //     Form_pg_attribute attr = tupdesc->attrs[i];
    //     if (attr->attisdropped) continue; /* skip dropped columns */
    //     check(err, avro_value_get_by_index(output_val, field, &field_val, NULL));
    //     datum = heap_getattr(tuple, i + 1, tupdesc, &isnull);
    //     if (isnull) {
    //         check(err, avro_value_set_branch(&field_val, 0, NULL));
    //     } else {
    //         check(err, update_avro_with_datum(&field_val, attr->atttypid, datum));
    //     }
    //     field++;
    // }
    // return err;

// THESE ARE TRANSLATED FROM PG MACROS MOSTLY
// on my system: /usr/local/Cellar/postgresql/9.4.1/include/server/access/htup_details.h
/* ----------------
 *		heap_getattr
 *
 *		Extract an attribute of a heap tuple and return it as a Datum.
 *		This works for either system or user attributes.  The given attnum
 *		is properly range-checked.
 *
 *		If the field in question has a NULL value, we return a zero Datum
 *		and set *isnull == true.  Otherwise, we set *isnull == false.
 *
 *		<tup> is the pointer to the heap tuple.  <attnum> is the attribute
 *		number of the column (field) caller wants.  <tupleDesc> is a
 *		pointer to the structure describing the row and all its fields.
 * ----------------
 */

// 11 bits for number of attributes
const HEAP_NATTS_MASK   : u16 = 0x07FF;
// const HEAP_KEYS_UPDATED : u16 = 0x2000; // tuple was updated and key cols modified, or tuple deleted
// const HEAP_HOT_UPDATED  : u16 = 0x4000; // tuple was HOT-updated
// const HEAP_ONLY_TUPLE   : u16 = 0x8000; // this is heap-only tuple
// const HEAP2_XACT_MASK   : u16 = 0xE000; // visibility-related bits

pub fn HeapTupleHeaderGetNatts(header:HeapTupleHeader) -> u16  {
    unsafe { *header }.t_infomask2 & HEAP_NATTS_MASK
}

// information stored in t_infomask:
const HEAP_HASNULL          : u16 = 0x0001; // has null attribute(s)
// const HEAP_HASVARWIDTH      : u16 = 0x0002; // has variable-width attribute(s)
// const HEAP_HASEXTERNAL      : u16 = 0x0004; // has external stored attribute(s)
// const HEAP_HASOID           : u16 = 0x0008; // has an object-id field
// const HEAP_XMAX_KEYSHR_LOCK : u16 = 0x0010; // xmax is a key-shared locker
// const HEAP_COMBOCID         : u16 = 0x0020; // t_cid is a combo cid
// const HEAP_XMAX_EXCL_LOCK   : u16 = 0x0040; // xmax is exclusive locker
// const HEAP_XMAX_LOCK_ONLY   : u16 = 0x0080; // xmax, if valid, is only a locker
pub fn HeapTupleNoNulls(tuple:HeapTuple) -> bool {
    0 == (HEAP_HASNULL & unsafe { (*(*tuple).t_data).t_infomask })
}

// check to see if the ATT'th bit of an array of 8-bit bytes is set.
// pub fn att_isnull(attnum:u32, pub t_bits: [bits8; 1usize],) {
//     let low_bitmask  = 1 << (attnum & 0x07); // set the attnumth bit
//     let high_bitmask = (BITS)[attnum >> 3];
//     0 == (low_bitmask & high_bitmask)
// }

// pub type HeapTuple = *mut HeapTupleData;
// pub type HeapTupleData = Struct_HeapTupleData;
// pub t_data: HeapTupleHeader,

// pub type HeapTupleHeader = *mut Struct_HeapTupleHeaderData;
// pub struct Struct_HeapTupleHeaderData {
//     pub t_choice: Union_Unnamed26,
//     pub t_ctid: ItemPointerData,
//     pub t_infomask2: uint16,
//     pub t_infomask: uint16,
//     pub t_hoff: uint8,
//     pub t_bits: [bits8; 1usize],
// }

// Originally the macro heap_getattr
pub fn heap_getattr(tuple: HeapTuple, attnum: u32, tuple_desc: TupleDesc) -> Option<Datum> {
    if attnum > 0 {
        if attnum > HeapTupleHeaderGetNatts(unsafe { *tuple }.t_data) as u32 {
            None
        } else {
            fastgetattr(tuple, attnum, tuple_desc)
        }
    } else {
        let isnull = &mut (0 as ::libc::c_char);
        let datum  = unsafe { heap_getsysattr(tuple, attnum as i32, tuple_desc, isnull) };
        if(*isnull == (0 as ::libc::c_char)) {
            None
        } else {
            Some(datum)
        }
    }
}

/* ----------------
 *		fastgetattr
 *
 *		Fetch a user attribute's value as a Datum (might be either a
 *		value, or a pointer into the data area of the tuple).
 *
 *		This must not be used when a system attribute might be requested.
 *		Furthermore, the passed attnum MUST be valid.  Use heap_getattr()
 *		instead, if in doubt.
 *
 *		This gets called many times, so we macro the cacheable and NULL
 *		lookups, and call nocachegetattr() for the rest.
 * ----------------
 */
pub fn fastgetattr(tuple: HeapTuple, attnum: u32, tuple_desc: TupleDesc) -> Option<Datum> {
    None
    // if HeapTupleNoNulls(tuple) {
    //     if /* (tupleDesc)->attrs[(attnum)-1]->attcacheoff >= 0 ? */ {
    //         // fetchatt((tupleDesc)->attrs[(attnum)-1],
    //         // 	(char *) (tup)->t_data + (tup)->t_data->t_hoff +
    //         // 		(tupleDesc)->attrs[(attnum)-1]->attcacheoff)
    //     } else {
    //         Some(nocachegetattr(tuple, attnum, tuple_desc))
    //     }
    // } else {
    //     if att_isnull((attnum)-1, (tup)->t_data->t_bits) {
    //         None
    //     } else {
    //         Some(nocachegetattr(tuple, attnum, tuple_desc))
    //     }
    // }
}

#[no_mangle]
pub extern fn pg_decode_commit_txn(ctx: &LogicalDecodingContext, txn: &ReorderBufferTXN, commit_lsn: u64) {
}

#[no_mangle]
pub extern fn pg_decode_begin_txn(ctx: &LogicalDecodingContext, txn: &ReorderBufferTXN) {
}

#[no_mangle]
pub extern fn pg_decode_shutdown(ctx: &LogicalDecodingContext) {
}

<|endoftext|>


<commit_before>#![deny(missing_docs)]

//! The official Piston window wrapper for the Piston game engine
//!
//! The purpose of this library is to provide an easy-to-use,
//! simple-to-get-started and convenient-for-applications API for Piston.
//!
//! Sets up:
//!
//! - [Gfx](https://github.com/gfx-rs/gfx) with an OpenGL back-end.
//! - [gfx_graphics](https://github.com/pistondevelopers/gfx_graphics)
//! for 2D rendering.
//! - [glutin_window](https://github.com/pistondevelopers/glutin_window)
//! as default window back-end, but this can be swapped (see below).
//!
//! ### Example
//!
//! ```no_run
//! extern crate piston_window;
//!
//! use piston_window::*;
//!
//! fn main() {
//!     let mut window: PistonWindow =
//!         WindowSettings::new("Hello World!", [512; 2])
//!             .build().unwrap();
//!     while let Some(e) = window.next() {
//!         window.draw_2d(&e, |c, g| {
//!             clear([0.5, 0.5, 0.5, 1.0], g);
//!             rectangle([1.0, 0.0, 0.0, 1.0], // red
//!                       [0.0, 0.0, 100.0, 100.0], // rectangle
//!                       c.transform, g);
//!         });
//!     }
//! }
//! ```
//!
//! ### Swap to another window back-end
//!
//! Change the generic parameter to the window back-end you want to use.
//!
//! ```ignore
//! extern crate piston_window;
//! extern crate sdl2_window;
//!
//! use piston_window::*;
//! use sdl2_window::Sdl2Window;
//!
//! # fn main() {
//!
//! let window: PistonWindow<Sdl2Window> =
//!     WindowSettings::new("title", [512; 2])
//!         .build().unwrap();
//!
//! # }
//! ```
//!
//! ### sRGB
//!
//! The impl of `BuildFromWindowSettings` in this library turns on
//! `WindowSettings::srgb`, because it is required by gfx_graphics.
//!
//! Most images such as those found on the internet uses sRGB,
//! that has a non-linear gamma corrected space.
//! When rendering 3D, make sure textures and colors are in linear gamma space.
//! Alternative is to use `Srgb8` and `Srgba8` formats for textures.
//!
//! For more information about sRGB, see
//! https://github.com/PistonDevelopers/piston/issues/1014
//!
//! ### Library dependencies
//!
//! This library is meant to be used in applications only.
//! It is not meant to be depended on by generic libraries.
//! Instead, libraries should depend on the lower abstractions,
//! such as the [Piston core](https://github.com/pistondevelopers/piston).

extern crate piston;
extern crate gfx;
extern crate gfx_core;
extern crate gfx_device_gl;
extern crate gfx_graphics;
extern crate graphics;
extern crate shader_version;
extern crate glutin_window;
pub extern crate texture;

use glutin_window::GlutinWindow;
pub use shader_version::OpenGL;
pub use graphics::*;
pub use piston::window::*;
pub use piston::input::*;
pub use piston::event_loop::*;
pub use gfx_graphics::{ GlyphError, Texture, TextureSettings, Flip };

use gfx_graphics::{ Gfx2d, GfxGraphics };

/// Actual gfx::Stream implementation carried by the window.
pub type GfxEncoder = gfx::Encoder<gfx_device_gl::Resources,
    gfx_device_gl::CommandBuffer>;
/// Glyph cache.
pub type Glyphs = gfx_graphics::GlyphCache<gfx_device_gl::Resources,
    gfx_device_gl::Factory>;
/// 2D graphics.
pub type G2d<'a> = GfxGraphics<'a,
    gfx_device_gl::Resources,
    gfx_device_gl::CommandBuffer>;
/// Texture type compatible with `G2d`.
pub type G2dTexture<'a> = Texture<gfx_device_gl::Resources>;

/// Contains everything required for controlling window, graphics, event loop.
pub struct PistonWindow<W: Window = GlutinWindow> {
    /// The window.
    pub window: W,
    /// GFX encoder.
    pub encoder: GfxEncoder,
    /// GFX device.
    pub device: gfx_device_gl::Device,
    /// Output frame buffer.
    pub output_color: gfx::handle::RenderTargetView<
        gfx_device_gl::Resources, gfx::format::Srgba8>,
    /// Output stencil buffer.
    pub output_stencil: gfx::handle::DepthStencilView<
        gfx_device_gl::Resources, gfx::format::DepthStencil>,
    /// Gfx2d.
    pub g2d: Gfx2d<gfx_device_gl::Resources>,
    /// Event loop state.
    pub events: WindowEvents,
    /// The factory that was created along with the device.
    pub factory: gfx_device_gl::Factory,
}

impl<W> BuildFromWindowSettings for PistonWindow<W>
    where W: Window + OpenGLWindow + BuildFromWindowSettings,
          W::Event: GenericEvent
{
    fn build_from_window_settings(settings: &WindowSettings) -> Result<PistonWindow<W>, String> {
        // Turn on sRGB.
        let settings = settings.clone().srgb(true);

        // Use OpenGL 3.2 by default, because this is what window backends
        // usually do.
        let opengl = settings.get_maybe_opengl().unwrap_or(OpenGL::V3_2);
        let samples = settings.get_samples();

        Ok(PistonWindow::new(opengl, samples, try!(settings.build())))
    }
}

fn create_main_targets(dim: gfx::tex::Dimensions) ->
(gfx::handle::RenderTargetView<
    gfx_device_gl::Resources, gfx::format::Srgba8>,
 gfx::handle::DepthStencilView<
    gfx_device_gl::Resources, gfx::format::DepthStencil>) {
    use gfx_core::factory::Typed;
    use gfx::format::{DepthStencil, Format, Formatted, Srgba8};

    let color_format: Format = <Srgba8 as Formatted>::get_format();
    let depth_format: Format = <DepthStencil as Formatted>::get_format();
    let (output_color, output_stencil) =
        gfx_device_gl::create_main_targets_raw(dim,
                                               color_format.0,
                                               depth_format.0);
    let output_color = Typed::new(output_color);
    let output_stencil = Typed::new(output_stencil);
    (output_color, output_stencil)
}

impl<W> PistonWindow<W>
    where W: Window, W::Event: GenericEvent
{
    /// Creates a new piston window.
    pub fn new(opengl: OpenGL, samples: u8, mut window: W) -> Self
        where W: OpenGLWindow
    {
        let (device, mut factory) =
            gfx_device_gl::create(|s|
                window.get_proc_address(s) as *const _);

        let (output_color, output_stencil) = {
            let aa = samples as gfx::tex::NumSamples;
            let draw_size = window.draw_size();
            let dim = (draw_size.width as u16, draw_size.height as u16,
                       1, aa.into());
            create_main_targets(dim)
        };

        let g2d = Gfx2d::new(opengl, &mut factory);
        let encoder = factory.create_command_buffer().into();
        let events = window.events();
        PistonWindow {
            window: window,
            encoder: encoder,
            device: device,
            output_color: output_color,
            output_stencil: output_stencil,
            g2d: g2d,
            events: events,
            factory: factory,
        }
    }

    /// Renders 2D graphics.
    pub fn draw_2d<E, F, U>(&mut self, e: &E, f: F) -> Option<U> where
        W: OpenGLWindow,
        E: GenericEvent,
        F: FnOnce(Context, &mut G2d) -> U
    {
        self.window.make_current();
        if let Some(args) = e.render_args() {
            let res = self.g2d.draw(
                &mut self.encoder,
                &self.output_color,
                &self.output_stencil,
                args.viewport(),
                f
            );
            self.encoder.flush(&mut self.device);
            Some(res)
        } else {
            None
        }
    }

    /// Renders 3D graphics.
    pub fn draw_3d<E, F, U>(&mut self, e: &E, f: F) -> Option<U> where
        W: OpenGLWindow,
        E: GenericEvent,
        F: FnOnce(&mut Self) -> U
    {
        self.window.make_current();
        if let Some(_) = e.render_args() {
            let res = f(self);
            self.encoder.flush(&mut self.device);
            Some(res)
        } else {
            None
        }
    }

    /// Returns next event.
    /// Cleans up after rendering and resizes frame buffers.
    pub fn next(&mut self) -> Option<Event<<W as Window>::Event>> {
        use piston::input::*;
        use gfx_core::factory::Typed;
        use gfx::Device;

        if let Some(e) = self.events.next(&mut self.window) {
            if let Some(_) = e.after_render_args() {
                // After swapping buffers.
                self.device.cleanup();
            }

            // Check whether window has resized and update the output.
            let dim = self.output_color.raw().get_dimensions();
            let (w, h) = (dim.0, dim.1);
            let draw_size = self.window.draw_size();
            if w != draw_size.width as u16 || h != draw_size.height as u16 {
                let dim = (draw_size.width as u16,
                           draw_size.height as u16,
                           dim.2, dim.3);
                let (output_color, output_stencil) = create_main_targets(dim);
                self.output_color = output_color;
                self.output_stencil = output_stencil;
            }
            Some(e)
        } else {
            None
        }
    }
}

impl<W> Window for PistonWindow<W>
    where W: Window
{
    type Event = <W as Window>::Event;

    fn should_close(&self) -> bool { self.window.should_close() }
    fn set_should_close(&mut self, value: bool) {
        self.window.set_should_close(value)
    }
    fn size(&self) -> Size { self.window.size() }
    fn draw_size(&self) -> Size { self.window.draw_size() }
    fn swap_buffers(&mut self) { self.window.swap_buffers() }
    fn poll_event(&mut self) -> Option<Self::Event> {
        Window::poll_event(&mut self.window)
    }
}

impl<W> AdvancedWindow for PistonWindow<W>
    where W: AdvancedWindow
{
    fn get_title(&self) -> String { self.window.get_title() }
    fn set_title(&mut self, title: String) {
        self.window.set_title(title)
    }
    fn get_exit_on_esc(&self) -> bool { self.window.get_exit_on_esc() }
    fn set_exit_on_esc(&mut self, value: bool) {
        self.window.set_exit_on_esc(value)
    }
    fn set_capture_cursor(&mut self, value: bool) {
        self.window.set_capture_cursor(value)
    }
    fn show(&mut self) { self.window.show() }
    fn hide(&mut self) { self.window.hide() }
    fn get_position(&self) -> Option<Position> {
        self.window.get_position()
    }
    fn set_position<P: Into<Position>>(&mut self, pos: P) {
        self.window.set_position(pos)
    }
}

impl<W> EventLoop for PistonWindow<W>
    where W: Window
{
    fn set_ups(&mut self, frames: u64) {
        self.events.set_ups(frames);
    }

    fn set_max_fps(&mut self, frames: u64) {
        self.events.set_max_fps(frames);
    }

    fn set_swap_buffers(&mut self, enable: bool) {
        self.events.set_swap_buffers(enable);
    }

    fn set_bench_mode(&mut self, enable: bool) {
        self.events.set_bench_mode(enable);
    }
}
<commit_msg>Expose window event handling method `event`, to enable alternate event loops<commit_after>#![deny(missing_docs)]

//! The official Piston window wrapper for the Piston game engine
//!
//! The purpose of this library is to provide an easy-to-use,
//! simple-to-get-started and convenient-for-applications API for Piston.
//!
//! Sets up:
//!
//! - [Gfx](https://github.com/gfx-rs/gfx) with an OpenGL back-end.
//! - [gfx_graphics](https://github.com/pistondevelopers/gfx_graphics)
//! for 2D rendering.
//! - [glutin_window](https://github.com/pistondevelopers/glutin_window)
//! as default window back-end, but this can be swapped (see below).
//!
//! ### Example
//!
//! ```no_run
//! extern crate piston_window;
//!
//! use piston_window::*;
//!
//! fn main() {
//!     let mut window: PistonWindow =
//!         WindowSettings::new("Hello World!", [512; 2])
//!             .build().unwrap();
//!     while let Some(e) = window.next() {
//!         window.draw_2d(&e, |c, g| {
//!             clear([0.5, 0.5, 0.5, 1.0], g);
//!             rectangle([1.0, 0.0, 0.0, 1.0], // red
//!                       [0.0, 0.0, 100.0, 100.0], // rectangle
//!                       c.transform, g);
//!         });
//!     }
//! }
//! ```
//!
//! ### Swap to another window back-end
//!
//! Change the generic parameter to the window back-end you want to use.
//!
//! ```ignore
//! extern crate piston_window;
//! extern crate sdl2_window;
//!
//! use piston_window::*;
//! use sdl2_window::Sdl2Window;
//!
//! # fn main() {
//!
//! let window: PistonWindow<Sdl2Window> =
//!     WindowSettings::new("title", [512; 2])
//!         .build().unwrap();
//!
//! # }
//! ```
//!
//! ### sRGB
//!
//! The impl of `BuildFromWindowSettings` in this library turns on
//! `WindowSettings::srgb`, because it is required by gfx_graphics.
//!
//! Most images such as those found on the internet uses sRGB,
//! that has a non-linear gamma corrected space.
//! When rendering 3D, make sure textures and colors are in linear gamma space.
//! Alternative is to use `Srgb8` and `Srgba8` formats for textures.
//!
//! For more information about sRGB, see
//! https://github.com/PistonDevelopers/piston/issues/1014
//!
//! ### Library dependencies
//!
//! This library is meant to be used in applications only.
//! It is not meant to be depended on by generic libraries.
//! Instead, libraries should depend on the lower abstractions,
//! such as the [Piston core](https://github.com/pistondevelopers/piston).

extern crate piston;
extern crate gfx;
extern crate gfx_core;
extern crate gfx_device_gl;
extern crate gfx_graphics;
extern crate graphics;
extern crate shader_version;
extern crate glutin_window;
pub extern crate texture;

use glutin_window::GlutinWindow;
pub use shader_version::OpenGL;
pub use graphics::*;
pub use piston::window::*;
pub use piston::input::*;
pub use piston::event_loop::*;
pub use gfx_graphics::{ GlyphError, Texture, TextureSettings, Flip };

use gfx_graphics::{ Gfx2d, GfxGraphics };

/// Actual gfx::Stream implementation carried by the window.
pub type GfxEncoder = gfx::Encoder<gfx_device_gl::Resources,
    gfx_device_gl::CommandBuffer>;
/// Glyph cache.
pub type Glyphs = gfx_graphics::GlyphCache<gfx_device_gl::Resources,
    gfx_device_gl::Factory>;
/// 2D graphics.
pub type G2d<'a> = GfxGraphics<'a,
    gfx_device_gl::Resources,
    gfx_device_gl::CommandBuffer>;
/// Texture type compatible with `G2d`.
pub type G2dTexture<'a> = Texture<gfx_device_gl::Resources>;

/// Contains everything required for controlling window, graphics, event loop.
pub struct PistonWindow<W: Window = GlutinWindow> {
    /// The window.
    pub window: W,
    /// GFX encoder.
    pub encoder: GfxEncoder,
    /// GFX device.
    pub device: gfx_device_gl::Device,
    /// Output frame buffer.
    pub output_color: gfx::handle::RenderTargetView<
        gfx_device_gl::Resources, gfx::format::Srgba8>,
    /// Output stencil buffer.
    pub output_stencil: gfx::handle::DepthStencilView<
        gfx_device_gl::Resources, gfx::format::DepthStencil>,
    /// Gfx2d.
    pub g2d: Gfx2d<gfx_device_gl::Resources>,
    /// Event loop state.
    pub events: WindowEvents,
    /// The factory that was created along with the device.
    pub factory: gfx_device_gl::Factory,
}

impl<W> BuildFromWindowSettings for PistonWindow<W>
    where W: Window + OpenGLWindow + BuildFromWindowSettings,
          W::Event: GenericEvent
{
    fn build_from_window_settings(settings: &WindowSettings) -> Result<PistonWindow<W>, String> {
        // Turn on sRGB.
        let settings = settings.clone().srgb(true);

        // Use OpenGL 3.2 by default, because this is what window backends
        // usually do.
        let opengl = settings.get_maybe_opengl().unwrap_or(OpenGL::V3_2);
        let samples = settings.get_samples();

        Ok(PistonWindow::new(opengl, samples, try!(settings.build())))
    }
}

fn create_main_targets(dim: gfx::tex::Dimensions) ->
(gfx::handle::RenderTargetView<
    gfx_device_gl::Resources, gfx::format::Srgba8>,
 gfx::handle::DepthStencilView<
    gfx_device_gl::Resources, gfx::format::DepthStencil>) {
    use gfx_core::factory::Typed;
    use gfx::format::{DepthStencil, Format, Formatted, Srgba8};

    let color_format: Format = <Srgba8 as Formatted>::get_format();
    let depth_format: Format = <DepthStencil as Formatted>::get_format();
    let (output_color, output_stencil) =
        gfx_device_gl::create_main_targets_raw(dim,
                                               color_format.0,
                                               depth_format.0);
    let output_color = Typed::new(output_color);
    let output_stencil = Typed::new(output_stencil);
    (output_color, output_stencil)
}

impl<W> PistonWindow<W>
    where W: Window, W::Event: GenericEvent
{
    /// Creates a new piston window.
    pub fn new(opengl: OpenGL, samples: u8, mut window: W) -> Self
        where W: OpenGLWindow
    {
        let (device, mut factory) =
            gfx_device_gl::create(|s|
                window.get_proc_address(s) as *const _);

        let (output_color, output_stencil) = {
            let aa = samples as gfx::tex::NumSamples;
            let draw_size = window.draw_size();
            let dim = (draw_size.width as u16, draw_size.height as u16,
                       1, aa.into());
            create_main_targets(dim)
        };

        let g2d = Gfx2d::new(opengl, &mut factory);
        let encoder = factory.create_command_buffer().into();
        let events = window.events();
        PistonWindow {
            window: window,
            encoder: encoder,
            device: device,
            output_color: output_color,
            output_stencil: output_stencil,
            g2d: g2d,
            events: events,
            factory: factory,
        }
    }

    /// Renders 2D graphics.
    pub fn draw_2d<E, F, U>(&mut self, e: &E, f: F) -> Option<U> where
        W: OpenGLWindow,
        E: GenericEvent,
        F: FnOnce(Context, &mut G2d) -> U
    {
        self.window.make_current();
        if let Some(args) = e.render_args() {
            let res = self.g2d.draw(
                &mut self.encoder,
                &self.output_color,
                &self.output_stencil,
                args.viewport(),
                f
            );
            self.encoder.flush(&mut self.device);
            Some(res)
        } else {
            None
        }
    }

    /// Renders 3D graphics.
    pub fn draw_3d<E, F, U>(&mut self, e: &E, f: F) -> Option<U> where
        W: OpenGLWindow,
        E: GenericEvent,
        F: FnOnce(&mut Self) -> U
    {
        self.window.make_current();
        if let Some(_) = e.render_args() {
            let res = f(self);
            self.encoder.flush(&mut self.device);
            Some(res)
        } else {
            None
        }
    }

    /// Returns next event.
    /// Cleans up after rendering and resizes frame buffers.
    pub fn next(&mut self) -> Option<Event<<W as Window>::Event>> {
        if let Some(e) = self.events.next(&mut self.window) {
            self.event(&e);
            Some(e)
        } else {
            None
        }
    }

    /// Let window handle new event.
    /// Cleans up after rendering and resizes frame buffers.
    pub fn event(&mut self, event: &Event<<W as Window>::Event>) {
        use piston::input::*;
        use gfx_core::factory::Typed;
        use gfx::Device;

        if let Some(_) = event.after_render_args() {
            // After swapping buffers.
            self.device.cleanup();
        }

        // Check whether window has resized and update the output.
        let dim = self.output_color.raw().get_dimensions();
        let (w, h) = (dim.0, dim.1);
        let draw_size = self.window.draw_size();
        if w != draw_size.width as u16 || h != draw_size.height as u16 {
            let dim = (draw_size.width as u16,
                       draw_size.height as u16,
                       dim.2, dim.3);
            let (output_color, output_stencil) = create_main_targets(dim);
            self.output_color = output_color;
            self.output_stencil = output_stencil;
        }
    }
}

impl<W> Window for PistonWindow<W>
    where W: Window
{
    type Event = <W as Window>::Event;

    fn should_close(&self) -> bool { self.window.should_close() }
    fn set_should_close(&mut self, value: bool) {
        self.window.set_should_close(value)
    }
    fn size(&self) -> Size { self.window.size() }
    fn draw_size(&self) -> Size { self.window.draw_size() }
    fn swap_buffers(&mut self) { self.window.swap_buffers() }
    fn poll_event(&mut self) -> Option<Self::Event> {
        Window::poll_event(&mut self.window)
    }
}

impl<W> AdvancedWindow for PistonWindow<W>
    where W: AdvancedWindow
{
    fn get_title(&self) -> String { self.window.get_title() }
    fn set_title(&mut self, title: String) {
        self.window.set_title(title)
    }
    fn get_exit_on_esc(&self) -> bool { self.window.get_exit_on_esc() }
    fn set_exit_on_esc(&mut self, value: bool) {
        self.window.set_exit_on_esc(value)
    }
    fn set_capture_cursor(&mut self, value: bool) {
        self.window.set_capture_cursor(value)
    }
    fn show(&mut self) { self.window.show() }
    fn hide(&mut self) { self.window.hide() }
    fn get_position(&self) -> Option<Position> {
        self.window.get_position()
    }
    fn set_position<P: Into<Position>>(&mut self, pos: P) {
        self.window.set_position(pos)
    }
}

impl<W> EventLoop for PistonWindow<W>
    where W: Window
{
    fn set_ups(&mut self, frames: u64) {
        self.events.set_ups(frames);
    }

    fn set_max_fps(&mut self, frames: u64) {
        self.events.set_max_fps(frames);
    }

    fn set_swap_buffers(&mut self, enable: bool) {
        self.events.set_swap_buffers(enable);
    }

    fn set_bench_mode(&mut self, enable: bool) {
        self.events.set_bench_mode(enable);
    }
}
<|endoftext|>


<commit_before>//! Replaces the deprecated functionality of std::os::num_cpus.
#![cfg_attr(test, deny(warnings))]
#![deny(missing_docs)]

extern crate libc;

/// Returns the number of CPUs of the current machine.
#[inline]
pub fn get() -> usize {
    get_num_cpus()
}

#[cfg(windows)]
fn get_num_cpus() -> usize {
    unsafe {
        let mut sysinfo: libc::SYSTEM_INFO = ::std::mem::uninitialized();
        libc::GetSystemInfo(&mut sysinfo);
        sysinfo.dwNumberOfProcessors as usize
    }
}

#[cfg(
    any(
        target_os = "freebsd",
        target_os = "dragonfly",
        target_os = "bitrig",
        target_os = "openbsd"
    )
)]
fn get_num_cpus() -> usize {
    use libc::{c_int, c_uint};
    use libc::funcs::bsd44::sysctl;

    //XXX: uplift to libc?
    const CTL_HW: c_int = 6;
    const HW_AVAILCPU: c_int = 25;
    const HW_NCPU: c_int = 3;

    let mut cpus: c_uint = 0;
    let mut CPUS_SIZE = ::std::mem::size_of::<c_uint>();
    let mut mib: [c_int; 4] = [CTL_HW, HW_AVAILCPU, 0, 0];

    unsafe {
        sysctl(mib.as_mut_ptr(), 2, &mut cpus, &mut CPUS_SIZE, &mut (), 0);
    }

    if cpus < 1 {
        mib[1] = HW_NCPU;
        unsafe {
            sysctl(mib.as_mut_ptr(), 2, &mut cpus, &mut CPUS_SIZE, &mut (), 0);
        }
        if cpus < 1 {
            cpus = 1;
        }
    }

    cpus as usize
}

#[cfg(
    any(
        target_os = "linux",
        target_os = "nacl",
        target_os = "macos",
        target_os = "ios"
    )
)]
fn get_num_cpus() -> usize {
    unsafe {
        libc::sysconf(libc::_SC_NPROCESSORS_ONLN) as usize
    }
}

#[cfg(target_os= "android")]
fn get_num_cpus() -> usize {
    //to-do: replace with libc::_SC_NPROCESSORS_ONLN once available
    unsafe {
        libc::sysconf(97) as usize
    }
}

#[test]
fn lower_bound() {
    assert!(get() > 0);
}

#[test]
fn upper_bound() {
    assert!(get() < 236_451);
}
<commit_msg>correct mismatched types on sysctl<commit_after>//! Replaces the deprecated functionality of std::os::num_cpus.
#![cfg_attr(test, deny(warnings))]
#![deny(missing_docs)]

extern crate libc;

/// Returns the number of CPUs of the current machine.
#[inline]
pub fn get() -> usize {
    get_num_cpus()
}

#[cfg(windows)]
fn get_num_cpus() -> usize {
    unsafe {
        let mut sysinfo: libc::SYSTEM_INFO = ::std::mem::uninitialized();
        libc::GetSystemInfo(&mut sysinfo);
        sysinfo.dwNumberOfProcessors as usize
    }
}

#[cfg(
    any(
        target_os = "freebsd",
        target_os = "dragonfly",
        target_os = "bitrig",
        target_os = "openbsd"
    )
)]
fn get_num_cpus() -> usize {
    use libc::{c_int, c_uint};
    use libc::funcs::bsd44::sysctl;
    use std::ptr;

    //XXX: uplift to libc?
    const CTL_HW: c_int = 6;
    const HW_AVAILCPU: c_int = 25;
    const HW_NCPU: c_int = 3;

    let mut cpus: c_uint = 0;
    let mut CPUS_SIZE = ::std::mem::size_of::<c_uint>();
    let mut mib: [c_int; 4] = [CTL_HW, HW_AVAILCPU, 0, 0];

    unsafe {
        sysctl(mib.as_mut_ptr(), 2,
               &mut cpus as *mut _ as *mut _, &mut CPUS_SIZE as *mut _ as *mut _,
               ptr::null_mut(), 0);
    }

    if cpus < 1 {
        mib[1] = HW_NCPU;
        unsafe {
            sysctl(mib.as_mut_ptr(), 2,
                   &mut cpus as *mut _ as *mut _, &mut CPUS_SIZE as *mut _ as *mut _,
                   ptr::null_mut(), 0);
        }
        if cpus < 1 {
            cpus = 1;
        }
    }

    cpus as usize
}

#[cfg(
    any(
        target_os = "linux",
        target_os = "nacl",
        target_os = "macos",
        target_os = "ios"
    )
)]
fn get_num_cpus() -> usize {
    unsafe {
        libc::sysconf(libc::_SC_NPROCESSORS_ONLN) as usize
    }
}

#[cfg(target_os= "android")]
fn get_num_cpus() -> usize {
    //to-do: replace with libc::_SC_NPROCESSORS_ONLN once available
    unsafe {
        libc::sysconf(97) as usize
    }
}

#[test]
fn lower_bound() {
    assert!(get() > 0);
}

#[test]
fn upper_bound() {
    assert!(get() < 236_451);
}
<|endoftext|>


<commit_before>//! Modbus implementation in pure Rust.
//!
//! # Examples
//!
//! ```
//! # extern crate modbus;
//! # extern crate test_server;
//! # use test_server::start_dummy_server;
//! # fn main() {
//! use modbus::{Client, Coil};
//! use modbus::tcp;
//! # if cfg!(feature = "modbus-server-tests") {
//! # let (_s, port) = start_dummy_server(Some(22221));
//!
//! let mut cfg = tcp::Config::default();
//! # cfg.tcp_port = port;
//! let mut client = tcp::Transport::new_with_cfg("127.0.0.1", cfg).unwrap();
//! assert!(client.write_single_coil(0, Coil::On).is_ok());
//! # }
//! # }
//! ```

#[macro_use]
extern crate enum_primitive;
extern crate num;
extern crate rustc_serialize;
extern crate bincode;
extern crate byteorder;

use std::io;
use std::str::FromStr;
use bincode::rustc_serialize as bcs;

mod binary;
mod client;

pub mod scoped;

/// The Modbus TCP backend implements a Modbus variant used for communication over TCP/IPv4 networks.
pub mod tcp;
pub use tcp::Transport;
pub use tcp::Config;
pub use client::Client;

type Address = u16;
type Quantity = u16;
type Value = u16;

enum Function<'a> {
    ReadCoils(Address, Quantity),
    ReadDiscreteInputs(Address, Quantity),
    ReadHoldingRegisters(Address, Quantity),
    ReadInputRegisters(Address, Quantity),
    WriteSingleCoil(Address, Value),
    WriteSingleRegister(Address, Value),
    WriteMultipleCoils(Address, Quantity, &'a [u8]),
    WriteMultipleRegisters(Address, Quantity, &'a [u8]),
}

impl<'a> Function<'a> {
    fn code(&self) -> u8 {
        match *self {
            Function::ReadCoils(_, _) => 0x01,
            Function::ReadDiscreteInputs(_, _) => 0x02,
            Function::ReadHoldingRegisters(_, _) => 0x03,
            Function::ReadInputRegisters(_, _) => 0x04,
            Function::WriteSingleCoil(_, _) => 0x05,
            Function::WriteSingleRegister(_, _) => 0x06,
            Function::WriteMultipleCoils(_, _, _) => 0x0f,
            Function::WriteMultipleRegisters(_, _, _) => 0x10,
        }
        // ReadExceptionStatus     = 0x07,
        // ReportSlaveId           = 0x11,
        // MaskWriteRegister       = 0x16,
        // WriteAndReadRegisters   = 0x17
    }
}

enum_from_primitive! {
#[derive(Debug, PartialEq)]
/// Modbus exception codes returned from the server.
pub enum ExceptionCode {
    IllegalFunction         = 0x01,
    IllegalDataAddress      = 0x02,
    IllegalDataValue        = 0x03,
    SlaveOrServerFailure    = 0x04,
    Acknowledge             = 0x05,
    SlaveOrServerBusy       = 0x06,
    NegativeAcknowledge     = 0x07,
    MemoryParity            = 0x08,
    NotDefined              = 0x09,
    GatewayPath             = 0x0a,
    GatewayTarget           = 0x0b
}
}

/// `InvalidData` reasons
#[derive(Debug)]
pub enum Reason {
    UnexpectedReplySize,
    BytecountNotEven,
    SendBufferEmpty,
    RecvBufferEmpty,
    SendBufferTooBig,
    DecodingError,
    EncodingError,
    InvalidByteorder,
}

/// Combination of Modbus, IO and data corruption errors
#[derive(Debug)]
pub enum Error {
    Exception(ExceptionCode),
    Io(io::Error),
    InvalidResponse,
    InvalidData(Reason),
    InvalidFunction,
    ParseCoilError,
}

impl From<ExceptionCode> for Error {
    fn from(err: ExceptionCode) -> Error {
        Error::Exception(err)
    }
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Error {
        Error::Io(err)
    }
}

impl From<bcs::DecodingError> for Error {
    fn from(_err: bcs::DecodingError) -> Error {
        Error::InvalidData(Reason::DecodingError)
    }
}

impl From<bcs::EncodingError> for Error {
    fn from(_err: bcs::EncodingError) -> Error {
        Error::InvalidData(Reason::EncodingError)
    }
}

/// Result type used to nofify success or failure in communication
pub type Result<T> = std::result::Result<T, Error>;

/// Single bit status values, used in read or write coil functions
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Coil {
    On,
    Off,
}

impl Coil {
    fn code(&self) -> u16 {
        match *self {
            Coil::On => 0xff00,
            Coil::Off => 0x0000,
        }
    }
}

impl FromStr for Coil {
    type Err = Error;
    fn from_str(s: &str) -> Result<Coil> {
        if s == "On" {
            Ok(Coil::On)
        } else if s == "Off" {
            Ok(Coil::Off)
        } else {
            Err(Error::ParseCoilError)
        }
    }
}
<commit_msg>Make binary available for the Modbus server<commit_after>//! Modbus implementation in pure Rust.
//!
//! # Examples
//!
//! ```
//! # extern crate modbus;
//! # extern crate test_server;
//! # use test_server::start_dummy_server;
//! # fn main() {
//! use modbus::{Client, Coil};
//! use modbus::tcp;
//! # if cfg!(feature = "modbus-server-tests") {
//! # let (_s, port) = start_dummy_server(Some(22221));
//!
//! let mut cfg = tcp::Config::default();
//! # cfg.tcp_port = port;
//! let mut client = tcp::Transport::new_with_cfg("127.0.0.1", cfg).unwrap();
//! assert!(client.write_single_coil(0, Coil::On).is_ok());
//! # }
//! # }
//! ```

#[macro_use]
extern crate enum_primitive;
extern crate num;
extern crate rustc_serialize;
extern crate bincode;
extern crate byteorder;

use std::io;
use std::str::FromStr;
use bincode::rustc_serialize as bcs;

pub mod binary;
mod client;

pub mod scoped;

/// The Modbus TCP backend implements a Modbus variant used for communication over TCP/IPv4 networks.
pub mod tcp;
pub use tcp::Transport;
pub use tcp::Config;
pub use client::Client;

type Address = u16;
type Quantity = u16;
type Value = u16;

enum Function<'a> {
    ReadCoils(Address, Quantity),
    ReadDiscreteInputs(Address, Quantity),
    ReadHoldingRegisters(Address, Quantity),
    ReadInputRegisters(Address, Quantity),
    WriteSingleCoil(Address, Value),
    WriteSingleRegister(Address, Value),
    WriteMultipleCoils(Address, Quantity, &'a [u8]),
    WriteMultipleRegisters(Address, Quantity, &'a [u8]),
}

impl<'a> Function<'a> {
    fn code(&self) -> u8 {
        match *self {
            Function::ReadCoils(_, _) => 0x01,
            Function::ReadDiscreteInputs(_, _) => 0x02,
            Function::ReadHoldingRegisters(_, _) => 0x03,
            Function::ReadInputRegisters(_, _) => 0x04,
            Function::WriteSingleCoil(_, _) => 0x05,
            Function::WriteSingleRegister(_, _) => 0x06,
            Function::WriteMultipleCoils(_, _, _) => 0x0f,
            Function::WriteMultipleRegisters(_, _, _) => 0x10,
        }
        // ReadExceptionStatus     = 0x07,
        // ReportSlaveId           = 0x11,
        // MaskWriteRegister       = 0x16,
        // WriteAndReadRegisters   = 0x17
    }
}

enum_from_primitive! {
#[derive(Debug, PartialEq)]
/// Modbus exception codes returned from the server.
pub enum ExceptionCode {
    IllegalFunction         = 0x01,
    IllegalDataAddress      = 0x02,
    IllegalDataValue        = 0x03,
    SlaveOrServerFailure    = 0x04,
    Acknowledge             = 0x05,
    SlaveOrServerBusy       = 0x06,
    NegativeAcknowledge     = 0x07,
    MemoryParity            = 0x08,
    NotDefined              = 0x09,
    GatewayPath             = 0x0a,
    GatewayTarget           = 0x0b
}
}

/// `InvalidData` reasons
#[derive(Debug)]
pub enum Reason {
    UnexpectedReplySize,
    BytecountNotEven,
    SendBufferEmpty,
    RecvBufferEmpty,
    SendBufferTooBig,
    DecodingError,
    EncodingError,
    InvalidByteorder,
}

/// Combination of Modbus, IO and data corruption errors
#[derive(Debug)]
pub enum Error {
    Exception(ExceptionCode),
    Io(io::Error),
    InvalidResponse,
    InvalidData(Reason),
    InvalidFunction,
    ParseCoilError,
}

impl From<ExceptionCode> for Error {
    fn from(err: ExceptionCode) -> Error {
        Error::Exception(err)
    }
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Error {
        Error::Io(err)
    }
}

impl From<bcs::DecodingError> for Error {
    fn from(_err: bcs::DecodingError) -> Error {
        Error::InvalidData(Reason::DecodingError)
    }
}

impl From<bcs::EncodingError> for Error {
    fn from(_err: bcs::EncodingError) -> Error {
        Error::InvalidData(Reason::EncodingError)
    }
}

/// Result type used to nofify success or failure in communication
pub type Result<T> = std::result::Result<T, Error>;

/// Single bit status values, used in read or write coil functions
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Coil {
    On,
    Off,
}

impl Coil {
    fn code(&self) -> u16 {
        match *self {
            Coil::On => 0xff00,
            Coil::Off => 0x0000,
        }
    }
}

impl FromStr for Coil {
    type Err = Error;
    fn from_str(s: &str) -> Result<Coil> {
        if s == "On" {
            Ok(Coil::On)
        } else if s == "Off" {
            Ok(Coil::Off)
        } else {
            Err(Error::ParseCoilError)
        }
    }
}
<|endoftext|>


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

use std::collections::{BTreeMap, HashMap, HashSet};
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 {
    fn hash<H>(&self, state: &mut H) where H: Hasher {
        state.write(self.id.as_bytes());
    }
}

impl PartialEq<Document> for Document {
    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),
}

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)
    }
}

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)
}

/// 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, search_result| {
                map.entry(search_result.doc)
                    .or_insert(Vec::new())
                    .push(search_result.highlighted);
                map
            });
        let mut results: Vec<_> = map.into_iter()
            .map(SearchResult::new_)
            .collect();
        results.sort_by(|result1, result2| result2.score.partial_cmp(&result1.score).unwrap());
        results
    }
}

#[derive(Clone, Debug, RustcEncodable)]
pub struct SearchResult {
    doc: Arc<Document>,
    highlights: Vec<(usize, usize)>,
    highlighted: String,
    score: f32,
}

impl PartialEq for SearchResult {
    fn eq(&self, other: &SearchResult) -> bool {
        self.doc == other.doc && self.highlights == other.highlights
    }
}

impl Eq for SearchResult {}

impl SearchResult {
    pub fn doc(&self) -> &Arc<Document> {
        &self.doc
    }

    pub fn highlights(&self) -> &Vec<(usize, usize)> {
        &self.highlights
    }

    pub fn highlighted(&self) -> &String {
        &self.highlighted
    }

    pub fn score(&self) -> f32 {
        self.score
    }

    fn new_((doc, highlights): (Arc<Document>, Vec<(usize, usize)>)) -> SearchResult {
        SearchResult::new(doc, highlights)
    }

    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,
            highlighted: SearchResult::highlighted_content(&doc.content, &mut highlights),
            doc: doc,
            highlights: highlights,
        }
    }

    fn highlighted_content(content: &str, highlights: &mut [(usize, usize)]) -> String {
        highlights.sort();
        let mut begin_idx = 0;
        let mut parts = vec![];
        for &mut (begin, end) in highlights {
            parts.push(&content[begin_idx..begin]);
            parts.push("");
            parts.push(&content[begin..end]);
            parts.push("");
            begin_idx = end;
        }
        parts.push(&content[begin_idx..]);
        parts.into_iter().join("")
    }
}

#[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].highlighted);
}

#[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].highlighted);

}

#[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!(expected[0].highlighted, "what to do today");
}

#[test]
fn test_unicode() {
    let mut index = InvertedIndex::new();
    let doc = Document::new("0", "abc åäö");
    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);
}

<commit_msg>impl Hash for SearchResult<commit_after>#![feature(collections, unboxed_closures, core)]
extern crate itertools;
extern crate rustc_serialize;

use std::collections::{BTreeMap, HashMap, HashSet};
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 {
    fn hash<H>(&self, state: &mut H) where H: Hasher {
        self.id.hash(state);
    }
}

impl PartialEq<Document> for Document {
    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),
}

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)
    }
}

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)
}

/// 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, search_result| {
                map.entry(search_result.doc)
                    .or_insert(Vec::new())
                    .push(search_result.highlighted);
                map
            });
        let mut results: Vec<_> = map.into_iter()
            .map(SearchResult::new_)
            .collect();
        results.sort_by(|result1, result2| result2.score.partial_cmp(&result1.score).unwrap());
        results
    }
}

#[derive(Clone, Debug, RustcEncodable)]
pub struct SearchResult {
    doc: Arc<Document>,
    highlights: Vec<(usize, usize)>,
    highlighted: String,
    score: f32,
}

impl PartialEq for SearchResult {
    fn eq(&self, other: &SearchResult) -> bool {
        self.doc == other.doc && self.highlights == other.highlights
    }
}

impl Eq for SearchResult {}

impl Hash for SearchResult {
    fn hash<H>(&self, state: &mut H) where H: Hasher {
        self.doc.hash(state);
        self.highlights.hash(state);
    }
}

impl SearchResult {
    pub fn doc(&self) -> &Arc<Document> {
        &self.doc
    }

    pub fn highlights(&self) -> &Vec<(usize, usize)> {
        &self.highlights
    }

    pub fn highlighted(&self) -> &String {
        &self.highlighted
    }

    pub fn score(&self) -> f32 {
        self.score
    }

    fn new_((doc, highlights): (Arc<Document>, Vec<(usize, usize)>)) -> SearchResult {
        SearchResult::new(doc, highlights)
    }

    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,
            highlighted: SearchResult::highlighted_content(&doc.content, &mut highlights),
            doc: doc,
            highlights: highlights,
        }
    }

    fn highlighted_content(content: &str, highlights: &mut [(usize, usize)]) -> String {
        highlights.sort();
        let mut begin_idx = 0;
        let mut parts = vec![];
        for &mut (begin, end) in highlights {
            parts.push(&content[begin_idx..begin]);
            parts.push("");
            parts.push(&content[begin..end]);
            parts.push("");
            begin_idx = end;
        }
        parts.push(&content[begin_idx..]);
        parts.into_iter().join("")
    }
}

#[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].highlighted);
}

#[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].highlighted);

}

#[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!(expected[0].highlighted, "what to do today");
}

#[test]
fn test_unicode() {
    let mut index = InvertedIndex::new();
    let doc = Document::new("0", "abc åäö");
    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);
}

<|endoftext|>


<commit_before>// The MIT License (MIT)
// Copyright (c) 2016 font-loader Developers
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
// associated documentation files (the "Software"), to deal in the Software without restriction,
// including without limitation the rights to use, copy, modify, merge, publish, distribute,
// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

//! # Font-Loader
//! A font loading utility written in and for rust.
//!
//! Currently supported platforms:
//!
//! * Windows
//! * Mac
//! * *nix systems
//!
//! # Usage
//! ## Linux, Unix:
//! Fontconfig is required on Linux and Unix, as it is the default Fontmanagement utility on these
//! platforms.
//!
//! ```
//! sudo apt-get install libfontconfig libfontconfig1-dev
//! ```
//!
//! # Example
//! ## Cargo.toml
//! ```toml
//! [dependencies]
//! font-loader = "https://github.com/MSleepyPanda/rust-font-loader/"
//! ```
//!
//! ## main.rs:
//! ```rust
//! extern crate font_loader as fonts;
//!
//! use fonts::system_fonts;
//!
//! fn main() {
//! 	// Enumerate all fonts
//!     let sysfonts = system_fonts::query_all();
//!     for string in &sysfonts {
//!         println!("{}", string);
//!     }
//!
//! 	let mut property = system_fonts::FontPropertyBuilder::new().monospace().build();
//! 	let sysfonts = system_fonts::query_specific(&mut property);
//! 	for string in &sysfonts {
//! 		println!("Monospaced font: {}", string);
//! 	}
//!
//! 	let property = system_fonts::FontPropertyBuilder::new().family("Arial").build();
//! 	let (font, _) = system_fonts::get(&property).unwrap();
//! 	println!("{:?}", &font[..50]);
//! }
//! ```

extern crate libc;

#[cfg(target_os = "windows")]
extern crate winapi;
#[cfg(target_os = "windows")]
extern crate gdi32;
#[cfg(target_os = "windows")]
extern crate user32;

#[cfg(target_os = "windows")]
mod win32;
#[cfg(target_os = "windows")]
pub use win32::*;

#[cfg(target_os = "macos")]
extern crate core_text;
#[cfg(target_os = "macos")]
extern crate core_foundation;
#[cfg(target_os = "macos")]
mod macos;
#[cfg(target_os = "macos")]
pub use macos::*;

#[cfg(all(unix, not(target_os = "macos")))]
extern crate fontconfig_sys;
#[cfg(all(unix, not(target_os = "macos")))]
mod fontconfig;
#[cfg(all(unix, not(target_os = "macos")))]
pub use fontconfig::*;
<commit_msg>Flag shell command as not being rust doctests.<commit_after>// The MIT License (MIT)
// Copyright (c) 2016 font-loader Developers
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
// associated documentation files (the "Software"), to deal in the Software without restriction,
// including without limitation the rights to use, copy, modify, merge, publish, distribute,
// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

//! # Font-Loader
//! A font loading utility written in and for rust.
//!
//! Currently supported platforms:
//!
//! * Windows
//! * Mac
//! * *nix systems
//!
//! # Usage
//! ## Linux, Unix:
//! Fontconfig is required on Linux and Unix, as it is the default Fontmanagement utility on these
//! platforms.
//!
//! ```shell
//! sudo apt-get install libfontconfig libfontconfig1-dev
//! ```
//!
//! # Example
//! ## Cargo.toml
//! ```toml
//! [dependencies]
//! font-loader = "https://github.com/MSleepyPanda/rust-font-loader/"
//! ```
//!
//! ## main.rs:
//! ```rust
//! extern crate font_loader as fonts;
//!
//! use fonts::system_fonts;
//!
//! fn main() {
//! 	// Enumerate all fonts
//!     let sysfonts = system_fonts::query_all();
//!     for string in &sysfonts {
//!         println!("{}", string);
//!     }
//!
//! 	let mut property = system_fonts::FontPropertyBuilder::new().monospace().build();
//! 	let sysfonts = system_fonts::query_specific(&mut property);
//! 	for string in &sysfonts {
//! 		println!("Monospaced font: {}", string);
//! 	}
//!
//! 	let property = system_fonts::FontPropertyBuilder::new().family("Arial").build();
//! 	let (font, _) = system_fonts::get(&property).unwrap();
//! 	println!("{:?}", &font[..50]);
//! }
//! ```

extern crate libc;

#[cfg(target_os = "windows")]
extern crate winapi;
#[cfg(target_os = "windows")]
extern crate gdi32;
#[cfg(target_os = "windows")]
extern crate user32;

#[cfg(target_os = "windows")]
mod win32;
#[cfg(target_os = "windows")]
pub use win32::*;

#[cfg(target_os = "macos")]
extern crate core_text;
#[cfg(target_os = "macos")]
extern crate core_foundation;
#[cfg(target_os = "macos")]
mod macos;
#[cfg(target_os = "macos")]
pub use macos::*;

#[cfg(all(unix, not(target_os = "macos")))]
extern crate fontconfig_sys;
#[cfg(all(unix, not(target_os = "macos")))]
mod fontconfig;
#[cfg(all(unix, not(target_os = "macos")))]
pub use fontconfig::*;
<|endoftext|>


<commit_before>#![crate_name = "graphics"]
#![deny(missing_doc)]
#![feature(default_type_params)]
#![feature(globs)]

//! A library for 2D graphics that works with multiple back-ends.

extern crate vecmath_lib = "vecmath";

pub use add::{
    AddBevel,
    AddBevelBorder,
    AddBorder,
    AddColor,
    AddEllipse,
    AddImage,
    AddLine,
    AddPolygon,
    AddPolygons,
    AddRectangle,
    AddRound,
    AddRoundBorder,
    AddSquareBorder,
    AddTween,
};
pub use back_end::BackEnd;
pub use draw::Draw;
pub use image_size::ImageSize;
pub use relative::{
    RelativeColor,
    RelativeRectangle,
    RelativeSourceRectangle,
    RelativeTransform2d,
};
pub use view::View;

pub use context::Context as Context;

use internal::{
    Color,
};

mod add;
mod back_end;
mod context;
mod draw;
mod image_size;
mod relative;
mod view;

pub mod internal;
pub mod interpolation;
pub mod modular_index;
pub mod triangulation;
pub mod vecmath;

mod shape;

pub type BevelBorderLineContext
    = context::Context<shape::BevelBorderLineShape>;
pub type BevelBorderLineColorContext
    = context::Context<shape::BevelBorderLineShape, Color>;
pub type BevelRectangleContext
    = context::Context<shape::BevelRectangleShape>;
pub type BevelRectangleColorContext
    = context::Context<shape::BevelRectangleShape, Color>;
pub type BevelRectangleBorderContext
    = context::Context<shape::BevelRectangleBorderShape>;
pub type BevelRectangleBorderColorContext
    = context::Context<shape::BevelRectangleBorderShape, Color>;
pub type ColorContext
    = context::Context<(), Color>;
pub type EllipseContext
    = context::Context<shape::EllipseShape>;
pub type EllipseBorderContext
    = context::Context<shape::EllipseBorderShape>;
pub type EllipseColorContext
    = context::Context<shape::EllipseShape, Color>;
pub type EllipseBorderColorContext
    = context::Context<shape::EllipseBorderShape, Color>;
pub type LerpTweenContext
    = context::Context<shape::LerpTweenShape>;
pub type LerpTweenColorContext
    = context::Context<shape::LerpTweenShape, Color>;
pub type LerpTweenPolygonsContext<'a>
    = context::Context<shape::LerpTweenPolygonsShape<'a>>;
pub type LerpTweenPolygonsColorContext<'a>
    = context::Context<shape::LerpTweenPolygonsShape<'a>, Color>;
pub type LineContext
    = context::Context<shape::LineShape>;
pub type LineColorContext
    = context::Context<shape::LineShape, Color>;
pub type ImageContext<'a, I>
    = context::Context<shape::ImageShape<'a, I>>;
pub type ImageColorContext<'a, I>
    = context::Context<shape::ImageShape<'a, I>, Color>;
pub type ImageRectangleContext<'a, I>
    = context::Context<shape::ImageRectangleShape<'a, I>>;
pub type ImageRectangleColorContext<'a, I>
    = context::Context<shape::ImageRectangleShape<'a, I>, Color>;
pub type PolygonContext<'a>
    = context::Context<shape::PolygonShape<'a>>;
pub type PolygonColorContext<'a>
    = context::Context<shape::PolygonShape<'a>, Color>;
pub type RectangleContext
    = context::Context<shape::RectangleShape>;
pub type RectangleBorderContext
    = context::Context<shape::RectangleBorderShape>;
pub type RectangleColorContext
    = context::Context<shape::RectangleShape, Color>;
pub type RectangleBorderColorContext
    = context::Context<shape::RectangleBorderShape, Color>;
pub type RoundBorderLineContext
    = context::Context<shape::RoundBorderLineShape>;
pub type RoundBorderLineColorContext
    = context::Context<shape::RoundBorderLineShape, Color>;
pub type RoundRectangleContext
    = context::Context<shape::RoundRectangleShape>;
pub type RoundRectangleColorContext
    = context::Context<shape::RoundRectangleShape, Color>;
pub type RoundRectangleBorderContext
    = context::Context<shape::RoundRectangleBorderShape>;
pub type RoundRectangleBorderColorContext
    = context::Context<shape::RoundRectangleBorderShape, Color>;
pub type SquareBorderLineContext
    = context::Context<shape::SquareBorderLineShape>;
pub type SquareBorderLineColorContext
    = context::Context<shape::SquareBorderLineShape, Color>;

<commit_msg>Added max vertex constant<commit_after>#![crate_name = "graphics"]
#![deny(missing_doc)]
#![feature(default_type_params)]
#![feature(globs)]

//! A library for 2D graphics that works with multiple back-ends.

extern crate vecmath_lib = "vecmath";

pub use add::{
    AddBevel,
    AddBevelBorder,
    AddBorder,
    AddColor,
    AddEllipse,
    AddImage,
    AddLine,
    AddPolygon,
    AddPolygons,
    AddRectangle,
    AddRound,
    AddRoundBorder,
    AddSquareBorder,
    AddTween,
};
pub use back_end::BackEnd;
pub use draw::Draw;
pub use image_size::ImageSize;
pub use relative::{
    RelativeColor,
    RelativeRectangle,
    RelativeSourceRectangle,
    RelativeTransform2d,
};
pub use view::View;

pub use context::Context as Context;

use internal::{
    Color,
};

/// Any triangulation method called on the back-end
/// never exceeds this number of vertices.
/// This can be used to initialize buffers that fit the chunk size.
pub static BACK_END_MAX_VERTEX_COUNT: uint = 1024;

mod add;
mod back_end;
mod context;
mod draw;
mod image_size;
mod relative;
mod view;

pub mod internal;
pub mod interpolation;
pub mod modular_index;
pub mod triangulation;
pub mod vecmath;

mod shape;

pub type BevelBorderLineContext
    = context::Context<shape::BevelBorderLineShape>;
pub type BevelBorderLineColorContext
    = context::Context<shape::BevelBorderLineShape, Color>;
pub type BevelRectangleContext
    = context::Context<shape::BevelRectangleShape>;
pub type BevelRectangleColorContext
    = context::Context<shape::BevelRectangleShape, Color>;
pub type BevelRectangleBorderContext
    = context::Context<shape::BevelRectangleBorderShape>;
pub type BevelRectangleBorderColorContext
    = context::Context<shape::BevelRectangleBorderShape, Color>;
pub type ColorContext
    = context::Context<(), Color>;
pub type EllipseContext
    = context::Context<shape::EllipseShape>;
pub type EllipseBorderContext
    = context::Context<shape::EllipseBorderShape>;
pub type EllipseColorContext
    = context::Context<shape::EllipseShape, Color>;
pub type EllipseBorderColorContext
    = context::Context<shape::EllipseBorderShape, Color>;
pub type LerpTweenContext
    = context::Context<shape::LerpTweenShape>;
pub type LerpTweenColorContext
    = context::Context<shape::LerpTweenShape, Color>;
pub type LerpTweenPolygonsContext<'a>
    = context::Context<shape::LerpTweenPolygonsShape<'a>>;
pub type LerpTweenPolygonsColorContext<'a>
    = context::Context<shape::LerpTweenPolygonsShape<'a>, Color>;
pub type LineContext
    = context::Context<shape::LineShape>;
pub type LineColorContext
    = context::Context<shape::LineShape, Color>;
pub type ImageContext<'a, I>
    = context::Context<shape::ImageShape<'a, I>>;
pub type ImageColorContext<'a, I>
    = context::Context<shape::ImageShape<'a, I>, Color>;
pub type ImageRectangleContext<'a, I>
    = context::Context<shape::ImageRectangleShape<'a, I>>;
pub type ImageRectangleColorContext<'a, I>
    = context::Context<shape::ImageRectangleShape<'a, I>, Color>;
pub type PolygonContext<'a>
    = context::Context<shape::PolygonShape<'a>>;
pub type PolygonColorContext<'a>
    = context::Context<shape::PolygonShape<'a>, Color>;
pub type RectangleContext
    = context::Context<shape::RectangleShape>;
pub type RectangleBorderContext
    = context::Context<shape::RectangleBorderShape>;
pub type RectangleColorContext
    = context::Context<shape::RectangleShape, Color>;
pub type RectangleBorderColorContext
    = context::Context<shape::RectangleBorderShape, Color>;
pub type RoundBorderLineContext
    = context::Context<shape::RoundBorderLineShape>;
pub type RoundBorderLineColorContext
    = context::Context<shape::RoundBorderLineShape, Color>;
pub type RoundRectangleContext
    = context::Context<shape::RoundRectangleShape>;
pub type RoundRectangleColorContext
    = context::Context<shape::RoundRectangleShape, Color>;
pub type RoundRectangleBorderContext
    = context::Context<shape::RoundRectangleBorderShape>;
pub type RoundRectangleBorderColorContext
    = context::Context<shape::RoundRectangleBorderShape, Color>;
pub type SquareBorderLineContext
    = context::Context<shape::SquareBorderLineShape>;
pub type SquareBorderLineColorContext
    = context::Context<shape::SquareBorderLineShape, Color>;

<|endoftext|>