<commit_before>#![doc(html_root_url = "https://urschrei.github.io/polyline-ffi/")]
//! This module exposes functions for accessing the Polyline encoding and decoding functions via FFI
extern crate polyline;
use polyline::{encode_coordinates, decode_polyline};
use std::mem;
use std::slice;
use std::f64;
use std::ffi::{CStr, CString};

extern crate libc;
use self::libc::{c_char, c_void, uint32_t, size_t};

#[repr(C)]
pub struct Array {
    pub data: *const c_void,
    pub len: size_t,
}

// Build an Array from &[[f64; 2]], so it can be leaked across the FFI boundary
impl From<Vec<[f64; 2]>> for Array {
    fn from(sl: Vec<[f64; 2]>) -> Self {
        let array = Array {
            data: sl.as_ptr() as *const c_void,
            len: sl.len() as size_t,
        };
        mem::forget(sl);
        array
    }
}

// Build &[[f64; 2]] from an Array, so it can be dropped
impl From<Array> for Vec<[f64; 2]> {
    fn from(arr: Array) -> Self {
        unsafe { slice::from_raw_parts(arr.data as *mut [f64; 2], arr.len).to_vec() }
    }
}

// Decode a Polyline into an Array
fn arr_from_string(incoming: String, precision: uint32_t) -> Array {
    let result: Array = match decode_polyline(incoming, precision) {
        Ok(res) => res.into(),
        // should be easy to check for
        Err(_) => vec![[f64::NAN, f64::NAN]].into(),
    };
    result.into()
}

// Decode an Array into a Polyline
fn string_from_arr(incoming: Array, precision: uint32_t) -> String {
    let inc: Vec<_> = incoming.into();
    match encode_coordinates(&inc, precision) {
        Ok(res) => res,
        // we don't need to adapt the error
        Err(res) => res,
    }
}

/// Convert a Polyline into an array of coordinates
///
/// Callers must pass two arguments:
///
/// - a pointer to `NUL`-terminated characters (`char*`)
/// - an unsigned 32-bit `int` for precision (5 for Google Polylines, 6 for
/// OSRM and Valhalla Polylines)
///
/// # Examples
///
/// ```
/// use std::ffi::CString;
/// use std::slice;
/// let input = CString::new("_ibE_seK_seK_seK").unwrap().as_ptr();
/// let result: Array = decode_polyline_ffi(input, 5);
/// let slice = unsafe { slice::from_raw_parts(result.data as *const [f64; 2], result.len) };
/// assert_eq!(slice, [[1.0, 2.0], [3.0, 4.0]]);
/// drop_float_array(result);
/// ```
///
/// A decoding failure will always return an array: `[[NaN, NaN]]`
///
/// Implementations calling this function **must** call [`drop_float_array`](fn.drop_float_array.html)
/// with the returned [Array](struct.Array.html), in order to free the memory it allocates.
///
/// # Safety
///
/// This function is unsafe because it accesses a raw pointer which could contain arbitrary data
#[no_mangle]
pub extern "C" fn decode_polyline_ffi(pl: *const c_char, precision: uint32_t) -> Array {
    let s: String = unsafe { CStr::from_ptr(pl).to_string_lossy().into_owned() };
    arr_from_string(s, precision)
}

/// Convert an array of coordinates into a Polyline
///
/// Callers must pass two arguments:
///
/// - a [Struct](struct.Array.html) with two fields:
///     - `data`, a void pointer to an array of floating-point lat, lon coordinates: `[[1.0, 2.0]]`
///     - `len`, the length of the array being passed. Its type must be `size_t`: `1`
/// - an unsigned 32-bit `int` for precision (5 for Google Polylines, 6 for
/// OSRM and Valhalla Polylines)
///
/// # Examples
///
/// ```
/// extern crate libc;
/// use libc::{c_void, size_t};
/// use std::ffi::CStr;
/// use std::slice;
/// let input = vec![[1.0, 2.0], [3.0, 4.0]].as_slice();
/// let array = Array { data: input.as_ptr() as *const c_void, len: input.len() as size_t };
/// let output = "_ibE_seK_seK_seK".to_string();
/// let pl = encode_coordinates_ffi(array, 5);
/// let result = unsafe { CStr::from_ptr(pl).to_str().unwrap() };
/// assert_eq!(result, output);
/// drop_cstring(pl);
/// ```
///
/// A decoding failure will always return a string: "Couldn't decode Polyline"
///
/// Implementations calling this function **must** call [`drop_cstring`](fn.drop_cstring.html)
/// with the returned `c_char` pointer, in order to free the memory it allocates.
///
/// # Safety
///
/// This function is unsafe because it accesses a raw pointer which could contain arbitrary data
#[no_mangle]
pub extern "C" fn encode_coordinates_ffi(coords: Array, precision: uint32_t) -> *mut c_char {
    let s: String = string_from_arr(coords, precision);
    match CString::new(s) {
        Ok(res) => res.into_raw(),
        // It's arguably better to fail noisily, but this is robust
        Err(_) => CString::new("Couldn't decode Polyline".to_string()).unwrap().into_raw(),
    }
}

/// Free Array memory which Rust has allocated across the FFI boundary
///
/// # Safety
///
/// This function is unsafe because it accesses a raw pointer which could contain arbitrary data
#[no_mangle]
pub extern "C" fn drop_float_array(arr: Array) {
    if arr.data.is_null() {
        return;
    }
    let _: Vec<_> = arr.into();
}

/// Free `CString` memory which Rust has allocated across the FFI boundary
///
/// # Safety
///
/// This function is unsafe because it accesses a raw pointer which could contain arbitrary data
#[no_mangle]
pub extern "C" fn drop_cstring(p: *mut c_char) {
    let _ = unsafe { CString::from_raw(p) };
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::ptr;
    use std::ffi::{CString, CStr};

    #[test]
    fn test_array_conversion() {
        let original = vec![[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]];
        // move into an Array, and leak it
        let arr: Array = original.into();
        // move back into an slice -- leaked value still needs to be dropped
        let converted: Vec<_> = arr.into();
        assert_eq!(converted, &[[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]]);
        // drop it
        drop_float_array(converted.into());
    }

    #[test]
    fn test_drop_empty_float_array() {
        let original = vec![[1.0, 2.0], [3.0, 4.0]];
        // move into an Array, and leak it
        let mut arr: Array = original.into();
        // zero Array contents
        arr.data = ptr::null();
        drop_float_array(arr);
    }

    #[test]
    fn test_coordinate_conversion() {
        let input = vec![[1.0, 2.0], [3.0, 4.0]];
        let output = "_ibE_seK_seK_seK";
        let input_arr: Array = input.into();
        let transformed: String = super::string_from_arr(input_arr, 5);
        assert_eq!(transformed, output);
    }

    #[test]
    fn test_string_conversion() {
        let input = "_ibE_seK_seK_seK".to_string();
        let output = [[1.0, 2.0], [3.0, 4.0]];
        // String to Array
        let transformed: Array = super::arr_from_string(input, 5);
        // Array to Vec
        let transformed_arr: Vec<_> = transformed.into();
        assert_eq!(transformed_arr, output);
    }

    #[test]
    #[should_panic]
    fn test_bad_string_conversion() {
        let input = "_p~iF~ps|U_u🗑lLnnqC_mqNvxq`@".to_string();
        let output = vec![[1.0, 2.0], [3.0, 4.0]];
        // String to Array
        let transformed: Array = super::arr_from_string(input, 5);
        // Array to Vec
        let transformed_arr: Vec<_> = transformed.into();
        // this will fail, bc transformed_arr is [[NaN, NaN]]
        assert_eq!(transformed_arr, output.as_slice());
    }

    #[test]
    fn test_ffi_polyline_decoding() {
        let input = CString::new("_ibE_seK_seK_seK").unwrap().as_ptr();
        let result: Vec<_> = decode_polyline_ffi(input, 5).into();
        assert_eq!(result, [[1.0, 2.0], [3.0, 4.0]]);
        drop_float_array(result.into());
    }

    #[test]
    fn test_ffi_coordinate_encoding() {
        let input: Array = vec![[1.0, 2.0], [3.0, 4.0]].into();
        let output = "_ibE_seK_seK_seK".to_string();
        let pl = encode_coordinates_ffi(input, 5);
        // Allocate a new String
        let result = unsafe { CStr::from_ptr(pl).to_str().unwrap() };
        assert_eq!(result, output);
        // Drop received FFI data
        drop_cstring(pl);
    }

    #[test]
    fn test_send_coords() {
        let res: Vec<_> = send_coords().into();
        assert_eq!(res, &[[1.0, 2.0], [3.0, 4.0]]);
        drop_float_array(res.into())
    }
}
<commit_msg>Remove test we no longer need<commit_after>#![doc(html_root_url = "https://urschrei.github.io/polyline-ffi/")]
//! This module exposes functions for accessing the Polyline encoding and decoding functions via FFI
extern crate polyline;
use polyline::{encode_coordinates, decode_polyline};
use std::mem;
use std::slice;
use std::f64;
use std::ffi::{CStr, CString};

extern crate libc;
use self::libc::{c_char, c_void, uint32_t, size_t};

#[repr(C)]
pub struct Array {
    pub data: *const c_void,
    pub len: size_t,
}

// Build an Array from &[[f64; 2]], so it can be leaked across the FFI boundary
impl From<Vec<[f64; 2]>> for Array {
    fn from(sl: Vec<[f64; 2]>) -> Self {
        let array = Array {
            data: sl.as_ptr() as *const c_void,
            len: sl.len() as size_t,
        };
        mem::forget(sl);
        array
    }
}

// Build &[[f64; 2]] from an Array, so it can be dropped
impl From<Array> for Vec<[f64; 2]> {
    fn from(arr: Array) -> Self {
        unsafe { slice::from_raw_parts(arr.data as *mut [f64; 2], arr.len).to_vec() }
    }
}

// Decode a Polyline into an Array
fn arr_from_string(incoming: String, precision: uint32_t) -> Array {
    let result: Array = match decode_polyline(incoming, precision) {
        Ok(res) => res.into(),
        // should be easy to check for
        Err(_) => vec![[f64::NAN, f64::NAN]].into(),
    };
    result.into()
}

// Decode an Array into a Polyline
fn string_from_arr(incoming: Array, precision: uint32_t) -> String {
    let inc: Vec<_> = incoming.into();
    match encode_coordinates(&inc, precision) {
        Ok(res) => res,
        // we don't need to adapt the error
        Err(res) => res,
    }
}

/// Convert a Polyline into an array of coordinates
///
/// Callers must pass two arguments:
///
/// - a pointer to `NUL`-terminated characters (`char*`)
/// - an unsigned 32-bit `int` for precision (5 for Google Polylines, 6 for
/// OSRM and Valhalla Polylines)
///
/// # Examples
///
/// ```
/// use std::ffi::CString;
/// use std::slice;
/// let input = CString::new("_ibE_seK_seK_seK").unwrap().as_ptr();
/// let result: Array = decode_polyline_ffi(input, 5);
/// let slice = unsafe { slice::from_raw_parts(result.data as *const [f64; 2], result.len) };
/// assert_eq!(slice, [[1.0, 2.0], [3.0, 4.0]]);
/// drop_float_array(result);
/// ```
///
/// A decoding failure will always return an array: `[[NaN, NaN]]`
///
/// Implementations calling this function **must** call [`drop_float_array`](fn.drop_float_array.html)
/// with the returned [Array](struct.Array.html), in order to free the memory it allocates.
///
/// # Safety
///
/// This function is unsafe because it accesses a raw pointer which could contain arbitrary data
#[no_mangle]
pub extern "C" fn decode_polyline_ffi(pl: *const c_char, precision: uint32_t) -> Array {
    let s: String = unsafe { CStr::from_ptr(pl).to_string_lossy().into_owned() };
    arr_from_string(s, precision)
}

/// Convert an array of coordinates into a Polyline
///
/// Callers must pass two arguments:
///
/// - a [Struct](struct.Array.html) with two fields:
///     - `data`, a void pointer to an array of floating-point lat, lon coordinates: `[[1.0, 2.0]]`
///     - `len`, the length of the array being passed. Its type must be `size_t`: `1`
/// - an unsigned 32-bit `int` for precision (5 for Google Polylines, 6 for
/// OSRM and Valhalla Polylines)
///
/// # Examples
///
/// ```
/// extern crate libc;
/// use libc::{c_void, size_t};
/// use std::ffi::CStr;
/// use std::slice;
/// let input = vec![[1.0, 2.0], [3.0, 4.0]].as_slice();
/// let array = Array { data: input.as_ptr() as *const c_void, len: input.len() as size_t };
/// let output = "_ibE_seK_seK_seK".to_string();
/// let pl = encode_coordinates_ffi(array, 5);
/// let result = unsafe { CStr::from_ptr(pl).to_str().unwrap() };
/// assert_eq!(result, output);
/// drop_cstring(pl);
/// ```
///
/// A decoding failure will always return a string: "Couldn't decode Polyline"
///
/// Implementations calling this function **must** call [`drop_cstring`](fn.drop_cstring.html)
/// with the returned `c_char` pointer, in order to free the memory it allocates.
///
/// # Safety
///
/// This function is unsafe because it accesses a raw pointer which could contain arbitrary data
#[no_mangle]
pub extern "C" fn encode_coordinates_ffi(coords: Array, precision: uint32_t) -> *mut c_char {
    let s: String = string_from_arr(coords, precision);
    match CString::new(s) {
        Ok(res) => res.into_raw(),
        // It's arguably better to fail noisily, but this is robust
        Err(_) => CString::new("Couldn't decode Polyline".to_string()).unwrap().into_raw(),
    }
}

/// Free Array memory which Rust has allocated across the FFI boundary
///
/// # Safety
///
/// This function is unsafe because it accesses a raw pointer which could contain arbitrary data
#[no_mangle]
pub extern "C" fn drop_float_array(arr: Array) {
    if arr.data.is_null() {
        return;
    }
    let _: Vec<_> = arr.into();
}

/// Free `CString` memory which Rust has allocated across the FFI boundary
///
/// # Safety
///
/// This function is unsafe because it accesses a raw pointer which could contain arbitrary data
#[no_mangle]
pub extern "C" fn drop_cstring(p: *mut c_char) {
    let _ = unsafe { CString::from_raw(p) };
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::ptr;
    use std::ffi::{CString, CStr};

    #[test]
    fn test_array_conversion() {
        let original = vec![[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]];
        // move into an Array, and leak it
        let arr: Array = original.into();
        // move back into an slice -- leaked value still needs to be dropped
        let converted: Vec<_> = arr.into();
        assert_eq!(converted, &[[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]]);
        // drop it
        drop_float_array(converted.into());
    }

    #[test]
    fn test_drop_empty_float_array() {
        let original = vec![[1.0, 2.0], [3.0, 4.0]];
        // move into an Array, and leak it
        let mut arr: Array = original.into();
        // zero Array contents
        arr.data = ptr::null();
        drop_float_array(arr);
    }

    #[test]
    fn test_coordinate_conversion() {
        let input = vec![[1.0, 2.0], [3.0, 4.0]];
        let output = "_ibE_seK_seK_seK";
        let input_arr: Array = input.into();
        let transformed: String = super::string_from_arr(input_arr, 5);
        assert_eq!(transformed, output);
    }

    #[test]
    fn test_string_conversion() {
        let input = "_ibE_seK_seK_seK".to_string();
        let output = [[1.0, 2.0], [3.0, 4.0]];
        // String to Array
        let transformed: Array = super::arr_from_string(input, 5);
        // Array to Vec
        let transformed_arr: Vec<_> = transformed.into();
        assert_eq!(transformed_arr, output);
    }

    #[test]
    #[should_panic]
    fn test_bad_string_conversion() {
        let input = "_p~iF~ps|U_u🗑lLnnqC_mqNvxq`@".to_string();
        let output = vec![[1.0, 2.0], [3.0, 4.0]];
        // String to Array
        let transformed: Array = super::arr_from_string(input, 5);
        // Array to Vec
        let transformed_arr: Vec<_> = transformed.into();
        // this will fail, bc transformed_arr is [[NaN, NaN]]
        assert_eq!(transformed_arr, output.as_slice());
    }

    #[test]
    fn test_ffi_polyline_decoding() {
        let input = CString::new("_ibE_seK_seK_seK").unwrap().as_ptr();
        let result: Vec<_> = decode_polyline_ffi(input, 5).into();
        assert_eq!(result, [[1.0, 2.0], [3.0, 4.0]]);
        drop_float_array(result.into());
    }

    #[test]
    fn test_ffi_coordinate_encoding() {
        let input: Array = vec![[1.0, 2.0], [3.0, 4.0]].into();
        let output = "_ibE_seK_seK_seK".to_string();
        let pl = encode_coordinates_ffi(input, 5);
        // Allocate a new String
        let result = unsafe { CStr::from_ptr(pl).to_str().unwrap() };
        assert_eq!(result, output);
        // Drop received FFI data
        drop_cstring(pl);
    }
}
<|endoftext|>


<commit_before>
#![warn(missing_docs)]

//! This crate provides types `Nil` and `Cons<H, T>`, which together allow for creating lists consisting of multiple types.
//! The types in the list are present in the type of the list, so that `Cons<i32, Cons<i64, Nil>>` contains an `i32` and an `i64`.
//! If type `T` is present exactly once in an `HList`, it is usually possible to allow the compiler to find the `T` in the `HList` by using the `Find` trait.

/// The empty `HList`.
pub struct Nil;

/// An `HList` with `H` at position 0, and `T` as the rest of the list.
pub struct Cons<H, T>(pub H, pub T);

/// A marker trait that `Nil` and `Cons<H, T>` satisfies.
/// Not currently used to enforce proper hlists, although this may change.
/// Provides the `push()` method
pub trait HList: Sized {
    /// Consumes the `HList`, and returns a new HList with `item` at the beginning.
    fn push<N>(self, item: N) -> Cons<N, Self> {
        Cons(item, self)
    }
}

impl HList for Nil {}
impl<H, T> HList for Cons<H, T> {}

/// Used as an index into an `HList`.
///
/// `Here` is 0, pointing to the head of the HList.
///
/// Users should normally allow type inference to create this type
#[allow(dead_code)]
pub enum Here {}

/// Used as an index into an `HList`.
///
/// `There<T>` is 1 + `T`.
///
/// Users should normally allow type inference to create this type.
#[allow(dead_code)]
pub struct There<T>(std::marker::PhantomData<T>);

/// `Find<T, I>` is implemented for an `HList` if index `I` of the `HList` is a `T`
///
/// Rust's type inferencer can often produce a correct `I`
/// if there is exactly one `T` in the `HList`.
///
/// ```rust
/// use hlist::{HList, Nil, Find};
///
/// // The type of list is Cons<i64, Cons<i32, Nil>>
/// let list = Nil.push(0i32).push(1i64);
///
/// // Here list satisfies the trait Find<i64, Here>.
/// // The compiler infers the second type parameter.
/// let a: i64 = *list.get();
/// assert!(a == 1);
///
/// // Here list satisfies the trait Find<i32, There<Here>>.
/// let b: i32 = *list.get();
/// assert!(b == 0);
/// ```
///
/// Functions that need to look up values of a type in an HList given to them should get the index from the call site:
///
/// ```rust
/// fn foo<I, L: Find<i32, I>>(list: &L) -> i32 {
///     *list.get()
/// }
/// let list = Nil.push("foo").push(5i32).push("bar");
/// assert!(foo(&list) == 5);
/// ```rust
///
/// When `foo()` is called, the compiler figures out the appropriate value for `I`.
pub trait Find<T, I> {
    /// Retrieves a &T.
    /// Allows for type inferencing to act like type-directed search
    /// ```rust
    /// use hlist::{HList, Nil, Find}
    ///
    /// let list = Nil.push(0i32).push(1i64);
    /// let a: i64 = *list.get();
    /// assert!(a == 1);
    fn get(&self) -> &T;

    /// Retrieves a &mut T.
    /// Allows for type inferencing to act like type-directed search
    /// ```rust
    /// use hlist::{HList, Nil, Find}
    ///
    /// let mut list = Nil.push(0i32).push(1i64);
    /// *list.get_mut() = 5i32;
    /// let a: i32 = *list.get()
    /// assert!(a == 5);
    fn get_mut(&mut self) -> &mut T;
}

impl<T, Tail> Find<T, Here> for Cons<T, Tail> {
    fn get(&self) -> &T {
        &self.0
    }
    fn get_mut(&mut self) -> &mut T {
        &mut self.0
    }
}

impl<Head, T, Tail, TailIndex> Find<T, There<TailIndex>> for Cons<Head, Tail>
    where Tail: Find<T, TailIndex> {
    fn get(&self) -> &T {
        self.1.get()
    }
    fn get_mut(&mut self) -> &mut T {
        self.1.get_mut()
    }
}

#[test]
fn test_get() {
    let list = Nil.push(5i32).push("Foo");
    let a: i32 = *list.get();
    let b: &str = *list.get();
    assert!(a == 5i32);
    assert!(b == "Foo");
}

#[test]
fn test_get_mut() {
    let mut list = Nil.push(5i32).push("Foo");
    *list.get_mut() = 6i32;
    *list.get_mut() = "Bar";
    let a: i32 = *list.get();
    let b: &str = *list.get();
    assert!(a == 6i32);
    assert!(b == "Bar");
}

#[test]
fn test_index_as_type_parameter() {
    fn foo<I, L: Find<i32, I>>(list: &L) -> i32 {
        *list.get()
    }
    let list = Nil.push("foo").push(5i32).push("bar");
    assert!(foo(&list) == 5);
}
<commit_msg>Fix documentation<commit_after>
#![warn(missing_docs)]

//! This crate provides types `Nil` and `Cons<H, T>`, which together allow for creating lists consisting of multiple types.
//! The types in the list are present in the type of the list, so that `Cons<i32, Cons<i64, Nil>>` contains an `i32` and an `i64`.
//! If type `T` is present exactly once in an `HList`, it is usually possible to allow the compiler to find the `T` in the `HList` by using the `Find` trait.

/// The empty `HList`.
pub struct Nil;

/// An `HList` with `H` at position 0, and `T` as the rest of the list.
pub struct Cons<H, T>(pub H, pub T);

/// A marker trait that `Nil` and `Cons<H, T>` satisfies.
/// Not currently used to enforce proper hlists, although this may change.
/// Provides the `push()` method
pub trait HList: Sized {
    /// Consumes the `HList`, and returns a new HList with `item` at the beginning.
    fn push<N>(self, item: N) -> Cons<N, Self> {
        Cons(item, self)
    }
}

impl HList for Nil {}
impl<H, T> HList for Cons<H, T> {}

/// Used as an index into an `HList`.
///
/// `Here` is 0, pointing to the head of the HList.
///
/// Users should normally allow type inference to create this type
#[allow(dead_code)]
pub enum Here {}

/// Used as an index into an `HList`.
///
/// `There<T>` is 1 + `T`.
///
/// Users should normally allow type inference to create this type.
#[allow(dead_code)]
pub struct There<T>(std::marker::PhantomData<T>);

/// `Find<T, I>` is implemented for an `HList` if index `I` of the `HList` is a `T`
///
/// Rust's type inferencer can often produce a correct `I`
/// if there is exactly one `T` in the `HList`.
///
/// ```rust
/// use hlist::{HList, Nil, Find};
///
/// // The type of list is Cons<i64, Cons<i32, Nil>>
/// let list = Nil.push(0i32).push(1i64);
///
/// // Here list satisfies the trait Find<i64, Here>.
/// // The compiler infers the second type parameter.
/// let a: i64 = *list.get();
/// assert!(a == 1);
///
/// // Here list satisfies the trait Find<i32, There<Here>>.
/// let b: i32 = *list.get();
/// assert!(b == 0);
/// ```
///
/// Functions that need to look up values of a type in an HList given to them should get the index from the call site:
///
/// ```rust
/// fn foo<I, L: Find<i32, I>>(list: &L) -> i32 {
///     *list.get()
/// }
/// let list = Nil.push("foo").push(5i32).push("bar");
/// assert!(foo(&list) == 5);
/// ```
///
/// When `foo()` is called, the compiler figures out the appropriate value for `I`.
pub trait Find<T, I> {
    /// Retrieves a &T.
    /// Allows for type inferencing to act like type-directed search
    /// ```rust
    /// use hlist::{HList, Nil, Find}
    ///
    /// let list = Nil.push(0i32).push(1i64);
    /// let a: i64 = *list.get();
    /// assert!(a == 1);
    fn get(&self) -> &T;

    /// Retrieves a &mut T.
    /// Allows for type inferencing to act like type-directed search
    /// ```rust
    /// use hlist::{HList, Nil, Find}
    ///
    /// let mut list = Nil.push(0i32).push(1i64);
    /// *list.get_mut() = 5i32;
    /// let a: i32 = *list.get()
    /// assert!(a == 5);
    fn get_mut(&mut self) -> &mut T;
}

impl<T, Tail> Find<T, Here> for Cons<T, Tail> {
    fn get(&self) -> &T {
        &self.0
    }
    fn get_mut(&mut self) -> &mut T {
        &mut self.0
    }
}

impl<Head, T, Tail, TailIndex> Find<T, There<TailIndex>> for Cons<Head, Tail>
    where Tail: Find<T, TailIndex> {
    fn get(&self) -> &T {
        self.1.get()
    }
    fn get_mut(&mut self) -> &mut T {
        self.1.get_mut()
    }
}

#[test]
fn test_get() {
    let list = Nil.push(5i32).push("Foo");
    let a: i32 = *list.get();
    let b: &str = *list.get();
    assert!(a == 5i32);
    assert!(b == "Foo");
}

#[test]
fn test_get_mut() {
    let mut list = Nil.push(5i32).push("Foo");
    *list.get_mut() = 6i32;
    *list.get_mut() = "Bar";
    let a: i32 = *list.get();
    let b: &str = *list.get();
    assert!(a == 6i32);
    assert!(b == "Bar");
}

#[test]
fn test_index_as_type_parameter() {
    fn foo<I, L: Find<i32, I>>(list: &L) -> i32 {
        *list.get()
    }
    let list = Nil.push("foo").push(5i32).push("bar");
    assert!(foo(&list) == 5);
}
<|endoftext|>


<commit_before>extern crate iron;
extern crate unicase;

use unicase::UniCase;
use iron::prelude::*;
use iron::method::Method;
use iron::status;
use iron::headers::{Origin, AccessControlRequestMethod, AccessControlAllowOrigin,
                    AccessControlAllowHeaders, AccessControlMaxAge, AccessControlAllowMethods};
use iron::middleware::{AroundMiddleware, Handler};

pub enum AllowedOrigins {
    Any,
    Specific(std::collections::HashSet<unicase::UniCase<String>>),
}

impl AllowedOrigins {
    fn answer_to(&self, origin: String) -> Option<String> {
        match self {
            &AllowedOrigins::Any => Some("*".to_owned()),
            &AllowedOrigins::Specific(ref allowed) => {
                if allowed.contains(&UniCase(origin.clone())) {
                    Some(origin)
                } else {
                    None
                }
            }
        }
    }
}

pub struct CorsMiddleware {
    pub allowed_origins: AllowedOrigins,
    pub allowed_methods: Vec<Method>,
    pub allowed_headers: Vec<UniCase<String>>,
    pub exposed_headers: Vec<String>,
    pub allow_credentials: bool,
    pub max_age_seconds: u32,
}

impl CorsMiddleware {
    pub fn new() -> CorsMiddleware {
        let allowed_methods: Vec<Method> = vec![Method::Get, Method::Put, Method::Post];
        let allowed_headers: Vec<unicase::UniCase<String>> =
            vec![// To allow application/json
                 UniCase("Content-Type".to_owned()),
                 // Set by some js libs
                 UniCase("X-Requested-With".to_owned())];
        let exposed_headers: Vec<String> = Vec::new();
        CorsMiddleware {
            allowed_origins: AllowedOrigins::Any,
            allowed_methods: allowed_methods,
            allowed_headers: allowed_headers,
            exposed_headers: exposed_headers,
            allow_credentials: false,
            max_age_seconds: 60 * 60,
        }
    }

    fn handle(&self, req: &mut Request, handler: &Handler) -> IronResult<Response> {
        if req.method == Method::Options &&
           req.headers.get::<AccessControlRequestMethod>().is_some() {
            self.handle_preflight(req)
        } else {
            self.handle_normal(req, handler)
        }
    }

    fn handle_preflight(&self, req: &mut Request) -> IronResult<Response> {
        // Preflight request
        // 1.If the Origin header is not present terminate this set of steps. The request is
        // outside the scope of this specification.
        let maybe_origin = req.headers.get::<Origin>();
        if maybe_origin.is_none() {
            let resp = Response::with((status::BadRequest,
                                       "Preflight request without Origin header"));
            return Ok(resp);
        }
        let origin = maybe_origin.unwrap();
        // Preflight is NoContent
        let mut res = Response::with((status::NoContent));
        let origin_string = origin.to_string();
        let allowed_origin = self.allowed_origins.answer_to(origin_string);
        //
        // 2.If the value of the Origin header is not a case-sensitive match for any of the
        // values in list of origins do not set any additional headers and terminate this
        // set of steps.
        if allowed_origin.is_none() {
            let resp = Response::with((status::BadRequest,
                                       "Preflight request with disallowed origin"));
            return Ok(resp);
        }
        //
        // Note: Always matching is acceptable since the list of origins can be unbounded.
        //
        // Note: The Origin header can only contain a single origin as the user agent
        //       will not follow redirects.
        //
        // 3. Let method be the value as result of parsing the Access-Control-Request-Method
        // header.
        //
        // If there is no Access-Control-Request-Method header or if parsing failed, do not
        // set any additional headers and terminate this set of steps. The request is
        // outside the scope of this specification.
        //
        // 4. Let header field-names be the values as result of parsing the
        // Access-Control-Request-Headers headers.
        //
        // If there are no Access-Control-Request-Headers headers let header field-names be
        // the empty list.
        //
        // If parsing failed do not set any additional headers and terminate this set of
        // steps. The request is outside the scope of this specification.
        //
        // 5.If method is not a case-sensitive match for any of the values in list of
        //   methods do not set any additional headers and terminate this set of steps.
        //
        // Always matching is acceptable since the list of methods can be unbounded.
        //
        // 6. If any of the header field-names is not a ASCII case-insensitive match for any
        // of the values in list of headers do not set any additional headers and terminate
        // this set of steps.
        //
        // Always matching is acceptable since the list of headers can be unbounded.
        //
        // 7. If the resource supports credentials add a single Access-Control-Allow-Origin
        // header, with the value of the Origin header as value, and add a single
        // Access-Control-Allow-Credentials header with the case-sensitive string "true" as
        // value.
        //
        // Otherwise, add a single Access-Control-Allow-Origin header, with either the
        // value of the Origin header or the string "*" as value.
        res.headers.set(AccessControlAllowOrigin::Value(format!("{}", origin)));
        //
        // The string "*" cannot be used for a resource that supports credentials.
        //
        // 8. Optionally add a single Access-Control-Max-Age header with as value the amount
        // of seconds the user agent is allowed to cache the result of the request.
        res.headers.set(AccessControlMaxAge(self.max_age_seconds));
        //
        // 9. If method is a simple method this step may be skipped.
        //
        // Add one or more Access-Control-Allow-Methods headers consisting of (a subset of)
        // the list of methods.
        res.headers.set(AccessControlAllowMethods(self.allowed_methods.clone()));
        //
        // If a method is a simple method it does not need to be listed, but this is not
        // prohibited.
        //
        // Since the list of methods can be unbounded, simply returning the method
        // indicated by Access-Control-Request-Method (if supported) can be enough.
        //
        // 10.If each of the header field-names is a simple header and none is Content-Type,
        // this step may be skipped.
        //
        // Add one or more Access-Control-Allow-Headers headers consisting of (a subset of)
        // the list of headers.
        res.headers.set(AccessControlAllowHeaders(self.allowed_headers.clone()));
        //
        // If a header field name is a simple header and is not Content-Type, it is not
        // required to be listed. Content-Type is to be listed as only a subset of its
        // values makes it qualify as simple header.
        //
        // Since the list of headers can be unbounded, simply returning supported headers
        // from Access-Control-Allow-Headers can be enough.
        Ok(res)
    }

    fn handle_normal(&self, req: &mut Request, handler: &Handler) -> IronResult<Response> {
        // Normal request
        // 1.If the Origin header is not present terminate this set of steps. The request is
        // outside the scope of this specification.
        let has_origin = req.headers.get::<Origin>().is_some();
        if !has_origin {
            return handler.handle(req);
        }
        //
        // 2.If the value of the Origin header is not a case-sensitive match for any of the
        // values in list of origins, do not set any additional headers and terminate this
        // set of steps.
        //
        // Note: Always matching is acceptable since the list of origins can be unbounded.
        //
        // 3. If the resource supports credentials add a single Access-Control-Allow-Origin
        // header, with the value of the Origin header as value, and add a single
        // Access-Control-Allow-Credentials header with the case-sensitive string "true" as
        // value.
        //
        // Otherwise, add a single Access-Control-Allow-Origin header, with either the
        // value of the Origin header or the string "*" as value.
        //
        // Note: The string "*" cannot be used for a resource that supports credentials.
        //
        // 4. If the list of exposed headers is not empty add one or more
        // Access-Control-Expose-Headers headers, with as values the header field names
        // given in the list of exposed headers.
        let result = handler.handle(req);
        match result {
            Ok(mut res) => {
                // And set CORS headers
                let origin = req.headers.get::<Origin>().unwrap();
                res.headers.set(AccessControlAllowOrigin::Value(format!("{}", origin)));
                Ok(res)
            }
            _ => result,
        }
    }
}

impl AroundMiddleware for CorsMiddleware {
    fn around(self, handler: Box<Handler>) -> Box<Handler> {
        Box::new(move |req: &mut Request| self.handle(req, &handler))
    }
}

#[cfg(test)]
mod tests {
    extern crate router;
    extern crate hyper;
    extern crate mount;
    use iron::Listening;
    use self::router::Router;
    use iron::prelude::*;
    use iron::status;
    use self::mount::Mount;
    use self::hyper::Client;
    use self::hyper::header::Headers;
    use std::io::Read;
    use iron::headers::{Origin, AccessControlRequestMethod, AccessControlAllowOrigin,
                        AccessControlAllowHeaders, AccessControlMaxAge, AccessControlAllowMethods};
    use iron::method::Method;
    use super::CorsMiddleware;
    use std::str::FromStr;

    struct AutoServer {
        listening: Listening,
        port: u16,
    }

    impl AutoServer {
        pub fn new() -> AutoServer {
            let mut router = Router::new();
            let handler = |_: &mut Request| Ok(Response::with((status::ImATeapot, "")));
            router.get("/", handler, "get_a");
            let mut chain = Chain::new(router);
            chain.link_around(CorsMiddleware::new());
            let mut mount = Mount::new();
            mount.mount("/a", chain);
            let l = Iron::new(mount).http(format!("localhost:0")).unwrap();
            let p = l.socket.port();
            AutoServer {
                listening: l,
                port: p,
            }
        }
    }

    impl Drop for AutoServer {
        fn drop(&mut self) {
            // Workaround for https://github.com/hyperium/hyper/issues/338
            self.listening.close().unwrap();
        }
    }

    #[test]
    fn normal_request_possible() {
        let server = AutoServer::new();
        let client = Client::new();
        let res = client.get(&format!("http://127.0.0.1:{}/a", server.port)).send().unwrap();
        assert_eq!(res.status, status::ImATeapot);
    }

    #[test]
    fn preflight_to_nonexistent_route_fails() {
        let server = AutoServer::new();
        let client = Client::new();
        let mut headers = Headers::new();
        headers.set(AccessControlRequestMethod(Method::Get));
        headers.set(Origin::from_str("http://www.a.com:8080").unwrap());
        let mut res = client.request(Method::Options,
                                     &format!("http://127.0.0.1:{}/b", server.port))
            .headers(headers)
            .send()
            .unwrap();
        assert_eq!(res.status, status::NotFound);
        let mut payload = String::new();
        res.read_to_string(&mut payload).unwrap();
        assert_eq!(payload, "");
    }

    #[test]
    fn preflight_without_origin_is_bad_request() {
        let server = AutoServer::new();
        let client = Client::new();
        let mut headers = Headers::new();
        headers.set(AccessControlRequestMethod(Method::Get));
        let mut res = client.request(Method::Options,
                                     &format!("http://127.0.0.1:{}/a", server.port))
            .headers(headers)
            .send()
            .unwrap();
        assert_eq!(res.status, status::BadRequest);
        let mut payload = String::new();
        res.read_to_string(&mut payload).unwrap();
        assert_eq!(payload, "Preflight request without Origin header");
    }

    #[test]
    fn preflight_with_origin_accepts_same_origin() {
        let server = AutoServer::new();
        let client = Client::new();
        let mut headers = Headers::new();
        headers.set(AccessControlRequestMethod(Method::Get));
        headers.set(Origin::from_str("http://www.a.com:8080").unwrap());
        let res = client.request(Method::Options,
                                 &format!("http://127.0.0.1:{}/a", server.port))
            .headers(headers)
            .send()
            .unwrap();
        assert_eq!(res.status, status::NoContent);
        let allow_origin = res.headers.get::<AccessControlAllowOrigin>().unwrap();
        assert_eq!(format!("{}", allow_origin), "http://www.a.com:8080");
        let allow_headers = res.headers.get::<AccessControlAllowHeaders>().unwrap();
        assert_eq!(format!("{}", allow_headers),
                   "Content-Type, X-Requested-With");
        let allow_methods = res.headers.get::<AccessControlAllowMethods>().unwrap();
        assert_eq!(format!("{}", allow_methods), "GET, PUT, POST");
        let max_age = res.headers.get::<AccessControlMaxAge>().unwrap();
        assert_eq!(max_age.0, 60 * 60u32);
    }

    #[test]
    fn normal_request_allows_origin() {
        let server = AutoServer::new();
        let client = Client::new();
        let mut headers = Headers::new();
        headers.set(Origin::from_str("http://www.a.com:8080").unwrap());
        let res = client.get(&format!("http://127.0.0.1:{}/a", server.port))
            .headers(headers)
            .send()
            .unwrap();
        assert_eq!(res.status, status::ImATeapot);
        let allow_origin = res.headers.get::<AccessControlAllowOrigin>().unwrap();
        assert_eq!(format!("{}", allow_origin), "http://www.a.com:8080");
        assert!(res.headers.get::<AccessControlAllowHeaders>().is_none());
        assert!(res.headers.get::<AccessControlAllowMethods>().is_none());
        assert!(res.headers.get::<AccessControlMaxAge>().is_none());
    }

    #[test]
    fn normal_request_without_origin_is_passthrough() {
        let server = AutoServer::new();
        let client = Client::new();
        let res = client.get(&format!("http://127.0.0.1:{}/a", server.port)).send().unwrap();
        assert_eq!(res.status, status::ImATeapot);
        assert!(res.headers.get::<AccessControlAllowOrigin>().is_none());
        assert!(res.headers.get::<AccessControlAllowHeaders>().is_none());
        assert!(res.headers.get::<AccessControlAllowMethods>().is_none());
        assert!(res.headers.get::<AccessControlMaxAge>().is_none());
    }

}
<commit_msg>no unicase on origin<commit_after>extern crate iron;
extern crate unicase;

use unicase::UniCase;
use iron::prelude::*;
use iron::method::Method;
use iron::status;
use iron::headers::{Origin, AccessControlRequestMethod, AccessControlAllowOrigin,
                    AccessControlAllowHeaders, AccessControlMaxAge, AccessControlAllowMethods};
use iron::middleware::{AroundMiddleware, Handler};
use std::collections::HashSet;

// Using case-sensitive match of protocol://host:port
// For a formal definition, see
// https://tools.ietf.org/html/rfc6454#section-4
pub enum AllowedOrigins {
    Any,
    Specific(HashSet<String>),
}

impl AllowedOrigins {
    fn answer_to(&self, origin: String) -> Option<String> {
        match self {
            &AllowedOrigins::Any => Some("*".to_owned()),
            &AllowedOrigins::Specific(ref allowed) => {
                if allowed.contains(&origin) {
                    Some(origin)
                } else {
                    None
                }
            }
        }
    }
}

pub struct CorsMiddleware {
    pub allowed_origins: AllowedOrigins,
    pub allowed_methods: Vec<Method>,
    pub allowed_headers: Vec<UniCase<String>>,
    pub exposed_headers: Vec<String>,
    pub allow_credentials: bool,
    pub max_age_seconds: u32,
}

impl CorsMiddleware {
    pub fn new() -> CorsMiddleware {
        let allowed_methods: Vec<Method> = vec![Method::Get, Method::Put, Method::Post];
        let allowed_headers: Vec<unicase::UniCase<String>> =
            vec![// To allow application/json
                 UniCase("Content-Type".to_owned()),
                 // Set by some js libs
                 UniCase("X-Requested-With".to_owned())];
        let exposed_headers: Vec<String> = Vec::new();
        CorsMiddleware {
            allowed_origins: AllowedOrigins::Any,
            allowed_methods: allowed_methods,
            allowed_headers: allowed_headers,
            exposed_headers: exposed_headers,
            allow_credentials: false,
            max_age_seconds: 60 * 60,
        }
    }

    fn handle(&self, req: &mut Request, handler: &Handler) -> IronResult<Response> {
        // http://stackoverflow.com/questions/14015118/
        // what-is-the-expected-response-to-an-invalid-cors-request
        if req.method == Method::Options &&
           req.headers.get::<AccessControlRequestMethod>().is_some() {
            self.handle_preflight(req, handler)
        } else {
            self.handle_normal(req, handler)
        }
    }

    fn handle_preflight(&self, req: &mut Request, handler: &Handler) -> IronResult<Response> {
        // Preflight request
        // 1.If the Origin header is not present terminate this set of steps. The request is
        // outside the scope of this specification.
        let maybe_origin = req.headers.get::<Origin>();
        if maybe_origin.is_none() {
            let resp = Response::with((status::BadRequest,
                                       "Preflight request without Origin header"));
            return Ok(resp);
        }
        let origin = maybe_origin.unwrap();
        // Preflight is NoContent
        let mut res = Response::with((status::NoContent));
        let origin_string = origin.to_string();
        let allowed_origin = self.allowed_origins.answer_to(origin_string);
        //
        // 2.If the value of the Origin header is not a case-sensitive match for any of the
        // values in list of origins do not set any additional headers and terminate this
        // set of steps.
        if allowed_origin.is_none() {
            let resp = Response::with((status::BadRequest,
                                       "Preflight request requesting disallowed origin"));
            return Ok(resp);
        }
        //
        // Note: Always matching is acceptable since the list of origins can be unbounded.
        //
        // Note: The Origin header can only contain a single origin as the user agent
        //       will not follow redirects.
        //
        // 3. Let method be the value as result of parsing the Access-Control-Request-Method
        // header.
        //
        // If there is no Access-Control-Request-Method header or if parsing failed, do not
        // set any additional headers and terminate this set of steps. The request is
        // outside the scope of this specification.
        //
        // 4. Let header field-names be the values as result of parsing the
        // Access-Control-Request-Headers headers.
        //
        // If there are no Access-Control-Request-Headers headers let header field-names be
        // the empty list.
        //
        // If parsing failed do not set any additional headers and terminate this set of
        // steps. The request is outside the scope of this specification.
        //
        // 5.If method is not a case-sensitive match for any of the values in list of
        //   methods do not set any additional headers and terminate this set of steps.
        //
        // Always matching is acceptable since the list of methods can be unbounded.
        //
        // 6. If any of the header field-names is not a ASCII case-insensitive match for any
        // of the values in list of headers do not set any additional headers and terminate
        // this set of steps.
        //
        // Always matching is acceptable since the list of headers can be unbounded.
        //
        // 7. If the resource supports credentials add a single Access-Control-Allow-Origin
        // header, with the value of the Origin header as value, and add a single
        // Access-Control-Allow-Credentials header with the case-sensitive string "true" as
        // value.
        //
        // Otherwise, add a single Access-Control-Allow-Origin header, with either the
        // value of the Origin header or the string "*" as value.
        res.headers.set(AccessControlAllowOrigin::Value(format!("{}", origin)));
        //
        // The string "*" cannot be used for a resource that supports credentials.
        //
        // 8. Optionally add a single Access-Control-Max-Age header with as value the amount
        // of seconds the user agent is allowed to cache the result of the request.
        res.headers.set(AccessControlMaxAge(self.max_age_seconds));
        //
        // 9. If method is a simple method this step may be skipped.
        //
        // Add one or more Access-Control-Allow-Methods headers consisting of (a subset of)
        // the list of methods.
        res.headers.set(AccessControlAllowMethods(self.allowed_methods.clone()));
        //
        // If a method is a simple method it does not need to be listed, but this is not
        // prohibited.
        //
        // Since the list of methods can be unbounded, simply returning the method
        // indicated by Access-Control-Request-Method (if supported) can be enough.
        //
        // 10.If each of the header field-names is a simple header and none is Content-Type,
        // this step may be skipped.
        //
        // Add one or more Access-Control-Allow-Headers headers consisting of (a subset of)
        // the list of headers.
        res.headers.set(AccessControlAllowHeaders(self.allowed_headers.clone()));
        //
        // If a header field name is a simple header and is not Content-Type, it is not
        // required to be listed. Content-Type is to be listed as only a subset of its
        // values makes it qualify as simple header.
        //
        // Since the list of headers can be unbounded, simply returning supported headers
        // from Access-Control-Allow-Headers can be enough.
        Ok(res)
    }

    fn handle_normal(&self, req: &mut Request, handler: &Handler) -> IronResult<Response> {
        // Normal request
        // 1.If the Origin header is not present terminate this set of steps. The request is
        // outside the scope of this specification.
        let has_origin = req.headers.get::<Origin>().is_some();
        if !has_origin {
            return handler.handle(req);
        }
        //
        // 2.If the value of the Origin header is not a case-sensitive match for any of the
        // values in list of origins, do not set any additional headers and terminate this
        // set of steps.
        //
        // Note: Always matching is acceptable since the list of origins can be unbounded.
        //
        // 3. If the resource supports credentials add a single Access-Control-Allow-Origin
        // header, with the value of the Origin header as value, and add a single
        // Access-Control-Allow-Credentials header with the case-sensitive string "true" as
        // value.
        //
        // Otherwise, add a single Access-Control-Allow-Origin header, with either the
        // value of the Origin header or the string "*" as value.
        //
        // Note: The string "*" cannot be used for a resource that supports credentials.
        //
        // 4. If the list of exposed headers is not empty add one or more
        // Access-Control-Expose-Headers headers, with as values the header field names
        // given in the list of exposed headers.
        let result = handler.handle(req);
        match result {
            Ok(mut res) => {
                // And set CORS headers
                let origin = req.headers.get::<Origin>().unwrap();
                res.headers.set(AccessControlAllowOrigin::Value(format!("{}", origin)));
                Ok(res)
            }
            _ => result,
        }
    }
}

impl AroundMiddleware for CorsMiddleware {
    fn around(self, handler: Box<Handler>) -> Box<Handler> {
        Box::new(move |req: &mut Request| self.handle(req, &handler))
    }
}

#[cfg(test)]
mod tests {
    extern crate router;
    extern crate hyper;
    extern crate mount;
    use iron::Listening;
    use self::router::Router;
    use iron::prelude::*;
    use iron::status;
    use self::mount::Mount;
    use self::hyper::Client;
    use self::hyper::header::Headers;
    use std::io::Read;
    use iron::headers::{Origin, AccessControlRequestMethod, AccessControlAllowOrigin,
                        AccessControlAllowHeaders, AccessControlMaxAge, AccessControlAllowMethods};
    use iron::method::Method;
    use iron::middleware::Handler;
    use super::{CorsMiddleware, AllowedOrigins};
    use std::str::FromStr;
    use std::collections::HashSet;

    struct AutoServer {
        listening: Listening,
        port: u16,
    }

    impl AutoServer {
        pub fn new() -> AutoServer {
            AutoServer::with_cors(CorsMiddleware::new())
        }

        pub fn with_cors(cors: CorsMiddleware) -> AutoServer {
            let get_handler = |_: &mut Request| Ok(Response::with((status::ImATeapot, "")));
            let put_handler = |_: &mut Request| Ok(Response::with((status::BadRequest, "")));

            let mut router = Router::new();
            router.get("", get_handler, "get_a");
            router.put("", put_handler, "put_a");

            let mut chain = Chain::new(router);
            chain.link_around(cors);
            let mut mount = Mount::new();

            mount.mount("/a", chain);
            AutoServer::with_handler(mount)
        }

        pub fn with_handler<H: Handler>(handler: H) -> AutoServer {
            let l = Iron::new(handler).http(format!("localhost:0")).unwrap();
            let p = l.socket.port();
            AutoServer {
                listening: l,
                port: p,
            }
        }
    }

    impl Drop for AutoServer {
        fn drop(&mut self) {
            // Workaround for https://github.com/hyperium/hyper/issues/338
            self.listening.close().unwrap();
        }
    }

    #[test]
    fn normal_request_possible() {
        let server = AutoServer::new();
        let client = Client::new();
        let res = client.get(&format!("http://127.0.0.1:{}/a", server.port)).send().unwrap();
        assert_eq!(res.status, status::ImATeapot);
    }

    fn to_string(res: &mut hyper::client::Response) -> String {
        let mut s = String::new();
        res.read_to_string(&mut s).unwrap();
        s
    }

    #[test]
    fn preflight_to_nonexistent_route_fails() {
        let server = AutoServer::new();
        let client = Client::new();
        let mut headers = Headers::new();
        headers.set(AccessControlRequestMethod(Method::Get));
        headers.set(Origin::from_str("http://www.a.com:8080").unwrap());
        let mut res = client.request(Method::Options,
                                     &format!("http://127.0.0.1:{}/b", server.port))
            .headers(headers)
            .send()
            .unwrap();
        assert_eq!(res.status, status::NotFound);
        assert_eq!(to_string(&mut res), "");
    }

    #[test]
    fn preflight_without_origin_is_bad_request() {
        let server = AutoServer::new();
        let client = Client::new();
        let mut headers = Headers::new();
        headers.set(AccessControlRequestMethod(Method::Get));
        let mut res = client.request(Method::Options,
                                     &format!("http://127.0.0.1:{}/a", server.port))
            .headers(headers)
            .send()
            .unwrap();
        assert_eq!(res.status, status::BadRequest);
        let mut payload = String::new();
        res.read_to_string(&mut payload).unwrap();
        assert_eq!(payload, "Preflight request without Origin header");
    }

    #[test]
    fn preflight_with_allowed_origin_sets_all_headers() {
        let server = AutoServer::new();
        let client = Client::new();
        let mut headers = Headers::new();
        headers.set(AccessControlRequestMethod(Method::Get));
        headers.set(Origin::from_str("http://www.a.com:8080").unwrap());
        let res = client.request(Method::Options,
                                 &format!("http://127.0.0.1:{}/a", server.port))
            .headers(headers)
            .send()
            .unwrap();
        assert_eq!(res.status, status::NoContent);
        let allow_origin = res.headers.get::<AccessControlAllowOrigin>().unwrap();
        assert_eq!(format!("{}", allow_origin), "http://www.a.com:8080");
        let allow_headers = res.headers.get::<AccessControlAllowHeaders>().unwrap();
        assert_eq!(format!("{}", allow_headers),
                   "Content-Type, X-Requested-With");
        let allow_methods = res.headers.get::<AccessControlAllowMethods>().unwrap();
        assert_eq!(format!("{}", allow_methods), "GET, PUT, POST");
        let max_age = res.headers.get::<AccessControlMaxAge>().unwrap();
        assert_eq!(max_age.0, 60 * 60u32);
    }

    #[test]
    fn preflight_with_disallowed_origin_is_error() {
        let mut cors = CorsMiddleware::new();
        let origins: HashSet<String> = vec!["http://www.a.com".to_owned()].into_iter().collect();
        cors.allowed_origins = AllowedOrigins::Specific(origins);
        let server = AutoServer::with_cors(cors);
        let client = Client::new();
        let mut headers = Headers::new();
        headers.set(AccessControlRequestMethod(Method::Get));
        headers.set(Origin::from_str("http://www.a.com:8080").unwrap());
        let res = client.request(Method::Options,
                                 &format!("http://127.0.0.1:{}/a", server.port))
            .headers(headers)
            .send()
            .unwrap();
        assert_eq!(res.status, status::NoContent);
        let allow_origin = res.headers.get::<AccessControlAllowOrigin>().unwrap();
        assert_eq!(format!("{}", allow_origin), "http://www.a.com:8080");
        let allow_headers = res.headers.get::<AccessControlAllowHeaders>().unwrap();
        assert_eq!(format!("{}", allow_headers),
                   "Content-Type, X-Requested-With");
        let allow_methods = res.headers.get::<AccessControlAllowMethods>().unwrap();
        assert_eq!(format!("{}", allow_methods), "GET, PUT, POST");
        let max_age = res.headers.get::<AccessControlMaxAge>().unwrap();
        assert_eq!(max_age.0, 60 * 60u32);
    }

    #[test]
    fn normal_request_allows_origin() {
        let server = AutoServer::new();
        let client = Client::new();
        let mut headers = Headers::new();
        headers.set(Origin::from_str("http://www.a.com:8080").unwrap());
        let res = client.get(&format!("http://127.0.0.1:{}/a", server.port))
            .headers(headers)
            .send()
            .unwrap();
        assert_eq!(res.status, status::ImATeapot);
        let allow_origin = res.headers.get::<AccessControlAllowOrigin>().unwrap();
        assert_eq!(format!("{}", allow_origin), "http://www.a.com:8080");
        assert!(res.headers.get::<AccessControlAllowHeaders>().is_none());
        assert!(res.headers.get::<AccessControlAllowMethods>().is_none());
        assert!(res.headers.get::<AccessControlMaxAge>().is_none());
    }

    #[test]
    fn normal_request_without_origin_is_passthrough() {
        let server = AutoServer::new();
        let client = Client::new();
        let res = client.get(&format!("http://127.0.0.1:{}/a", server.port)).send().unwrap();
        assert_eq!(res.status, status::ImATeapot);
        assert!(res.headers.get::<AccessControlAllowOrigin>().is_none());
        assert!(res.headers.get::<AccessControlAllowHeaders>().is_none());
        assert!(res.headers.get::<AccessControlAllowMethods>().is_none());
        assert!(res.headers.get::<AccessControlMaxAge>().is_none());
    }

}
<|endoftext|>


<commit_before>// Copyright 2017 The rust-jdbc developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// 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.

#![feature(associated_consts)]

extern crate jvm;

macro_rules! jvm_object {
  ($name:ident, $full_name:expr) => (
    pub struct $name(jvm::Object);

    impl java::lang::IObject for $name {
      const CLASS_NAME: &'static str = $full_name;

      fn from_jvm_object(object: jvm::Object) -> Self { return Self { 0: object }; }
      fn as_jvm_object(&self) -> &jvm::Object { return &self.0; }
    }

    impl std::fmt::Debug for $name {
      fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match self.to_string() {
          Ok(None) => write!(f, "{} {{ null }}", Self::CLASS_NAME),
          Ok(Some(s)) => write!(f, "{} {{ {} }}", Self::CLASS_NAME, s),
          Err(_) => write!(f, "{} {{ ERROR }}", Self::CLASS_NAME)
        }
      }
    }

    impl<T: java::lang::IObject> std::cmp::PartialEq<T> for $name {
      fn eq(&self, other: &T) -> bool {
        return self.equals(other).unwrap_or(false);
      }
    }
  )
}

macro_rules! jvm_call {
  (static, nonnull $rty:tt: $name:expr, $sig:expr, $args:expr) => ({
    let class = Self::jvm_class();
    let method = class.get_static_method($name, $sig).unwrap_or_else(|e| panic!("Could not find static method {:?}", java::lang::Throwable::from_jvm_object(e)));

    match unsafe { class.call_object_method(&method, $args) } {
      Ok(o) => Ok($rty::from_jvm_object(o.unwrap())),
      Err(e) => Err(java::lang::Throwable::from_jvm_object(e))
    }
  });
  (void: $s:expr, $name:expr, $sig:expr, $args:expr) => ({
    let method = Self::jvm_class().get_method($name, $sig).unwrap_or_else(|e| panic!("Could not find method {:?}", java::lang::Throwable::from_jvm_object(e)));

    match unsafe { $s.as_jvm_object().call_void_method(&method, $args) } {
      Ok(()) => Ok(()),
      Err(e) => Err(java::lang::Throwable::from_jvm_object(e))
    }
  });
  (bool: $s:expr, $name:expr, $sig:expr, $args:expr) => ({
    let method = Self::jvm_class().get_method($name, $sig).unwrap_or_else(|e| panic!("Could not find method {:?}", java::lang::Throwable::from_jvm_object(e)));

    match unsafe { $s.as_jvm_object().call_bool_method(&method, $args) } {
      Ok(b) => Ok(b),
      Err(e) => Err(java::lang::Throwable::from_jvm_object(e))
    }
  });
  (int: $s:expr, $name:expr, $sig:expr, $args:expr) => ({
    let method = Self::jvm_class().get_method($name, $sig).unwrap_or_else(|e| panic!("Could not find method {:?}", java::lang::Throwable::from_jvm_object(e)));

    match unsafe { $s.as_jvm_object().call_int_method(&method, $args) } {
      Ok(i) => Ok(i),
      Err(e) => Err(java::lang::Throwable::from_jvm_object(e))
    }
  });
  (string: $s:expr, $name:expr, $sig:expr, $args:expr) => ({
    let method = Self::jvm_class().get_method($name, $sig).unwrap_or_else(|e| panic!("Could not find method {:?}", java::lang::Throwable::from_jvm_object(e)));

    match unsafe { $s.as_jvm_object().call_object_method(&method, $args) } {
      Ok(o) => Ok(o.map({ |h| unsafe { jvm::String::from_object(h) }.to_string() })),
      Err(e) => Err(java::lang::Throwable::from_jvm_object(e))
    }
  });
  (nonnull $rty:tt: $s:expr, $name:expr, $sig:expr, $args:expr) => ({
    let method = Self::jvm_class().get_method($name, $sig).unwrap_or_else(|e| panic!("Could not find method {:?}", java::lang::Throwable::from_jvm_object(e)));

    match unsafe { $s.as_jvm_object().call_object_method(&method, $args) } {
      Ok(o) => Ok($rty::from_jvm_object(o.unwrap())),
      Err(e) => Err(java::lang::Throwable::from_jvm_object(e))
    }
  })
}

pub mod java;
<commit_msg>Remove old unstable feature<commit_after>// Copyright 2017 The rust-jdbc developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// 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 jvm;

macro_rules! jvm_object {
  ($name:ident, $full_name:expr) => (
    pub struct $name(jvm::Object);

    impl java::lang::IObject for $name {
      const CLASS_NAME: &'static str = $full_name;

      fn from_jvm_object(object: jvm::Object) -> Self { return Self { 0: object }; }
      fn as_jvm_object(&self) -> &jvm::Object { return &self.0; }
    }

    impl std::fmt::Debug for $name {
      fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match self.to_string() {
          Ok(None) => write!(f, "{} {{ null }}", Self::CLASS_NAME),
          Ok(Some(s)) => write!(f, "{} {{ {} }}", Self::CLASS_NAME, s),
          Err(_) => write!(f, "{} {{ ERROR }}", Self::CLASS_NAME)
        }
      }
    }

    impl<T: java::lang::IObject> std::cmp::PartialEq<T> for $name {
      fn eq(&self, other: &T) -> bool {
        return self.equals(other).unwrap_or(false);
      }
    }
  )
}

macro_rules! jvm_call {
  (static, nonnull $rty:tt: $name:expr, $sig:expr, $args:expr) => ({
    let class = Self::jvm_class();
    let method = class.get_static_method($name, $sig).unwrap_or_else(|e| panic!("Could not find static method {:?}", java::lang::Throwable::from_jvm_object(e)));

    match unsafe { class.call_object_method(&method, $args) } {
      Ok(o) => Ok($rty::from_jvm_object(o.unwrap())),
      Err(e) => Err(java::lang::Throwable::from_jvm_object(e))
    }
  });
  (void: $s:expr, $name:expr, $sig:expr, $args:expr) => ({
    let method = Self::jvm_class().get_method($name, $sig).unwrap_or_else(|e| panic!("Could not find method {:?}", java::lang::Throwable::from_jvm_object(e)));

    match unsafe { $s.as_jvm_object().call_void_method(&method, $args) } {
      Ok(()) => Ok(()),
      Err(e) => Err(java::lang::Throwable::from_jvm_object(e))
    }
  });
  (bool: $s:expr, $name:expr, $sig:expr, $args:expr) => ({
    let method = Self::jvm_class().get_method($name, $sig).unwrap_or_else(|e| panic!("Could not find method {:?}", java::lang::Throwable::from_jvm_object(e)));

    match unsafe { $s.as_jvm_object().call_bool_method(&method, $args) } {
      Ok(b) => Ok(b),
      Err(e) => Err(java::lang::Throwable::from_jvm_object(e))
    }
  });
  (int: $s:expr, $name:expr, $sig:expr, $args:expr) => ({
    let method = Self::jvm_class().get_method($name, $sig).unwrap_or_else(|e| panic!("Could not find method {:?}", java::lang::Throwable::from_jvm_object(e)));

    match unsafe { $s.as_jvm_object().call_int_method(&method, $args) } {
      Ok(i) => Ok(i),
      Err(e) => Err(java::lang::Throwable::from_jvm_object(e))
    }
  });
  (string: $s:expr, $name:expr, $sig:expr, $args:expr) => ({
    let method = Self::jvm_class().get_method($name, $sig).unwrap_or_else(|e| panic!("Could not find method {:?}", java::lang::Throwable::from_jvm_object(e)));

    match unsafe { $s.as_jvm_object().call_object_method(&method, $args) } {
      Ok(o) => Ok(o.map({ |h| unsafe { jvm::String::from_object(h) }.to_string() })),
      Err(e) => Err(java::lang::Throwable::from_jvm_object(e))
    }
  });
  (nonnull $rty:tt: $s:expr, $name:expr, $sig:expr, $args:expr) => ({
    let method = Self::jvm_class().get_method($name, $sig).unwrap_or_else(|e| panic!("Could not find method {:?}", java::lang::Throwable::from_jvm_object(e)));

    match unsafe { $s.as_jvm_object().call_object_method(&method, $args) } {
      Ok(o) => Ok($rty::from_jvm_object(o.unwrap())),
      Err(e) => Err(java::lang::Throwable::from_jvm_object(e))
    }
  })
}

pub mod java;
<|endoftext|>


<commit_before>extern crate byteorder;
extern crate crc;
extern crate futures;
extern crate futures_cpupool;
extern crate inflate;
#[macro_use]
extern crate slog;

use byteorder::ReadBytesExt;

use crc::crc32::Hasher32;

use futures::Future;

use std::collections::VecDeque;

use std::io::{
    BufRead,
    Error,
    Read,
    Seek,
    SeekFrom,
    Write
};

use std::str;

// 100 blocks of 64 kiB, even accounting for a huge overhead,
// is still less than 10 MiB, which is trivially manageable.
// Additionally, there's no chance that 100 threads or more
// give any speedup inflating blocks of at most 64 kiB.
const MAX_FUTURES: usize = 100;

const GZIP_IDENTIFIER: [u8; 2] = [0x1f, 0x8b];
const BGZF_IDENTIFIER: [u8; 2] = [0x42, 0x43];

const DEFLATE: u8 = 8;

const FEXTRA: u8 = 1 << 2;

pub fn version() -> &'static str {
    return option_env!("CARGO_PKG_VERSION").unwrap_or("unknown");
}

pub trait Rescuable: BufRead + Seek {}
impl<T: BufRead + Seek> Rescuable for T {}

struct BGZFBlock {
    header_bytes: Vec<u8>,
    deflated_payload_bytes: Vec<u8>,
    inflated_payload_crc32: u32,
    inflated_payload_size: u32,
}

struct BGZFBlockStatus {
    corrupted: bool,
    inflated_payload_size: u32,
}

pub struct Results {
    pub blocks_count: u64,
    pub blocks_size: u64,
    pub bad_blocks_count: u64,
    pub bad_blocks_size: u64,
    pub truncated_in_block: bool,
    pub truncated_between_blocks: bool,
}

fn check_payload(block: &Option<BGZFBlock>) -> Result<BGZFBlockStatus, Error> {
    match *block {
        None => Ok(BGZFBlockStatus {
            corrupted: false,
            inflated_payload_size: 0,
        }),
        Some(ref block) => {
            let inflated_payload_bytes = match inflate::inflate_bytes(&block.deflated_payload_bytes) {
                Ok(inflated_payload_bytes) => inflated_payload_bytes,
                Err(_) => return Ok(BGZFBlockStatus {
                    corrupted: true,
                    inflated_payload_size: block.inflated_payload_size,
                }),
            };

            let mut inflated_payload_digest = crc::crc32::Digest::new(crc::crc32::IEEE);
            inflated_payload_digest.write(&inflated_payload_bytes);
            let inflated_payload_crc32 = inflated_payload_digest.sum32();
            if inflated_payload_crc32 != block.inflated_payload_crc32 {
                return Ok(BGZFBlockStatus {
                    corrupted: true,
                    inflated_payload_size: block.inflated_payload_size,
                });
            }

            let inflated_payload_size = inflated_payload_bytes.len() as u32;
            if inflated_payload_size != block.inflated_payload_size {
                // TODO recoverable (wrong size is not a big issue if the CRC32 is correct)
                return Ok(BGZFBlockStatus {
                    corrupted: true,
                    inflated_payload_size: block.inflated_payload_size,
                });
            }

            Ok(BGZFBlockStatus {
                corrupted: false,
                inflated_payload_size: block.inflated_payload_size,
            })
        }
    }
}

macro_rules! fail {
    ($fail_fast: expr, $results: expr, $truncated_in_block: expr) => {
        $results.bad_blocks_count += 1;
        if $truncated_in_block {
            $results.truncated_in_block = true;
        }
        if $fail_fast {
            return $results;
        }
    }
}

pub fn check(reader: &mut Rescuable, fail_fast: bool, threads: usize, logger: &slog::Logger) -> Results {
    info!(logger, "Checking integrity…");

    let mut results = Results {
        blocks_count: 0u64,
        blocks_size: 0u64,
        bad_blocks_count: 0u64,
        bad_blocks_size: 0u64,
        truncated_in_block: false,
        truncated_between_blocks: false,
    };

    let pool;
    if threads == 0 {
        pool = futures_cpupool::CpuPool::new_num_cpus();
    } else {
        pool = futures_cpupool::CpuPool::new(threads);
    }

    let mut payload_status_futures = VecDeque::<futures_cpupool::CpuFuture<BGZFBlockStatus, Error>>::with_capacity(MAX_FUTURES);

    let mut previous_block: Option<BGZFBlock> = None;
    loop {
        if payload_status_futures.len() == MAX_FUTURES {
            let payload_status = payload_status_futures.pop_front().unwrap().wait().unwrap();
            if payload_status.corrupted {
                results.bad_blocks_size += payload_status.inflated_payload_size as u64;
                fail!(fail_fast, results, false);
            }
        }

        let mut header_bytes = vec![];
        {
            let mut header_reader = reader.take(12);
            match header_reader.read_to_end(&mut header_bytes) {
                Ok(header_size) => {
                    if header_size == 0 {
                        break;
                    }

                    if header_size < 12 {
                        fail!(fail_fast, results, true);
                        break;
                    }
                },
                Err(_) => {
                    fail!(fail_fast, results, true);
                    break;
                }
            }
        }

        let mut correct_bytes = 0;
        if header_bytes[0] == GZIP_IDENTIFIER[0] {
            correct_bytes += 1;
        }
        if header_bytes[1] == GZIP_IDENTIFIER[1] {
            correct_bytes += 1;
        }
        if header_bytes[2] == DEFLATE {
            correct_bytes += 1;
        }
        if header_bytes[3] == FEXTRA {
            correct_bytes += 1;
        }

        if correct_bytes < 4 {
            fail!(fail_fast, results, false);
            if correct_bytes == 3 {
                // single corrupted byte, can probably deal with it in place
                // TODO fix the four bytes for rescue
            } else {
                // mutliple corrupted bytes, safer to jump to the next block
                // TODO FIXME check the next bytes, and if not correct, jump to the next block
                panic!("Unexpected byte while checking header of block {}", results.blocks_count);
            }
        }

        // header_bytes[4..8] => modification time; can be anything
        // header_bytes[8] => extra flags; can be anything
        // header_bytes[9] => operating system; can be anything

        let extra_field_size = (&mut &header_bytes[10..12]).read_u16::<byteorder::LittleEndian>().unwrap();

        // TODO add the next extra_field_size bytes to header_bytes for rescue

        let mut bgzf_block_size = 0u16;

        let mut remaining_extra_field_size = extra_field_size;
        while remaining_extra_field_size > 4 {
            let mut extra_subfield_identifier = [0u8; 2];
            match reader.read_exact(&mut extra_subfield_identifier) {
                Ok(_) => (),
                Err(_) => {
                    fail!(fail_fast, results, true);
                    break;
                }
            }

            let extra_subfield_size = match reader.read_u16::<byteorder::LittleEndian>() {
                Ok(extra_subfield_size) => extra_subfield_size,
                Err(_) => {
                    fail!(fail_fast, results, true);
                    break;
                }
            };

            let mut correct_bytes = 0;
            if extra_subfield_identifier[0] == BGZF_IDENTIFIER[0] {
                correct_bytes += 1;
            }
            if extra_subfield_identifier[1] == BGZF_IDENTIFIER[1] {
                correct_bytes += 1;
            }
            if extra_subfield_size & 0xff == 2 {
                correct_bytes += 1;
            }
            if extra_subfield_size & 0xff00 == 0 {
                correct_bytes += 1;
            }

            if extra_subfield_size > remaining_extra_field_size - 4 {
                fail!(fail_fast, results, false);
                // TODO FIXME check the next bytes, and if not correct, jump to the next block
                panic!("Unexpected byte while checking header of block {}", results.blocks_count);
            }

            if correct_bytes == 4 ||
               (correct_bytes == 3 &&
                extra_field_size == 6) {
                if correct_bytes != 4 {
                    fail!(fail_fast, results, false);
                    // single corrupted byte, but most likely at the right place anyway
                    // TODO fix the four bytes for rescue
                }
                bgzf_block_size = match reader.read_u16::<byteorder::LittleEndian>() {
                    Ok(bgzf_block_size) => bgzf_block_size + 1,
                    Err(_) => {
                        fail!(fail_fast, results, true);
                        break;
                    }
                };
            } else {
                match reader.seek(SeekFrom::Current(extra_subfield_size as i64)) {
                    Ok(_) => (),
                    Err(_) => {
                        fail!(fail_fast, results, true);
                        break;
                    }
                }
            }

            remaining_extra_field_size -= 4 + extra_subfield_size;
        }

        if remaining_extra_field_size != 0 {
            fail!(fail_fast, results, false);
            // TODO FIXME check the next bytes, and if not correct, jump to the next block
            panic!("Unexpected byte while checking header of block {}", results.blocks_count);
        }

        if bgzf_block_size == 0u16 {
            fail!(fail_fast, results, false);
            // TODO FIXME check the next bytes, and if not correct, jump to the next block
            panic!("Unexpected byte while checking header of block {}", results.blocks_count);
        }

        // TODO if not at the right position for the next header, fix the previous header / payload or
        // the current header, seek to the right position and “continue”

        if threads == 1 {
            let payload_status = check_payload(&previous_block).unwrap();
            if payload_status.corrupted {
                results.bad_blocks_size += payload_status.inflated_payload_size as u64;
                fail!(fail_fast, results, false);
            }
        } else {
            let payload_status_future = pool.spawn_fn(move || {
                check_payload(&previous_block)
            });
            payload_status_futures.push_back(payload_status_future);
            previous_block = None;
        }

        if bgzf_block_size == 0 {
            bgzf_block_size = match reader.read_u16::<byteorder::LittleEndian>() {
                Ok(bgzf_block_size) => bgzf_block_size + 1,
                Err(_) => {
                    fail!(fail_fast, results, true);
                    break;
                }
            };
        }

        let mut deflated_payload_bytes = vec![];
        {
            let deflated_payload_size = bgzf_block_size - 20u16 - extra_field_size;
            let mut deflated_payload_reader = reader.take(deflated_payload_size as u64);
            match deflated_payload_reader.read_to_end(&mut deflated_payload_bytes) {
                Ok(deflated_payload_read_size) => {
                    if deflated_payload_read_size < deflated_payload_size as usize {
                        fail!(fail_fast, results, true);
                        break;
                    }
                },
                Err(_) => {
                    fail!(fail_fast, results, true);
                    break;
                }
            }
        }

        let inflated_payload_crc32 = match reader.read_u32::<byteorder::LittleEndian>() {
            Ok(inflated_payload_crc32) => inflated_payload_crc32,
            Err(_) => {
                fail!(fail_fast, results, true);
                break;
            }
        };
        let inflated_payload_size = match reader.read_u32::<byteorder::LittleEndian>() {
            Ok(inflated_payload_size) => inflated_payload_size,
            Err(_) => {
                fail!(fail_fast, results, true);
                break;
            }
        };

        previous_block = Some(BGZFBlock {
            header_bytes: header_bytes,
            deflated_payload_bytes: deflated_payload_bytes,
            inflated_payload_crc32: inflated_payload_crc32,
            inflated_payload_size: inflated_payload_size,
        });

        results.blocks_count += 1;
        results.blocks_size += inflated_payload_size as u64;
    }

    let mut last_inflated_payload_size = 0u32;
    if threads == 1 {
        let payload_status = check_payload(&previous_block).unwrap();
        if payload_status.corrupted {
            results.bad_blocks_size += payload_status.inflated_payload_size as u64;
            fail!(fail_fast, results, false);
        }
        last_inflated_payload_size = payload_status.inflated_payload_size;
    } else {
        let payload_status_future = pool.spawn_fn(move || {
            check_payload(&previous_block)
        });
        payload_status_futures.push_back(payload_status_future);
        for payload_status_future in payload_status_futures.iter_mut() {
            let payload_status = payload_status_future.wait().unwrap();
            if payload_status.corrupted {
                results.bad_blocks_size += payload_status.inflated_payload_size as u64;
                fail!(fail_fast, results, false);
            }
            last_inflated_payload_size = payload_status.inflated_payload_size;
        }
    }
    if last_inflated_payload_size != 0 {
        results.truncated_between_blocks = true;
        if fail_fast {
            return results;
        }
    }

    results
}

pub fn rescue(reader: &mut Rescuable, writer: &mut Write, logger: &slog::Logger) -> Result<(), Error> {
    info!(logger, "Rescuing file…");

    error!(logger, "bamrescue::rescue() is not yet implemented");
    unimplemented!();
}
<commit_msg>Remove dead code (not a special case anymore)<commit_after>extern crate byteorder;
extern crate crc;
extern crate futures;
extern crate futures_cpupool;
extern crate inflate;
#[macro_use]
extern crate slog;

use byteorder::ReadBytesExt;

use crc::crc32::Hasher32;

use futures::Future;

use std::collections::VecDeque;

use std::io::{
    BufRead,
    Error,
    Read,
    Seek,
    SeekFrom,
    Write
};

use std::str;

// 100 blocks of 64 kiB, even accounting for a huge overhead,
// is still less than 10 MiB, which is trivially manageable.
// Additionally, there's no chance that 100 threads or more
// give any speedup inflating blocks of at most 64 kiB.
const MAX_FUTURES: usize = 100;

const GZIP_IDENTIFIER: [u8; 2] = [0x1f, 0x8b];
const BGZF_IDENTIFIER: [u8; 2] = [0x42, 0x43];

const DEFLATE: u8 = 8;

const FEXTRA: u8 = 1 << 2;

pub fn version() -> &'static str {
    return option_env!("CARGO_PKG_VERSION").unwrap_or("unknown");
}

pub trait Rescuable: BufRead + Seek {}
impl<T: BufRead + Seek> Rescuable for T {}

struct BGZFBlock {
    header_bytes: Vec<u8>,
    deflated_payload_bytes: Vec<u8>,
    inflated_payload_crc32: u32,
    inflated_payload_size: u32,
}

struct BGZFBlockStatus {
    corrupted: bool,
    inflated_payload_size: u32,
}

pub struct Results {
    pub blocks_count: u64,
    pub blocks_size: u64,
    pub bad_blocks_count: u64,
    pub bad_blocks_size: u64,
    pub truncated_in_block: bool,
    pub truncated_between_blocks: bool,
}

fn check_payload(block: &Option<BGZFBlock>) -> Result<BGZFBlockStatus, Error> {
    match *block {
        None => Ok(BGZFBlockStatus {
            corrupted: false,
            inflated_payload_size: 0,
        }),
        Some(ref block) => {
            let inflated_payload_bytes = match inflate::inflate_bytes(&block.deflated_payload_bytes) {
                Ok(inflated_payload_bytes) => inflated_payload_bytes,
                Err(_) => return Ok(BGZFBlockStatus {
                    corrupted: true,
                    inflated_payload_size: block.inflated_payload_size,
                }),
            };

            let mut inflated_payload_digest = crc::crc32::Digest::new(crc::crc32::IEEE);
            inflated_payload_digest.write(&inflated_payload_bytes);
            let inflated_payload_crc32 = inflated_payload_digest.sum32();
            if inflated_payload_crc32 != block.inflated_payload_crc32 {
                return Ok(BGZFBlockStatus {
                    corrupted: true,
                    inflated_payload_size: block.inflated_payload_size,
                });
            }

            let inflated_payload_size = inflated_payload_bytes.len() as u32;
            if inflated_payload_size != block.inflated_payload_size {
                // TODO recoverable (wrong size is not a big issue if the CRC32 is correct)
                return Ok(BGZFBlockStatus {
                    corrupted: true,
                    inflated_payload_size: block.inflated_payload_size,
                });
            }

            Ok(BGZFBlockStatus {
                corrupted: false,
                inflated_payload_size: block.inflated_payload_size,
            })
        }
    }
}

macro_rules! fail {
    ($fail_fast: expr, $results: expr, $truncated_in_block: expr) => {
        $results.bad_blocks_count += 1;
        if $truncated_in_block {
            $results.truncated_in_block = true;
        }
        if $fail_fast {
            return $results;
        }
    }
}

pub fn check(reader: &mut Rescuable, fail_fast: bool, threads: usize, logger: &slog::Logger) -> Results {
    info!(logger, "Checking integrity…");

    let mut results = Results {
        blocks_count: 0u64,
        blocks_size: 0u64,
        bad_blocks_count: 0u64,
        bad_blocks_size: 0u64,
        truncated_in_block: false,
        truncated_between_blocks: false,
    };

    let pool;
    if threads == 0 {
        pool = futures_cpupool::CpuPool::new_num_cpus();
    } else {
        pool = futures_cpupool::CpuPool::new(threads);
    }

    let mut payload_status_futures = VecDeque::<futures_cpupool::CpuFuture<BGZFBlockStatus, Error>>::with_capacity(MAX_FUTURES);

    let mut previous_block: Option<BGZFBlock> = None;
    loop {
        if payload_status_futures.len() == MAX_FUTURES {
            let payload_status = payload_status_futures.pop_front().unwrap().wait().unwrap();
            if payload_status.corrupted {
                results.bad_blocks_size += payload_status.inflated_payload_size as u64;
                fail!(fail_fast, results, false);
            }
        }

        let mut header_bytes = vec![];
        {
            let mut header_reader = reader.take(12);
            match header_reader.read_to_end(&mut header_bytes) {
                Ok(header_size) => {
                    if header_size == 0 {
                        break;
                    }

                    if header_size < 12 {
                        fail!(fail_fast, results, true);
                        break;
                    }
                },
                Err(_) => {
                    fail!(fail_fast, results, true);
                    break;
                }
            }
        }

        let mut correct_bytes = 0;
        if header_bytes[0] == GZIP_IDENTIFIER[0] {
            correct_bytes += 1;
        }
        if header_bytes[1] == GZIP_IDENTIFIER[1] {
            correct_bytes += 1;
        }
        if header_bytes[2] == DEFLATE {
            correct_bytes += 1;
        }
        if header_bytes[3] == FEXTRA {
            correct_bytes += 1;
        }

        if correct_bytes < 4 {
            fail!(fail_fast, results, false);
            if correct_bytes == 3 {
                // single corrupted byte, can probably deal with it in place
                // TODO fix the four bytes for rescue
            } else {
                // mutliple corrupted bytes, safer to jump to the next block
                // TODO FIXME check the next bytes, and if not correct, jump to the next block
                panic!("Unexpected byte while checking header of block {}", results.blocks_count);
            }
        }

        // header_bytes[4..8] => modification time; can be anything
        // header_bytes[8] => extra flags; can be anything
        // header_bytes[9] => operating system; can be anything

        let extra_field_size = (&mut &header_bytes[10..12]).read_u16::<byteorder::LittleEndian>().unwrap();

        // TODO add the next extra_field_size bytes to header_bytes for rescue

        let mut bgzf_block_size = 0u16;

        let mut remaining_extra_field_size = extra_field_size;
        while remaining_extra_field_size > 4 {
            let mut extra_subfield_identifier = [0u8; 2];
            match reader.read_exact(&mut extra_subfield_identifier) {
                Ok(_) => (),
                Err(_) => {
                    fail!(fail_fast, results, true);
                    break;
                }
            }

            let extra_subfield_size = match reader.read_u16::<byteorder::LittleEndian>() {
                Ok(extra_subfield_size) => extra_subfield_size,
                Err(_) => {
                    fail!(fail_fast, results, true);
                    break;
                }
            };

            let mut correct_bytes = 0;
            if extra_subfield_identifier[0] == BGZF_IDENTIFIER[0] {
                correct_bytes += 1;
            }
            if extra_subfield_identifier[1] == BGZF_IDENTIFIER[1] {
                correct_bytes += 1;
            }
            if extra_subfield_size & 0xff == 2 {
                correct_bytes += 1;
            }
            if extra_subfield_size & 0xff00 == 0 {
                correct_bytes += 1;
            }

            if extra_subfield_size > remaining_extra_field_size - 4 {
                fail!(fail_fast, results, false);
                // TODO FIXME check the next bytes, and if not correct, jump to the next block
                panic!("Unexpected byte while checking header of block {}", results.blocks_count);
            }

            if correct_bytes == 4 ||
               (correct_bytes == 3 &&
                extra_field_size == 6) {
                if correct_bytes != 4 {
                    fail!(fail_fast, results, false);
                    // single corrupted byte, but most likely at the right place anyway
                    // TODO fix the four bytes for rescue
                }
                bgzf_block_size = match reader.read_u16::<byteorder::LittleEndian>() {
                    Ok(bgzf_block_size) => bgzf_block_size + 1,
                    Err(_) => {
                        fail!(fail_fast, results, true);
                        break;
                    }
                };
            } else {
                match reader.seek(SeekFrom::Current(extra_subfield_size as i64)) {
                    Ok(_) => (),
                    Err(_) => {
                        fail!(fail_fast, results, true);
                        break;
                    }
                }
            }

            remaining_extra_field_size -= 4 + extra_subfield_size;
        }

        if remaining_extra_field_size != 0 {
            fail!(fail_fast, results, false);
            // TODO FIXME check the next bytes, and if not correct, jump to the next block
            panic!("Unexpected byte while checking header of block {}", results.blocks_count);
        }

        if bgzf_block_size == 0u16 {
            fail!(fail_fast, results, false);
            // TODO FIXME check the next bytes, and if not correct, jump to the next block
            panic!("Unexpected byte while checking header of block {}", results.blocks_count);
        }

        // TODO if not at the right position for the next header, fix the previous header / payload or
        // the current header, seek to the right position and “continue”

        if threads == 1 {
            let payload_status = check_payload(&previous_block).unwrap();
            if payload_status.corrupted {
                results.bad_blocks_size += payload_status.inflated_payload_size as u64;
                fail!(fail_fast, results, false);
            }
        } else {
            let payload_status_future = pool.spawn_fn(move || {
                check_payload(&previous_block)
            });
            payload_status_futures.push_back(payload_status_future);
            previous_block = None;
        }

        let mut deflated_payload_bytes = vec![];
        {
            let deflated_payload_size = bgzf_block_size - 20u16 - extra_field_size;
            let mut deflated_payload_reader = reader.take(deflated_payload_size as u64);
            match deflated_payload_reader.read_to_end(&mut deflated_payload_bytes) {
                Ok(deflated_payload_read_size) => {
                    if deflated_payload_read_size < deflated_payload_size as usize {
                        fail!(fail_fast, results, true);
                        break;
                    }
                },
                Err(_) => {
                    fail!(fail_fast, results, true);
                    break;
                }
            }
        }

        let inflated_payload_crc32 = match reader.read_u32::<byteorder::LittleEndian>() {
            Ok(inflated_payload_crc32) => inflated_payload_crc32,
            Err(_) => {
                fail!(fail_fast, results, true);
                break;
            }
        };
        let inflated_payload_size = match reader.read_u32::<byteorder::LittleEndian>() {
            Ok(inflated_payload_size) => inflated_payload_size,
            Err(_) => {
                fail!(fail_fast, results, true);
                break;
            }
        };

        previous_block = Some(BGZFBlock {
            header_bytes: header_bytes,
            deflated_payload_bytes: deflated_payload_bytes,
            inflated_payload_crc32: inflated_payload_crc32,
            inflated_payload_size: inflated_payload_size,
        });

        results.blocks_count += 1;
        results.blocks_size += inflated_payload_size as u64;
    }

    let mut last_inflated_payload_size = 0u32;
    if threads == 1 {
        let payload_status = check_payload(&previous_block).unwrap();
        if payload_status.corrupted {
            results.bad_blocks_size += payload_status.inflated_payload_size as u64;
            fail!(fail_fast, results, false);
        }
        last_inflated_payload_size = payload_status.inflated_payload_size;
    } else {
        let payload_status_future = pool.spawn_fn(move || {
            check_payload(&previous_block)
        });
        payload_status_futures.push_back(payload_status_future);
        for payload_status_future in payload_status_futures.iter_mut() {
            let payload_status = payload_status_future.wait().unwrap();
            if payload_status.corrupted {
                results.bad_blocks_size += payload_status.inflated_payload_size as u64;
                fail!(fail_fast, results, false);
            }
            last_inflated_payload_size = payload_status.inflated_payload_size;
        }
    }
    if last_inflated_payload_size != 0 {
        results.truncated_between_blocks = true;
        if fail_fast {
            return results;
        }
    }

    results
}

pub fn rescue(reader: &mut Rescuable, writer: &mut Write, logger: &slog::Logger) -> Result<(), Error> {
    info!(logger, "Rescuing file…");

    error!(logger, "bamrescue::rescue() is not yet implemented");
    unimplemented!();
}
<|endoftext|>


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

//! A flexible structure for user interactions
//! to be used in window frameworks and widgets libraries.

pub mod keyboard;
pub mod mouse;

/// Models different kinds of buttons.
#[deriving(Clone, PartialEq, Eq, Show)]
pub enum Button {
    /// A keyboard button.
    Keyboard(keyboard::Key),
    /// A mouse button.
    Mouse(mouse::Button),
}

/// Models different kinds of motion.
#[deriving(Clone, Show)]
pub enum Motion {
    /// x and y in window coordinates.
    MouseCursor(f64, f64),
    /// x and y in relative coordinates.
    MouseRelative(f64, f64),
    /// x and y in scroll ticks.
    MouseScroll(f64, f64),
}

/// Models input events.
#[deriving(Clone, Show)]
pub enum InputEvent {
    /// Pressed a button.
    Press(Button),
    /// Released a button.
    Release(Button),
    /// Moved mouse cursor.
    Move(Motion),
    /// Text (usually from keyboard).
    Text(String),
    /// Window got resized.
    Resize(u32, u32),
    /// Window gained or lost focus.
    Focus(bool),
}

<commit_msg>Deriving PartialEq for InputEvent<commit_after>#![crate_name = "input"]
#![deny(missing_doc)]
#![feature(globs)]
#![feature(struct_variant)]

//! A flexible structure for user interactions
//! to be used in window frameworks and widgets libraries.

pub mod keyboard;
pub mod mouse;

/// Models different kinds of buttons.
#[deriving(Clone, PartialEq, Eq, Show)]
pub enum Button {
    /// A keyboard button.
    Keyboard(keyboard::Key),
    /// A mouse button.
    Mouse(mouse::Button),
}

/// Models different kinds of motion.
#[deriving(Clone, PartialEq, Show)]
pub enum Motion {
    /// x and y in window coordinates.
    MouseCursor(f64, f64),
    /// x and y in relative coordinates.
    MouseRelative(f64, f64),
    /// x and y in scroll ticks.
    MouseScroll(f64, f64),
}

/// Models input events.
#[deriving(Clone, PartialEq, Show)]
pub enum InputEvent {
    /// Pressed a button.
    Press(Button),
    /// Released a button.
    Release(Button),
    /// Moved mouse cursor.
    Move(Motion),
    /// Text (usually from keyboard).
    Text(String),
    /// Window got resized.
    Resize(u32, u32),
    /// Window gained or lost focus.
    Focus(bool),
}

<|endoftext|>


<commit_before>// Adapton for Rust
#![feature(associated_type_defaults)]
#![feature(custom_derive)]
#![feature(zero_one)]
#![feature(type_ascription)]

#![cfg_attr(test, feature(plugin))]
// #![cfg_attr(test, plugin(quickcheck_macros))]

#![crate_name = "adapton"]
#![crate_type = "lib"]

// extern crate quickcheck;
extern crate rand;

extern crate time;

#[macro_use]
extern crate log;

#[macro_use]
pub mod macros ;
pub mod engine ;
pub mod collections ;

mod adapton {
    pub use super::*;
}

<commit_msg>dropped unused features<commit_after>// Adapton for Rust

#![feature(zero_one)]
//#![feature(associated_type_defaults)]
//#![feature(custom_derive)]
//#![feature(type_ascription)]
//#![cfg_attr(test, feature(plugin))]
//#![cfg_attr(test, plugin(quickcheck_macros))]

#![crate_name = "adapton"]
#![crate_type = "lib"]

// extern crate quickcheck;
extern crate rand;

extern crate time;

#[macro_use]
extern crate log;

#[macro_use]
pub mod macros ;
pub mod engine ;
pub mod collections ;

mod adapton {
    pub use super::*;
}

<|endoftext|>


<commit_before>#![feature(backtrace)]

mod as_dyn_error;
mod context;
mod error;

#[cfg(not(feature = "std"))]
compile_error!("no_std support is not implemented yet");

pub use crate::as_dyn_error::AsDynError;
pub use crate::context::Context;
pub use crate::error::{Chain, Error};

/// `Result<T, Error>`
pub type Result<T> = std::result::Result<T, Error>;

/// Return early with an error.
///
/// This macro is equivalent to `return Err(From::from($err))`.
#[macro_export]
macro_rules! bail {
    ($err:expr) => {
        return std::result::Result::Err(std::convert::From::from($err));
    };
}

/// Construct an ad-hoc error from a string.
///
/// This evaluates to an `Error`. It can take either just a string, or a format
/// string with arguments. It also can take any custom type which implements
/// `Debug` and `Display`.
#[macro_export]
macro_rules! error {
    ($e:expr)   => { $crate::Error::new_adhoc($e) };
    ($($arg:tt)*) => { $crate::Error::new_adhoc(format!($($arg)*)) };
}
<commit_msg>Rename error macro to anyhow<commit_after>#![feature(backtrace)]

mod as_dyn_error;
mod context;
mod error;

#[cfg(not(feature = "std"))]
compile_error!("no_std support is not implemented yet");

pub use crate::as_dyn_error::AsDynError;
pub use crate::context::Context;
pub use crate::error::{Chain, Error};

/// `Result<T, Error>`
pub type Result<T> = std::result::Result<T, Error>;

/// Return early with an error.
///
/// This macro is equivalent to `return Err(From::from($err))`.
#[macro_export]
macro_rules! bail {
    ($err:expr) => {
        return std::result::Result::Err(std::convert::From::from($err));
    };
}

/// Construct an ad-hoc error from a string.
///
/// This evaluates to an `Error`. It can take either just a string, or a format
/// string with arguments. It also can take any custom type which implements
/// `Debug` and `Display`.
#[macro_export]
macro_rules! anyhow {
    ($e:expr)   => { $crate::Error::new_adhoc($e) };
    ($($arg:tt)*) => { $crate::Error::new_adhoc(format!($($arg)*)) };
}
<|endoftext|>