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

//! Functions for randomly accessing and sampling sequences.

use super::Rng;
use std::collections::hash_map::HashMap;

/// Randomly sample `amount` elements from a finite iterator.
///
/// The following can be returned:
/// - `Ok`: `Vec` of `amount` non-repeating randomly sampled elements. The order is not random.
/// - `Err`: `Vec` of all the elements from `iterable` in sequential order. This happens when the
///   length of `iterable` was less than `amount`. This is considered an error since exactly
///   `amount` elements is typically expected.
///
/// This implementation uses `O(len(iterable))` time and `O(amount)` memory.
///
/// # Example
///
/// ```rust
/// use rand::{thread_rng, seq};
///
/// let mut rng = thread_rng();
/// let sample = seq::sample_iter(&mut rng, 1..100, 5).unwrap();
/// println!("{:?}", sample);
/// ```
pub fn sample_iter<T, I, R>(rng: &mut R, iterable: I, amount: usize) -> Result<Vec<T>, Vec<T>>
    where I: IntoIterator<Item=T>,
          R: Rng,
{
    let mut iter = iterable.into_iter();
    let mut reservoir = Vec::with_capacity(amount);
    reservoir.extend(iter.by_ref().take(amount));

    // Continue unless the iterator was exhausted
    //
    // note: this prevents iterators that "restart" from causing problems.
    // If the iterator stops once, then so do we.
    if reservoir.len() == amount {
        for (i, elem) in iter.enumerate() {
            let k = rng.gen_range(0, i + 1 + amount);
            if let Some(spot) = reservoir.get_mut(k) {
                *spot = elem;
            }
        }
        Ok(reservoir)
    } else {
        // Don't hang onto extra memory. There is a corner case where
        // `amount` was much less than `len(iterable)`.
        reservoir.shrink_to_fit();
        Err(reservoir)
    }
}

/// Randomly sample exactly `amount` values from `slice`.
///
/// The values are non-repeating and in random order.
///
/// This implementation uses `O(amount)` time and memory.
///
/// Panics if `amount > slice.len()`
///
/// # Example
///
/// ```rust
/// use rand::{thread_rng, seq};
///
/// let mut rng = thread_rng();
/// let values = vec![5, 6, 1, 3, 4, 6, 7];
/// println!("{:?}", seq::sample_slice(&mut rng, &values, 3));
/// ```
pub fn sample_slice<R, T>(rng: &mut R, slice: &[T], amount: usize) -> Vec<T>
    where R: Rng,
          T: Clone
{
    let indices = sample_indices(rng, slice.len(), amount);

    let mut out = Vec::with_capacity(amount);
    out.extend(indices.iter().map(|i| slice[*i].clone()));
    out
}

/// Randomly sample exactly `amount` references from `slice`.
///
/// The references are non-repeating and in random order.
///
/// This implementation uses `O(amount)` time and memory.
///
/// Panics if `amount > slice.len()`
///
/// # Example
///
/// ```rust
/// use rand::{thread_rng, seq};
///
/// let mut rng = thread_rng();
/// let values = vec![5, 6, 1, 3, 4, 6, 7];
/// println!("{:?}", seq::sample_slice_ref(&mut rng, &values, 3));
/// ```
pub fn sample_slice_ref<'a, R, T>(rng: &mut R, slice: &'a [T], amount: usize) -> Vec<&'a T>
    where R: Rng
{
    let indices = sample_indices(rng, slice.len(), amount);

    let mut out = Vec::with_capacity(amount);
    out.extend(indices.iter().map(|i| &slice[*i]));
    out
}

/// Randomly sample exactly `amount` indices from `0..length`.
///
/// The values are non-repeating and in random order.
///
/// This implementation uses `O(amount)` time and memory.
///
/// This method is used internally by the slice sampling methods, but it can sometimes be useful to
/// have the indices themselves so this is provided as an alternative.
///
/// Panics if `amount > length`
pub fn sample_indices<R>(rng: &mut R, length: usize, amount: usize) -> Vec<usize>
    where R: Rng,
{
    if amount > length {
        panic!("`amount` must be less than or equal to `slice.len()`");
    }

    // We are going to have to allocate at least `amount` for the output no matter what. However,
    // if we use the `cached` version we will have to allocate `amount` as a HashMap as well since
    // it inserts an element for every loop.
    //
    // Therefore, if `amount >= length / 2` then inplace will be both faster and use less memory.
    //
    // TODO: there is probably even more fine-tuning that can be done here since
    // `HashMap::with_capacity(amount)` probably allocates more than `amount` in practice,
    // and a trade off could probably be made between memory/cpu, since hashmap operations
    // are slower than array index swapping.
    if amount >= length / 2 {
        sample_indices_inplace(rng, length, amount)
    } else {
        sample_indices_cache(rng, length, amount)
    }
}

/// Sample an amount of indices using an inplace partial fisher yates method.
///
/// This allocates the entire `length` of indices and randomizes only the first `amount`.
/// It then truncates to `amount` and returns.
///
/// This is better than using a HashMap "cache" when `amount >= length / 2` since it does not
/// require allocating an extra cache and is much faster.
fn sample_indices_inplace<R>(rng: &mut R, length: usize, amount: usize) -> Vec<usize>
    where R: Rng,
{
    debug_assert!(amount <= length);
    let mut indices: Vec<usize> = Vec::with_capacity(length);
    indices.extend(0..length);
    for i in 0..amount {
        let j: usize = rng.gen_range(i, length);
        let tmp = indices[i];
        indices[i] = indices[j];
        indices[j] = tmp;
    }
    indices.truncate(amount);
    debug_assert_eq!(indices.len(), amount);
    indices
}

/// This method performs a partial fisher-yates on a range of indices using a HashMap
/// as a cache to record potential collisions.
///
/// The cache avoids allocating the entire `length` of values. This is especially useful when
/// `amount <<< length`, i.e. select 3 non-repeating from 1_000_000
fn sample_indices_cache<R>(
    rng: &mut R,
    length: usize,
    amount: usize,
) -> Vec<usize>
    where R: Rng,
{
    debug_assert!(amount <= length);
    let mut cache = HashMap::with_capacity(amount);
    let mut out = Vec::with_capacity(amount);
    for i in 0..amount {
        let j: usize = rng.gen_range(i, length);

        // equiv: let tmp = slice[i];
        let tmp = match cache.get(&i) {
            Some(e) => *e,
            None => i,
        };

        // equiv: slice[i] = slice[j];
        let x = match cache.get(&j) {
            Some(x) => *x,
            None => j,
        };

        // equiv: slice[j] = tmp;
        cache.insert(j, tmp);

        // note that in the inplace version, slice[i] is automatically "returned" value
        out.push(x);
    }
    debug_assert_eq!(out.len(), amount);
    out
}

#[cfg(test)]
mod test {
    use super::*;
    use {thread_rng, XorShiftRng, SeedableRng};

    #[test]
    fn test_sample_iter() {
        let min_val = 1;
        let max_val = 100;

        let mut r = thread_rng();
        let vals = (min_val..max_val).collect::<Vec<i32>>();
        let small_sample = sample_iter(&mut r, vals.iter(), 5).unwrap();
        let large_sample = sample_iter(&mut r, vals.iter(), vals.len() + 5).unwrap_err();

        assert_eq!(small_sample.len(), 5);
        assert_eq!(large_sample.len(), vals.len());
        // no randomization happens when amount >= len
        assert_eq!(large_sample, vals.iter().collect::<Vec<_>>());

        assert!(small_sample.iter().all(|e| {
            **e >= min_val && **e <= max_val
        }));
    }
    #[test]
    fn test_sample_slice_boundaries() {
        let empty: &[u8] = &[];

        let mut r = thread_rng();

        // sample 0 items
        assert_eq!(sample_slice(&mut r, empty, 0), vec![]);
        assert_eq!(sample_slice(&mut r, &[42, 2, 42], 0), vec![]);

        // sample 1 item
        assert_eq!(sample_slice(&mut r, &[42], 1), vec![42]);
        let v = sample_slice(&mut r, &[1, 42], 1)[0];
        assert!(v == 1 || v == 42);

        // sample "all" the items
        let v = sample_slice(&mut r, &[42, 133], 2);
        assert!(v == vec![42, 133] || v == vec![133, 42]);

        assert_eq!(sample_indices_inplace(&mut r, 0, 0), vec![]);
        assert_eq!(sample_indices_inplace(&mut r, 1, 0), vec![]);
        assert_eq!(sample_indices_inplace(&mut r, 1, 1), vec![0]);

        assert_eq!(sample_indices_cache(&mut r, 0, 0), vec![]);
        assert_eq!(sample_indices_cache(&mut r, 1, 0), vec![]);
        assert_eq!(sample_indices_cache(&mut r, 1, 1), vec![0]);

        // Make sure lucky 777's aren't lucky
        let slice = &[42, 777];
        let mut num_42 = 0;
        let total = 1000;
        for _ in 0..total {
            let v = sample_slice(&mut r, slice, 1);
            assert_eq!(v.len(), 1);
            let v = v[0];
            assert!(v == 42 || v == 777);
            if v == 42 {
                num_42 += 1;
            }
        }
        let ratio_42 = num_42 as f64 / 1000 as f64;
        assert!(0.4 <= ratio_42 || ratio_42 <= 0.6, "{}", ratio_42);
    }

    #[test]
    fn test_sample_slice() {
        let xor_rng = XorShiftRng::from_seed;

        let max_range = 100;
        let mut r = thread_rng();

        for length in 1usize..max_range {
            let amount = r.gen_range(0, length);
            let seed: [u32; 4] = [
                r.next_u32(), r.next_u32(), r.next_u32(), r.next_u32()
            ];

            println!("Selecting indices: len={}, amount={}, seed={:?}", length, amount, seed);

            // assert that the two index methods give exactly the same result
            let inplace = sample_indices_inplace(
                &mut xor_rng(seed), length, amount);
            let cache = sample_indices_cache(
                &mut xor_rng(seed), length, amount);
            assert_eq!(inplace, cache);

            // assert the basics work
            let regular = sample_indices(
                &mut xor_rng(seed), length, amount);
            assert_eq!(regular.len(), amount);
            assert!(regular.iter().all(|e| *e < length));
            assert_eq!(regular, inplace);

            // also test that sampling the slice works
            let vec: Vec<usize> = (0..length).collect();
            {
                let result = sample_slice(&mut xor_rng(seed), &vec, amount);
                assert_eq!(result, regular);
            }

            {
                let result = sample_slice_ref(&mut xor_rng(seed), &vec, amount);
                let expected = regular.iter().map(|v| v).collect::<Vec<_>>();
                assert_eq!(result, expected);
            }
        }
    }
}
<commit_msg>seq::sample_indices: adjust factor, according to benchmark results<commit_after>// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Functions for randomly accessing and sampling sequences.

use super::Rng;
use std::collections::hash_map::HashMap;

/// Randomly sample `amount` elements from a finite iterator.
///
/// The following can be returned:
/// - `Ok`: `Vec` of `amount` non-repeating randomly sampled elements. The order is not random.
/// - `Err`: `Vec` of all the elements from `iterable` in sequential order. This happens when the
///   length of `iterable` was less than `amount`. This is considered an error since exactly
///   `amount` elements is typically expected.
///
/// This implementation uses `O(len(iterable))` time and `O(amount)` memory.
///
/// # Example
///
/// ```rust
/// use rand::{thread_rng, seq};
///
/// let mut rng = thread_rng();
/// let sample = seq::sample_iter(&mut rng, 1..100, 5).unwrap();
/// println!("{:?}", sample);
/// ```
pub fn sample_iter<T, I, R>(rng: &mut R, iterable: I, amount: usize) -> Result<Vec<T>, Vec<T>>
    where I: IntoIterator<Item=T>,
          R: Rng,
{
    let mut iter = iterable.into_iter();
    let mut reservoir = Vec::with_capacity(amount);
    reservoir.extend(iter.by_ref().take(amount));

    // Continue unless the iterator was exhausted
    //
    // note: this prevents iterators that "restart" from causing problems.
    // If the iterator stops once, then so do we.
    if reservoir.len() == amount {
        for (i, elem) in iter.enumerate() {
            let k = rng.gen_range(0, i + 1 + amount);
            if let Some(spot) = reservoir.get_mut(k) {
                *spot = elem;
            }
        }
        Ok(reservoir)
    } else {
        // Don't hang onto extra memory. There is a corner case where
        // `amount` was much less than `len(iterable)`.
        reservoir.shrink_to_fit();
        Err(reservoir)
    }
}

/// Randomly sample exactly `amount` values from `slice`.
///
/// The values are non-repeating and in random order.
///
/// This implementation uses `O(amount)` time and memory.
///
/// Panics if `amount > slice.len()`
///
/// # Example
///
/// ```rust
/// use rand::{thread_rng, seq};
///
/// let mut rng = thread_rng();
/// let values = vec![5, 6, 1, 3, 4, 6, 7];
/// println!("{:?}", seq::sample_slice(&mut rng, &values, 3));
/// ```
pub fn sample_slice<R, T>(rng: &mut R, slice: &[T], amount: usize) -> Vec<T>
    where R: Rng,
          T: Clone
{
    let indices = sample_indices(rng, slice.len(), amount);

    let mut out = Vec::with_capacity(amount);
    out.extend(indices.iter().map(|i| slice[*i].clone()));
    out
}

/// Randomly sample exactly `amount` references from `slice`.
///
/// The references are non-repeating and in random order.
///
/// This implementation uses `O(amount)` time and memory.
///
/// Panics if `amount > slice.len()`
///
/// # Example
///
/// ```rust
/// use rand::{thread_rng, seq};
///
/// let mut rng = thread_rng();
/// let values = vec![5, 6, 1, 3, 4, 6, 7];
/// println!("{:?}", seq::sample_slice_ref(&mut rng, &values, 3));
/// ```
pub fn sample_slice_ref<'a, R, T>(rng: &mut R, slice: &'a [T], amount: usize) -> Vec<&'a T>
    where R: Rng
{
    let indices = sample_indices(rng, slice.len(), amount);

    let mut out = Vec::with_capacity(amount);
    out.extend(indices.iter().map(|i| &slice[*i]));
    out
}

/// Randomly sample exactly `amount` indices from `0..length`.
///
/// The values are non-repeating and in random order.
///
/// This implementation uses `O(amount)` time and memory.
///
/// This method is used internally by the slice sampling methods, but it can sometimes be useful to
/// have the indices themselves so this is provided as an alternative.
///
/// Panics if `amount > length`
pub fn sample_indices<R>(rng: &mut R, length: usize, amount: usize) -> Vec<usize>
    where R: Rng,
{
    if amount > length {
        panic!("`amount` must be less than or equal to `slice.len()`");
    }

    // We are going to have to allocate at least `amount` for the output no matter what. However,
    // if we use the `cached` version we will have to allocate `amount` as a HashMap as well since
    // it inserts an element for every loop.
    //
    // Therefore, if `amount >= length / 2` then inplace will be both faster and use less memory.
    // In fact, benchmarks show the inplace version is faster for length up to about 20 times
    // faster than amount.
    //
    // TODO: there is probably even more fine-tuning that can be done here since
    // `HashMap::with_capacity(amount)` probably allocates more than `amount` in practice,
    // and a trade off could probably be made between memory/cpu, since hashmap operations
    // are slower than array index swapping.
    if amount * 20 >= length {
        sample_indices_inplace(rng, length, amount)
    } else {
        sample_indices_cache(rng, length, amount)
    }
}

/// Sample an amount of indices using an inplace partial fisher yates method.
///
/// This allocates the entire `length` of indices and randomizes only the first `amount`.
/// It then truncates to `amount` and returns.
///
/// This is better than using a HashMap "cache" when `amount >= length / 2` since it does not
/// require allocating an extra cache and is much faster.
fn sample_indices_inplace<R>(rng: &mut R, length: usize, amount: usize) -> Vec<usize>
    where R: Rng,
{
    debug_assert!(amount <= length);
    let mut indices: Vec<usize> = Vec::with_capacity(length);
    indices.extend(0..length);
    for i in 0..amount {
        let j: usize = rng.gen_range(i, length);
        let tmp = indices[i];
        indices[i] = indices[j];
        indices[j] = tmp;
    }
    indices.truncate(amount);
    debug_assert_eq!(indices.len(), amount);
    indices
}

/// This method performs a partial fisher-yates on a range of indices using a HashMap
/// as a cache to record potential collisions.
///
/// The cache avoids allocating the entire `length` of values. This is especially useful when
/// `amount <<< length`, i.e. select 3 non-repeating from 1_000_000
fn sample_indices_cache<R>(
    rng: &mut R,
    length: usize,
    amount: usize,
) -> Vec<usize>
    where R: Rng,
{
    debug_assert!(amount <= length);
    let mut cache = HashMap::with_capacity(amount);
    let mut out = Vec::with_capacity(amount);
    for i in 0..amount {
        let j: usize = rng.gen_range(i, length);

        // equiv: let tmp = slice[i];
        let tmp = match cache.get(&i) {
            Some(e) => *e,
            None => i,
        };

        // equiv: slice[i] = slice[j];
        let x = match cache.get(&j) {
            Some(x) => *x,
            None => j,
        };

        // equiv: slice[j] = tmp;
        cache.insert(j, tmp);

        // note that in the inplace version, slice[i] is automatically "returned" value
        out.push(x);
    }
    debug_assert_eq!(out.len(), amount);
    out
}

#[cfg(test)]
mod test {
    use super::*;
    use {thread_rng, XorShiftRng, SeedableRng};

    #[test]
    fn test_sample_iter() {
        let min_val = 1;
        let max_val = 100;

        let mut r = thread_rng();
        let vals = (min_val..max_val).collect::<Vec<i32>>();
        let small_sample = sample_iter(&mut r, vals.iter(), 5).unwrap();
        let large_sample = sample_iter(&mut r, vals.iter(), vals.len() + 5).unwrap_err();

        assert_eq!(small_sample.len(), 5);
        assert_eq!(large_sample.len(), vals.len());
        // no randomization happens when amount >= len
        assert_eq!(large_sample, vals.iter().collect::<Vec<_>>());

        assert!(small_sample.iter().all(|e| {
            **e >= min_val && **e <= max_val
        }));
    }
    #[test]
    fn test_sample_slice_boundaries() {
        let empty: &[u8] = &[];

        let mut r = thread_rng();

        // sample 0 items
        assert_eq!(sample_slice(&mut r, empty, 0), vec![]);
        assert_eq!(sample_slice(&mut r, &[42, 2, 42], 0), vec![]);

        // sample 1 item
        assert_eq!(sample_slice(&mut r, &[42], 1), vec![42]);
        let v = sample_slice(&mut r, &[1, 42], 1)[0];
        assert!(v == 1 || v == 42);

        // sample "all" the items
        let v = sample_slice(&mut r, &[42, 133], 2);
        assert!(v == vec![42, 133] || v == vec![133, 42]);

        assert_eq!(sample_indices_inplace(&mut r, 0, 0), vec![]);
        assert_eq!(sample_indices_inplace(&mut r, 1, 0), vec![]);
        assert_eq!(sample_indices_inplace(&mut r, 1, 1), vec![0]);

        assert_eq!(sample_indices_cache(&mut r, 0, 0), vec![]);
        assert_eq!(sample_indices_cache(&mut r, 1, 0), vec![]);
        assert_eq!(sample_indices_cache(&mut r, 1, 1), vec![0]);

        // Make sure lucky 777's aren't lucky
        let slice = &[42, 777];
        let mut num_42 = 0;
        let total = 1000;
        for _ in 0..total {
            let v = sample_slice(&mut r, slice, 1);
            assert_eq!(v.len(), 1);
            let v = v[0];
            assert!(v == 42 || v == 777);
            if v == 42 {
                num_42 += 1;
            }
        }
        let ratio_42 = num_42 as f64 / 1000 as f64;
        assert!(0.4 <= ratio_42 || ratio_42 <= 0.6, "{}", ratio_42);
    }

    #[test]
    fn test_sample_slice() {
        let xor_rng = XorShiftRng::from_seed;

        let max_range = 100;
        let mut r = thread_rng();

        for length in 1usize..max_range {
            let amount = r.gen_range(0, length);
            let seed: [u32; 4] = [
                r.next_u32(), r.next_u32(), r.next_u32(), r.next_u32()
            ];

            println!("Selecting indices: len={}, amount={}, seed={:?}", length, amount, seed);

            // assert that the two index methods give exactly the same result
            let inplace = sample_indices_inplace(
                &mut xor_rng(seed), length, amount);
            let cache = sample_indices_cache(
                &mut xor_rng(seed), length, amount);
            assert_eq!(inplace, cache);

            // assert the basics work
            let regular = sample_indices(
                &mut xor_rng(seed), length, amount);
            assert_eq!(regular.len(), amount);
            assert!(regular.iter().all(|e| *e < length));
            assert_eq!(regular, inplace);

            // also test that sampling the slice works
            let vec: Vec<usize> = (0..length).collect();
            {
                let result = sample_slice(&mut xor_rng(seed), &vec, amount);
                assert_eq!(result, regular);
            }

            {
                let result = sample_slice_ref(&mut xor_rng(seed), &vec, amount);
                let expected = regular.iter().map(|v| v).collect::<Vec<_>>();
                assert_eq!(result, expected);
            }
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2018 Syn Developers
//
// 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 proc_macro2;
extern crate syn;
extern crate syntax;
extern crate syntax_pos;

use self::syntax::ast;
use self::syntax::codemap::FilePathMapping;
use self::syntax::parse::{self, ParseSess};
use self::syntax::ptr::P;
use self::syntax_pos::edition::Edition;
use self::syntax_pos::{hygiene, FileName};

use std::panic;

use self::syn::buffer::TokenBuffer;
use self::syn::synom::Synom;

pub fn libsyntax_expr(input: &str) -> Option<P<ast::Expr>> {
    match panic::catch_unwind(|| {
        hygiene::set_default_edition(Edition::Edition2018);
        let sess = ParseSess::new(FilePathMapping::empty());
        sess.span_diagnostic.set_continue_after_error(false);
        let e = parse::new_parser_from_source_str(
            &sess,
            FileName::Custom("test_precedence".to_string()),
            input.to_string(),
        ).parse_expr();
        Some(match e {
            Ok(expr) => expr,
            Err(mut diagnostic) => {
                diagnostic.emit();;
                return None;
            }
        })
    }) {
        Ok(Some(e)) => Some(e),
        Ok(None) => None,
        Err(_) => {
            errorf!("libsyntax panicked\n");
            None
        }
    }
}

pub fn syn_expr(input: &str) -> Option<syn::Expr> {
    match syn::parse_str(input) {
        Ok(e) => Some(e),
        Err(msg) => {
            errorf!("syn failed to parse\n{:?}\n", msg);
            None
        }
    }
}

pub fn syn<T: Synom>(tokens: proc_macro2::TokenStream) -> T {
    let buf = TokenBuffer::new2(tokens);
    let result = T::parse(buf.begin());
    match result {
        Ok((t, rest)) => {
            if rest.eof() {
                t
            } else if rest == buf.begin() {
                panic!("failed to parse anything")
            } else {
                panic!("failed to parse all tokens")
            }
        }
        Err(err) => panic!("failed to parse: {}", err),
    }
}
<commit_msg>Clean up confusing match-expression<commit_after>// Copyright 2018 Syn Developers
//
// 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 proc_macro2;
extern crate syn;
extern crate syntax;
extern crate syntax_pos;

use self::syntax::ast;
use self::syntax::codemap::FilePathMapping;
use self::syntax::parse::{self, ParseSess};
use self::syntax::ptr::P;
use self::syntax_pos::edition::Edition;
use self::syntax_pos::{hygiene, FileName};

use std::panic;

use self::syn::buffer::TokenBuffer;
use self::syn::synom::Synom;

pub fn libsyntax_expr(input: &str) -> Option<P<ast::Expr>> {
    match panic::catch_unwind(|| {
        hygiene::set_default_edition(Edition::Edition2018);
        let sess = ParseSess::new(FilePathMapping::empty());
        sess.span_diagnostic.set_continue_after_error(false);
        let e = parse::new_parser_from_source_str(
            &sess,
            FileName::Custom("test_precedence".to_string()),
            input.to_string(),
        ).parse_expr();
        match e {
            Ok(expr) => Some(expr),
            Err(mut diagnostic) => {
                diagnostic.emit();
                None
            }
        }
    }) {
        Ok(Some(e)) => Some(e),
        Ok(None) => None,
        Err(_) => {
            errorf!("libsyntax panicked\n");
            None
        }
    }
}

pub fn syn_expr(input: &str) -> Option<syn::Expr> {
    match syn::parse_str(input) {
        Ok(e) => Some(e),
        Err(msg) => {
            errorf!("syn failed to parse\n{:?}\n", msg);
            None
        }
    }
}

pub fn syn<T: Synom>(tokens: proc_macro2::TokenStream) -> T {
    let buf = TokenBuffer::new2(tokens);
    let result = T::parse(buf.begin());
    match result {
        Ok((t, rest)) => {
            if rest.eof() {
                t
            } else if rest == buf.begin() {
                panic!("failed to parse anything")
            } else {
                panic!("failed to parse all tokens")
            }
        }
        Err(err) => panic!("failed to parse: {}", err),
    }
}
<|endoftext|>


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

extern crate cgmath;
#[macro_use]
extern crate gfx;
extern crate gfx_core;
extern crate gfx_window_glutin;
extern crate glutin;
extern crate gfx_gl as gl;
extern crate gfx_device_gl;

use cgmath::{SquareMatrix, Matrix, Point3, Vector3, Matrix3, Matrix4};
use cgmath::{Transform, Vector4};
pub use gfx::format::{DepthStencil, Rgba8 as ColorFormat};
//use glfw::Context;
use gl::Gl;
use gl::types::*;
use std::mem;
use std::ptr;
use std::str;
use std::env;
use std::str::FromStr;
use std::iter::repeat;
use std::ffi::CString;
use std::time::{Duration, Instant};
use gfx_device_gl::{Resources as R, CommandBuffer as CB};
use gfx_core::Device;

gfx_defines!{
    vertex Vertex {
        pos: [f32; 3] = "a_Pos",
    }

    pipeline pipe {
        vbuf: gfx::VertexBuffer<Vertex> = (),
        transform: gfx::Global<[[f32; 4]; 4]> = "u_Transform",
        out_color: gfx::RenderTarget<ColorFormat> = "o_Color",
    }
}

static VERTEX_SRC: &'static [u8] = b"
    #version 150 core
    in vec3 a_Pos;
    uniform mat4 u_Transform;

    void main() {
        gl_Position = u_Transform * vec4(a_Pos, 1.0);
   }
";

static FRAGMENT_SRC: &'static [u8] = b"
    #version 150 core
    out vec4 o_Color;

    void main() {
        o_Color = vec4(1.0, 0.0, 0.0, 1.0);
    }
";

static VERTEX_DATA: &'static [Vertex] = &[
    Vertex { pos: [-1.0, 0.0, -1.0] },
    Vertex { pos: [ 1.0, 0.0, -1.0] },
    Vertex { pos: [-1.0, 0.0,  1.0] },
];

const CLEAR_COLOR: (f32, f32, f32, f32) = (0.3, 0.3, 0.3, 1.0);

//----------------------------------------

fn transform(x: i16, y: i16, proj_view: &Matrix4<f32>) -> Matrix4<f32> {
    let mut model = Matrix4::from(Matrix3::identity() * 0.05);
    model.w = Vector4::new(x as f32 * 0.10,
                           0f32,
                           y as f32 * 0.10,
                           1f32);
    proj_view * model
}

trait Renderer: Drop {
    fn render(&mut self, proj_view: &Matrix4<f32>);
    fn window(&mut self) -> &glutin::Window;
}

struct GFX {
    dimension: i16,
    window: glutin::Window,
    device:gfx_device_gl::Device,
    encoder: gfx::Encoder<R,CB>,
    data: pipe::Data<R>,
    pso: gfx::PipelineState<R, pipe::Meta>,
    slice: gfx::Slice<R>,
}

struct GL {
    dimension: i16,
    window: glutin::Window,
    gl:Gl,
    trans_uniform:GLint,
    vs:GLuint,
    fs:GLuint,
    program:GLuint,
    vbo:GLuint,
    vao:GLuint,
}

impl GFX {
    fn new(builder: glutin::WindowBuilder, dimension: i16) -> Self {
        use gfx::traits::FactoryExt;

        let (window, device, mut factory, main_color, _) =
            gfx_window_glutin::init::<ColorFormat, DepthStencil>(builder);
        let encoder: gfx::Encoder<_,_> = factory.create_command_buffer().into();

        let pso = factory.create_pipeline_simple(
            VERTEX_SRC, FRAGMENT_SRC,
            pipe::new()
        ).unwrap();

        let (vbuf, slice) = factory.create_vertex_buffer_with_slice(VERTEX_DATA,());

        let data = pipe::Data {
            vbuf: vbuf,
            transform: cgmath::Matrix4::identity().into(),
            out_color: main_color,
        };

        GFX {
            window: window,
            dimension: dimension,
            device: device,
            encoder: encoder,
            data: data,
            pso: pso,
            slice: slice,
        }
    }
}

fn duration_to_f64(dur: Duration) -> f64 {
   dur.as_secs() as f64 + dur.subsec_nanos() as f64 / 1000_000_000.0
}

impl Renderer for GFX {
    fn render(&mut self, proj_view: &Matrix4<f32>) {
        let start = Instant::now();
        self.encoder.clear(&self.data.out_color, [CLEAR_COLOR.0,
                                                  CLEAR_COLOR.1,
                                                  CLEAR_COLOR.2,
                                                  CLEAR_COLOR.3]);

        for x in (-self.dimension) ..self.dimension {
            for y in (-self.dimension) ..self.dimension {
                self.data.transform = transform(x, y, proj_view).into();
                self.encoder.draw(&self.slice, &self.pso, &self.data);
            }
        }

        let pre_submit = start.elapsed();
        self.encoder.flush(&mut self.device);
        let post_submit = start.elapsed();
        self.window.swap_buffers().unwrap();
        self.device.cleanup();
        let swap = start.elapsed();

        println!("total time:\t\t{0:4.2}ms", duration_to_f64(swap));
        println!("\tcreate list:\t{0:4.2}ms", duration_to_f64(pre_submit));
        println!("\tsubmit:\t\t{0:4.2}ms", duration_to_f64(post_submit - pre_submit));
        println!("\tgpu wait:\t{0:4.2}ms", duration_to_f64(swap - post_submit));
    }
    fn window(&mut self) -> &glutin::Window { &self.window }
}

impl Drop for GFX {
    fn drop(&mut self) {
    }
}

impl GL {
    fn new(builder: glutin::WindowBuilder, dimension: i16) -> Self {
        fn compile_shader (gl:&Gl, src: &[u8], ty: GLenum) -> GLuint {
            unsafe {
                let shader = gl.CreateShader(ty);
                // Attempt to compile the shader
                gl.ShaderSource(shader, 1,
                                &(src.as_ptr() as *const i8),
                                &(src.len() as GLint));
                gl.CompileShader(shader);

                // Get the compile status
                let mut status = gl::FALSE as GLint;
                gl.GetShaderiv(shader, gl::COMPILE_STATUS, &mut status);

                // Fail on error
                if status != (gl::TRUE as GLint) {
                    let mut len: GLint = 0;
                    gl.GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut len);

                    // allocate a buffer of size (len - 1) to skip the trailing null character
                    let mut buf: Vec<u8> = repeat(0u8).take((len as usize).saturating_sub(1)).collect();
                    gl.GetShaderInfoLog(shader, len, ptr::null_mut(), buf.as_mut_ptr() as *mut GLchar);
                    panic!("{}", str::from_utf8(&buf).ok().expect("ShaderInfoLog not valid utf8"));
                }
                shader
            }
        };

        let window = builder.build().unwrap();
        unsafe { window.make_current().unwrap() };
        let gl = Gl::load_with(|s| window.get_proc_address(s) as *const _);

        // Create GLSL shaders
        let vs = compile_shader(&gl, VERTEX_SRC, gl::VERTEX_SHADER);
        let fs = compile_shader(&gl, FRAGMENT_SRC, gl::FRAGMENT_SHADER);

        // Link program
        let program;
        unsafe {
            program = gl.CreateProgram();
            gl.AttachShader(program, vs);
            gl.AttachShader(program, fs);
            gl.LinkProgram(program);
            // Get the link status
            let mut status = gl::FALSE as GLint;
            gl.GetProgramiv(program, gl::LINK_STATUS, &mut status);

            // Fail on error
            if status != (gl::TRUE as GLint) {
                let mut len: GLint = 0;
                gl.GetProgramiv(program, gl::INFO_LOG_LENGTH, &mut len);

                // allocate a buffer of size (len - 1) to skip the trailing null character
                let mut buf: Vec<u8> = repeat(0u8).take((len as usize).saturating_sub(1)).collect();
                gl.GetProgramInfoLog(program, len, ptr::null_mut(), buf.as_mut_ptr() as *mut GLchar);
                panic!("{}", str::from_utf8(&buf).ok().expect("ProgramInfoLog not valid utf8"));
            }
        }

        let mut vao = 0;
        let mut vbo = 0;

        let trans_uniform;
        unsafe {
            // Create Vertex Array Object
            gl.GenVertexArrays(1, &mut vao);
            gl.BindVertexArray(vao);

            // Create a Vertex Buffer Object and copy the vertex data to it
            gl.GenBuffers(1, &mut vbo);
            gl.BindBuffer(gl::ARRAY_BUFFER, vbo);

            gl.BufferData(gl::ARRAY_BUFFER,
                          (VERTEX_DATA.len() * mem::size_of::<Vertex>()) as GLsizeiptr,
                          mem::transmute(&VERTEX_DATA[0]),
                          gl::STATIC_DRAW);

            // Use shader program
            gl.UseProgram(program);
            let o_color = CString::new("o_Color").unwrap();
            gl.BindFragDataLocation(program, 0, o_color.as_bytes_with_nul().as_ptr() as *const i8);

            // Specify the layout of the vertex data
            let a_pos = CString::new("a_Pos").unwrap();
            gl.BindFragDataLocation(program, 0, a_pos.as_bytes_with_nul().as_ptr() as *const i8);

            let pos_attr = gl.GetAttribLocation(program, a_pos.as_ptr());
            gl.EnableVertexAttribArray(pos_attr as GLuint);
            gl.VertexAttribPointer(pos_attr as GLuint, 3, gl::FLOAT,
                                   gl::FALSE as GLboolean, 0, ptr::null());

            let u_transform = CString::new("u_Transform").unwrap();
            trans_uniform = gl.GetUniformLocation(program, u_transform.as_bytes_with_nul().as_ptr() as *const i8)
        };

        GL {
            window: window,
            dimension: dimension,
            gl: gl,
            vs: vs,
            fs: fs,
            program: program,
            vbo: vbo,
            vao: vao,
            trans_uniform: trans_uniform,
        }
    }
}

fn duration_to_ms(dur: Duration) -> f64 {
    (dur.as_secs() * 1000) as f64 + dur.subsec_nanos() as f64 / 1000_000.0
}

impl Renderer for GL {
    fn render(&mut self, proj_view: &Matrix4<f32>) {
        let start = Instant::now();

        // Clear the screen to black
        unsafe {
            self.gl.ClearColor(CLEAR_COLOR.0, CLEAR_COLOR.1, CLEAR_COLOR.2, CLEAR_COLOR.3);
            self.gl.Clear(gl::COLOR_BUFFER_BIT);
        }

        for x in (-self.dimension) ..self.dimension {
            for y in (-self.dimension) ..self.dimension {
                let mat:Matrix4<f32> = transform(x, y, proj_view).into();

                unsafe {
                    self.gl.UniformMatrix4fv(self.trans_uniform,
                                             1,
                                             gl::FALSE,
                                             mat.as_ptr());
                    self.gl.DrawArrays(gl::TRIANGLES, 0, 3);
                }

            }
        }

        let submit = start.elapsed();

        // Swap buffers
        self.window.swap_buffers().unwrap();
        let swap = start.elapsed();

        println!("total time:\t\t{0:4.2}ms", duration_to_ms(swap));
        println!("\tsubmit:\t\t{0:4.2}ms", duration_to_ms(submit));
        println!("\tgpu wait:\t{0:4.2}ms", duration_to_ms(swap - submit));
    }
    fn window(&mut self) -> &glutin::Window { &self.window }
}

impl Drop for GL {
    fn drop(&mut self) {
        unsafe {
            self.gl.DeleteProgram(self.program);
            self.gl.DeleteShader(self.fs);
            self.gl.DeleteShader(self.vs);
            self.gl.DeleteBuffers(1, &self.vbo);
            self.gl.DeleteVertexArrays(1, &self.vao);
        }
    }
}

fn main() {
    let ref mut args = env::args();
    let args_count = env::args().count();
    if args_count == 1 {
        println!("gfx-perf [gl|gfx] <size>");
        return;
    }

    let mode = args.nth(1).unwrap();
    let count: i32 = if args_count == 3 {
        FromStr::from_str(&args.next().unwrap()).ok()
    } else {
        None
    }.unwrap_or(10000);

    let count = ((count as f64).sqrt() / 2.) as i16;

    let builder = glutin::WindowBuilder::new()
        .with_title("Performance example".to_string())
        .with_dimensions(800, 600)
        .with_vsync();

    let mut r: Box<Renderer>;
    match mode.as_ref() {
        "gfx" => r = Box::new(GFX::new(builder, count)),
        "gl" => r = Box::new(GL::new(builder, count)),
        x => {
            panic!("{} is not a known mode", x)
        }
    }

    let proj_view = {
        let view = Matrix4::look_at(
            Point3::new(0f32, 5.0, -5.0),
            Point3::new(0f32, 0.0, 0.0),
            Vector3::unit_z(),
        );

        let proj = {
            let aspect = {
                let (w, h) = r.window().get_inner_size().unwrap();
                w as f32 / h as f32
            };
            cgmath::perspective(cgmath::deg(45.0f32), aspect, 1.0, 10.0)
        };
        proj * view
    };

    println!("count is {}", count*count*4);

    'main: loop {
        for event in r.window().poll_events() {
            match event {
                glutin::Event::KeyboardInput(_, _, Some(glutin::VirtualKeyCode::Escape)) |
                glutin::Event::Closed => break 'main,
                _ => {},
            }
        }
        r.render(&proj_view);
    }
}
<commit_msg>Minor cleanup of the performance example warnings<commit_after>// Copyright 2014 The Gfx-rs Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

extern crate cgmath;
#[macro_use]
extern crate gfx;
extern crate gfx_core;
extern crate gfx_window_glutin;
extern crate glutin;
extern crate gfx_gl as gl;
extern crate gfx_device_gl;

use cgmath::{SquareMatrix, Matrix, Point3, Vector3, Matrix3, Matrix4, Vector4};
pub use gfx::format::{DepthStencil, Rgba8 as ColorFormat};
//use glfw::Context;
use gl::Gl;
use gl::types::*;
use std::mem;
use std::ptr;
use std::str;
use std::env;
use std::str::FromStr;
use std::iter::repeat;
use std::ffi::CString;
use std::time::{Duration, Instant};
use gfx_device_gl::{Resources as R, CommandBuffer as CB};
use gfx_core::Device;

gfx_defines!{
    vertex Vertex {
        pos: [f32; 3] = "a_Pos",
    }

    pipeline pipe {
        vbuf: gfx::VertexBuffer<Vertex> = (),
        transform: gfx::Global<[[f32; 4]; 4]> = "u_Transform",
        out_color: gfx::RenderTarget<ColorFormat> = "o_Color",
    }
}

static VERTEX_SRC: &'static [u8] = b"
    #version 150 core
    in vec3 a_Pos;
    uniform mat4 u_Transform;

    void main() {
        gl_Position = u_Transform * vec4(a_Pos, 1.0);
   }
";

static FRAGMENT_SRC: &'static [u8] = b"
    #version 150 core
    out vec4 o_Color;

    void main() {
        o_Color = vec4(1.0, 0.0, 0.0, 1.0);
    }
";

static VERTEX_DATA: &'static [Vertex] = &[
    Vertex { pos: [-1.0, 0.0, -1.0] },
    Vertex { pos: [ 1.0, 0.0, -1.0] },
    Vertex { pos: [-1.0, 0.0,  1.0] },
];

const CLEAR_COLOR: (f32, f32, f32, f32) = (0.3, 0.3, 0.3, 1.0);

//----------------------------------------

fn transform(x: i16, y: i16, proj_view: &Matrix4<f32>) -> Matrix4<f32> {
    let mut model = Matrix4::from(Matrix3::identity() * 0.05);
    model.w = Vector4::new(x as f32 * 0.10,
                           0f32,
                           y as f32 * 0.10,
                           1f32);
    proj_view * model
}

trait Renderer: Drop {
    fn render(&mut self, proj_view: &Matrix4<f32>);
    fn window(&mut self) -> &glutin::Window;
}

struct GFX {
    dimension: i16,
    window: glutin::Window,
    device:gfx_device_gl::Device,
    encoder: gfx::Encoder<R,CB>,
    data: pipe::Data<R>,
    pso: gfx::PipelineState<R, pipe::Meta>,
    slice: gfx::Slice<R>,
}

struct GL {
    dimension: i16,
    window: glutin::Window,
    gl:Gl,
    trans_uniform:GLint,
    vs:GLuint,
    fs:GLuint,
    program:GLuint,
    vbo:GLuint,
    vao:GLuint,
}

impl GFX {
    fn new(builder: glutin::WindowBuilder, dimension: i16) -> Self {
        use gfx::traits::FactoryExt;

        let (window, device, mut factory, main_color, _) =
            gfx_window_glutin::init::<ColorFormat, DepthStencil>(builder);
        let encoder: gfx::Encoder<_,_> = factory.create_command_buffer().into();

        let pso = factory.create_pipeline_simple(
            VERTEX_SRC, FRAGMENT_SRC,
            pipe::new()
        ).unwrap();

        let (vbuf, slice) = factory.create_vertex_buffer_with_slice(VERTEX_DATA,());

        let data = pipe::Data {
            vbuf: vbuf,
            transform: cgmath::Matrix4::identity().into(),
            out_color: main_color,
        };

        GFX {
            window: window,
            dimension: dimension,
            device: device,
            encoder: encoder,
            data: data,
            pso: pso,
            slice: slice,
        }
    }
}

fn duration_to_f64(dur: Duration) -> f64 {
   dur.as_secs() as f64 + dur.subsec_nanos() as f64 / 1000_000_000.0
}

impl Renderer for GFX {
    fn render(&mut self, proj_view: &Matrix4<f32>) {
        let start = Instant::now();
        self.encoder.clear(&self.data.out_color, [CLEAR_COLOR.0,
                                                  CLEAR_COLOR.1,
                                                  CLEAR_COLOR.2,
                                                  CLEAR_COLOR.3]);

        for x in (-self.dimension) ..self.dimension {
            for y in (-self.dimension) ..self.dimension {
                self.data.transform = transform(x, y, proj_view).into();
                self.encoder.draw(&self.slice, &self.pso, &self.data);
            }
        }

        let pre_submit = start.elapsed();
        self.encoder.flush(&mut self.device);
        let post_submit = start.elapsed();
        self.window.swap_buffers().unwrap();
        self.device.cleanup();
        let swap = start.elapsed();

        println!("total time:\t\t{0:4.2}ms", duration_to_f64(swap));
        println!("\tcreate list:\t{0:4.2}ms", duration_to_f64(pre_submit));
        println!("\tsubmit:\t\t{0:4.2}ms", duration_to_f64(post_submit - pre_submit));
        println!("\tgpu wait:\t{0:4.2}ms", duration_to_f64(swap - post_submit));
    }
    fn window(&mut self) -> &glutin::Window { &self.window }
}

impl Drop for GFX {
    fn drop(&mut self) {
    }
}

impl GL {
    fn new(builder: glutin::WindowBuilder, dimension: i16) -> Self {
        fn compile_shader (gl:&Gl, src: &[u8], ty: GLenum) -> GLuint {
            unsafe {
                let shader = gl.CreateShader(ty);
                // Attempt to compile the shader
                gl.ShaderSource(shader, 1,
                                &(src.as_ptr() as *const i8),
                                &(src.len() as GLint));
                gl.CompileShader(shader);

                // Get the compile status
                let mut status = gl::FALSE as GLint;
                gl.GetShaderiv(shader, gl::COMPILE_STATUS, &mut status);

                // Fail on error
                if status != (gl::TRUE as GLint) {
                    let mut len: GLint = 0;
                    gl.GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut len);

                    // allocate a buffer of size (len - 1) to skip the trailing null character
                    let mut buf: Vec<u8> = repeat(0u8).take((len as usize).saturating_sub(1)).collect();
                    gl.GetShaderInfoLog(shader, len, ptr::null_mut(), buf.as_mut_ptr() as *mut GLchar);
                    panic!("{}", str::from_utf8(&buf).ok().expect("ShaderInfoLog not valid utf8"));
                }
                shader
            }
        };

        let window = builder.build().unwrap();
        unsafe { window.make_current().unwrap() };
        let gl = Gl::load_with(|s| window.get_proc_address(s) as *const _);

        // Create GLSL shaders
        let vs = compile_shader(&gl, VERTEX_SRC, gl::VERTEX_SHADER);
        let fs = compile_shader(&gl, FRAGMENT_SRC, gl::FRAGMENT_SHADER);

        // Link program
        let program;
        unsafe {
            program = gl.CreateProgram();
            gl.AttachShader(program, vs);
            gl.AttachShader(program, fs);
            gl.LinkProgram(program);
            // Get the link status
            let mut status = gl::FALSE as GLint;
            gl.GetProgramiv(program, gl::LINK_STATUS, &mut status);

            // Fail on error
            if status != (gl::TRUE as GLint) {
                let mut len: GLint = 0;
                gl.GetProgramiv(program, gl::INFO_LOG_LENGTH, &mut len);

                // allocate a buffer of size (len - 1) to skip the trailing null character
                let mut buf: Vec<u8> = repeat(0u8).take((len as usize).saturating_sub(1)).collect();
                gl.GetProgramInfoLog(program, len, ptr::null_mut(), buf.as_mut_ptr() as *mut GLchar);
                panic!("{}", str::from_utf8(&buf).ok().expect("ProgramInfoLog not valid utf8"));
            }
        }

        let mut vao = 0;
        let mut vbo = 0;

        let trans_uniform;
        unsafe {
            // Create Vertex Array Object
            gl.GenVertexArrays(1, &mut vao);
            gl.BindVertexArray(vao);

            // Create a Vertex Buffer Object and copy the vertex data to it
            gl.GenBuffers(1, &mut vbo);
            gl.BindBuffer(gl::ARRAY_BUFFER, vbo);

            gl.BufferData(gl::ARRAY_BUFFER,
                          (VERTEX_DATA.len() * mem::size_of::<Vertex>()) as GLsizeiptr,
                          mem::transmute(&VERTEX_DATA[0]),
                          gl::STATIC_DRAW);

            // Use shader program
            gl.UseProgram(program);
            let o_color = CString::new("o_Color").unwrap();
            gl.BindFragDataLocation(program, 0, o_color.as_bytes_with_nul().as_ptr() as *const i8);

            // Specify the layout of the vertex data
            let a_pos = CString::new("a_Pos").unwrap();
            gl.BindFragDataLocation(program, 0, a_pos.as_bytes_with_nul().as_ptr() as *const i8);

            let pos_attr = gl.GetAttribLocation(program, a_pos.as_ptr());
            gl.EnableVertexAttribArray(pos_attr as GLuint);
            gl.VertexAttribPointer(pos_attr as GLuint, 3, gl::FLOAT,
                                   gl::FALSE as GLboolean, 0, ptr::null());

            let u_transform = CString::new("u_Transform").unwrap();
            trans_uniform = gl.GetUniformLocation(program, u_transform.as_bytes_with_nul().as_ptr() as *const i8)
        };

        GL {
            window: window,
            dimension: dimension,
            gl: gl,
            vs: vs,
            fs: fs,
            program: program,
            vbo: vbo,
            vao: vao,
            trans_uniform: trans_uniform,
        }
    }
}

fn duration_to_ms(dur: Duration) -> f64 {
    (dur.as_secs() * 1000) as f64 + dur.subsec_nanos() as f64 / 1000_000.0
}

impl Renderer for GL {
    fn render(&mut self, proj_view: &Matrix4<f32>) {
        let start = Instant::now();

        // Clear the screen to black
        unsafe {
            self.gl.ClearColor(CLEAR_COLOR.0, CLEAR_COLOR.1, CLEAR_COLOR.2, CLEAR_COLOR.3);
            self.gl.Clear(gl::COLOR_BUFFER_BIT);
        }

        for x in (-self.dimension) ..self.dimension {
            for y in (-self.dimension) ..self.dimension {
                let mat:Matrix4<f32> = transform(x, y, proj_view).into();

                unsafe {
                    self.gl.UniformMatrix4fv(self.trans_uniform,
                                             1,
                                             gl::FALSE,
                                             mat.as_ptr());
                    self.gl.DrawArrays(gl::TRIANGLES, 0, 3);
                }

            }
        }

        let submit = start.elapsed();

        // Swap buffers
        self.window.swap_buffers().unwrap();
        let swap = start.elapsed();

        println!("total time:\t\t{0:4.2}ms", duration_to_ms(swap));
        println!("\tsubmit:\t\t{0:4.2}ms", duration_to_ms(submit));
        println!("\tgpu wait:\t{0:4.2}ms", duration_to_ms(swap - submit));
    }
    fn window(&mut self) -> &glutin::Window { &self.window }
}

impl Drop for GL {
    fn drop(&mut self) {
        unsafe {
            self.gl.DeleteProgram(self.program);
            self.gl.DeleteShader(self.fs);
            self.gl.DeleteShader(self.vs);
            self.gl.DeleteBuffers(1, &self.vbo);
            self.gl.DeleteVertexArrays(1, &self.vao);
        }
    }
}

fn main() {
    let ref mut args = env::args();
    let args_count = env::args().count();
    if args_count == 1 {
        println!("gfx-perf [gl|gfx] <size>");
        return;
    }

    let mode = args.nth(1).unwrap();
    let count: i32 = if args_count == 3 {
        FromStr::from_str(&args.next().unwrap()).ok()
    } else {
        None
    }.unwrap_or(10000);

    let count = ((count as f64).sqrt() / 2.) as i16;

    let builder = glutin::WindowBuilder::new()
        .with_title("Performance example".to_string())
        .with_dimensions(800, 600)
        .with_vsync();

    let mut r: Box<Renderer>;
    match mode.as_ref() {
        "gfx" => r = Box::new(GFX::new(builder, count)),
        "gl" => r = Box::new(GL::new(builder, count)),
        x => {
            panic!("{} is not a known mode", x)
        }
    }

    let proj_view = {
        let view = Matrix4::look_at(
            Point3::new(0f32, 5.0, -5.0),
            Point3::new(0f32, 0.0, 0.0),
            Vector3::unit_z(),
        );

        let proj = {
            let aspect = {
                let (w, h) = r.window().get_inner_size().unwrap();
                w as f32 / h as f32
            };
            cgmath::perspective(cgmath::deg(45.0f32), aspect, 1.0, 10.0)
        };
        proj * view
    };

    println!("count is {}", count*count*4);

    'main: loop {
        for event in r.window().poll_events() {
            match event {
                glutin::Event::KeyboardInput(_, _, Some(glutin::VirtualKeyCode::Escape)) |
                glutin::Event::Closed => break 'main,
                _ => {},
            }
        }
        r.render(&proj_view);
    }
}
<|endoftext|>


<commit_before>use crate::matching::{MatchResult, match_request};

use pact_matching::models::{RequestResponsePact, Request, OptionalBody, HttpPart};
use pact_matching::models::parse_query_string;

use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use log::{error, warn, info, debug};
use hyper::{Body, Response, Server, Error};
use hyper::http::response::{Builder as ResponseBuilder};
use hyper::http::header::{HeaderName, HeaderValue};
use hyper::service::service_fn;
use hyper::service::make_service_fn;
use serde_json::json;
use maplit::*;
use hyper::server::conn::AddrIncoming;
use rustls::ServerConfig;
use crate::mock_server::{MockServer, MockServerConfig};

#[derive(Debug, Clone)]
enum InteractionError {
    RequestHeaderEncodingError,
    RequestBodyError,
    ResponseHeaderEncodingError,
    ResponseBodyError
}

fn extract_path(uri: &hyper::Uri) -> String {
    uri.path_and_query()
        .map(|path_and_query| path_and_query.path())
        .unwrap_or("/")
        .into()
}

fn extract_query_string(uri: &hyper::Uri) -> Option<HashMap<String, Vec<String>>> {
    uri.path_and_query()
        .and_then(|path_and_query| path_and_query.query())
        .and_then(|query| parse_query_string(&query.into()))
}

fn extract_headers(headers: &hyper::HeaderMap) -> Result<Option<HashMap<String, Vec<String>>>, InteractionError> {
  if !headers.is_empty() {
    let result: Result<HashMap<String, Vec<String>>, InteractionError> = headers.keys()
      .map(|name| -> Result<(String, Vec<String>), InteractionError> {
        let values = headers.get_all(name);
        let parsed_vals: Vec<Result<String, InteractionError>> = values.iter()
          .map(|val| val.to_str()
            .map(|v| v.to_string())
            .map_err(|err| {
              warn!("Failed to parse HTTP header value: {}", err);
              InteractionError::RequestHeaderEncodingError
            })
          ).collect();
        if parsed_vals.iter().find(|val| val.is_err()).is_some() {
          Err(InteractionError::RequestHeaderEncodingError)
        } else {
          Ok((name.as_str().into(), parsed_vals.iter().cloned()
            .map(|val| val.unwrap_or_default())
            .flat_map(|val| val.split(",").map(|v| v.to_string()).collect::<Vec<String>>())
            .map(|val| val.trim().to_string())
            .collect()))
        }
      })
      .collect();

    result.map(|map| Some(map))
  } else {
    Ok(None)
  }
}

fn extract_body(bytes: bytes::Bytes, request: &Request) -> OptionalBody {
    if bytes.len() > 0 {
      OptionalBody::Present(bytes.to_vec(), request.content_type().map(|c| c.to_string()))
    } else {
      OptionalBody::Empty
    }
}

async fn hyper_request_to_pact_request(req: hyper::Request<Body>) -> Result<Request, InteractionError> {
    let method = req.method().to_string();
    let path = extract_path(req.uri());
    let query = extract_query_string(req.uri());
    let headers = extract_headers(req.headers())?;

    let body_bytes = hyper::body::to_bytes(req.into_body())
        .await
        .map_err(|_| InteractionError::RequestBodyError)?;

    let request = Request {
      method,
      path,
      query,
      headers,
      .. Request::default()
    };

    Ok(Request {
      body: extract_body(body_bytes, &request),
      .. request.clone()
    })
}

fn set_hyper_headers(builder: &mut ResponseBuilder, headers: &Option<HashMap<String, Vec<String>>>) -> Result<(), InteractionError> {
    let hyper_headers = builder.headers_mut().unwrap();
    match headers {
        Some(header_map) => {
            for (k, v) in header_map {
                for val in v {
                    // FIXME?: Headers are not sent in "raw" mode.
                    // Names are converted to lower case and values are parsed.
                    hyper_headers.append(
                        HeaderName::from_bytes(k.as_bytes())
                            .map_err(|err| {
                                error!("Invalid header name '{}' ({})", k, err);
                                InteractionError::ResponseHeaderEncodingError
                            })?,
                        val.parse::<HeaderValue>()
                            .map_err(|err| {
                                error!("Invalid header value '{}': '{}' ({})", k, val, err);
                                InteractionError::ResponseHeaderEncodingError
                            })?
                    );
                }
            }
        },
        _ => {}
    }
    Ok(())
}

fn error_body(request: &Request, error: &String) -> String {
    let body = json!({ "error" : format!("{} : {:?}", error, request) });
    body.to_string()
}

fn match_result_to_hyper_response(
  request: &Request,
  match_result: MatchResult,
  config: &MockServerConfig
) -> Result<Response<Body>, InteractionError> {
  let cors_preflight = config.cors_preflight;

  match match_result {
    MatchResult::RequestMatch(ref interaction) => {
      let response = pact_matching::generate_response(&interaction.response, &hashmap!{});
      info!("Request matched, sending response {}", response);
      if interaction.response.has_text_body() {
        debug!("     body: '{}'", interaction.response.body.str_value());
      }

      let mut builder = Response::builder()
        .status(response.status)
        .header(hyper::header::ACCESS_CONTROL_ALLOW_ORIGIN, "*");

      set_hyper_headers(&mut builder, &response.headers)?;

      builder.body(match response.body {
        OptionalBody::Present(ref s, _) => Body::from(s.clone()),
        _ => Body::empty()
      })
        .map_err(|_| InteractionError::ResponseBodyError)
    },
    _ => {
      debug!("Request did not match: {}", match_result);
      if cors_preflight && request.method.to_uppercase() == "OPTIONS" {
        info!("Responding to CORS pre-flight request");
        let origin = if cors_preflight {
          match request.headers {
            Some(ref h) => h.iter()
              .find(|kv| kv.0.to_lowercase() == "referer")
              .map(|kv| kv.1.clone().join(", ")).unwrap_or("*".to_string()),
            None => "*".to_string()
          }
        } else { "*".to_string() };
        Response::builder()
          .status(204)
          .header(hyper::header::ACCESS_CONTROL_ALLOW_ORIGIN, origin)
          .header(hyper::header::ACCESS_CONTROL_ALLOW_METHODS, "GET, HEAD, POST, PUT, DELETE, CONNECT, OPTIONS, TRACE, PATCH")
          .header(hyper::header::ACCESS_CONTROL_ALLOW_HEADERS, "*")
          .body(Body::empty())
          .map_err(|_| InteractionError::ResponseBodyError)
      } else {
        Response::builder()
          .status(500)
          .header(hyper::header::ACCESS_CONTROL_ALLOW_ORIGIN, "*")
          .header(hyper::header::CONTENT_TYPE, "application/json; charset=utf-8")
          .header("X-Pact", match_result.match_key())
          .body(Body::from(error_body(&request, &match_result.match_key())))
          .map_err(|_| InteractionError::ResponseBodyError)
      }
    }
  }
}

async fn handle_request(
  req: hyper::Request<Body>,
  pact: Arc<RequestResponsePact>,
  matches: Arc<Mutex<Vec<MatchResult>>>,
  config: Arc<MockServerConfig>
) -> Result<Response<Body>, InteractionError> {
    debug!("Creating pact request from hyper request");

    let pact_request = hyper_request_to_pact_request(req).await?;
    info!("Received request {}", pact_request);
    if pact_request.has_text_body() {
      debug!("     body: '{}'", pact_request.body.str_value());
    }

    let match_result = match_request(&pact_request, &pact.interactions);

    matches.lock().unwrap().push(match_result.clone());

    match_result_to_hyper_response(&pact_request, match_result, &config)
}

// TODO: Should instead use some form of X-Pact headers
fn handle_mock_request_error(result: Result<Response<Body>, InteractionError>) -> Result<Response<Body>, Error> {
    match result {
        Ok(response) => Ok(response),
        Err(error) => {
            let response = match error {
                InteractionError::RequestHeaderEncodingError => Response::builder()
                    .status(400)
                    .body(Body::from("Found an invalid header encoding")),
                InteractionError::RequestBodyError => Response::builder()
                    .status(500)
                    .body(Body::from("Could not process request body")),
                InteractionError::ResponseBodyError => Response::builder()
                    .status(500)
                    .body(Body::from("Could not process response body")),
                InteractionError::ResponseHeaderEncodingError => Response::builder()
                    .status(500)
                    .body(Body::from("Could not set response header"))
            };
            Ok(response.unwrap())
        }
    }
}

// Create and bind the server, but do not start it.
// Returns a future that drives the server.
// The reason that the function itself is still async (even if it performs
// no async operations) is that it needs a tokio context to be able to call try_bind.
pub(crate) async fn create_and_bind(
  pact: RequestResponsePact,
  addr: std::net::SocketAddr,
  shutdown: impl std::future::Future<Output = ()>,
  matches: Arc<Mutex<Vec<MatchResult>>>,
  mock_server: &MockServer
) -> Result<(impl std::future::Future<Output = ()>, std::net::SocketAddr), hyper::Error> {
  let pact = Arc::new(pact);
  let config = Arc::new(mock_server.config.clone());

  let server = Server::try_bind(&addr)?
    .serve(make_service_fn(move |_| {
      let pact = pact.clone();
      let matches = matches.clone();
      let config = config.clone();

      async {
        Ok::<_, hyper::Error>(
          service_fn(move |req| {
            let pact = pact.clone();
            let matches = matches.clone();
            let config = config.clone();

            async {
              handle_mock_request_error(
                handle_request(req, pact, matches, config).await
              )
            }
          })
        )
      }
    }));

  let socket_addr = server.local_addr();

  Ok((
      // This is the future that drives the server:
      async {
          let _ = server
              .with_graceful_shutdown(shutdown)
              .await;
      },
      socket_addr
  ))
}

pub(crate) async fn create_and_bind_tls(
  pact: RequestResponsePact,
  addr: std::net::SocketAddr,
  shutdown: impl std::future::Future<Output = ()>,
  matches: Arc<Mutex<Vec<MatchResult>>>,
  tls: &ServerConfig,
  mock_server: &MockServer
) -> Result<(impl std::future::Future<Output = ()>, std::net::SocketAddr), hyper::Error> {
  let pact = Arc::new(pact);
  let config = Arc::new(mock_server.config.clone());

  let incoming = AddrIncoming::bind(&addr)?;
  let socket_addr = incoming.local_addr();
  let server = Server::builder(crate::tls::TlsAcceptor::new(tls.clone(), incoming))
    .serve(make_service_fn(move |_| {
      let pact = pact.clone();
      let matches = matches.clone();
      let config = config.clone();

      async {
        Ok::<_, hyper::Error>(
          service_fn(move |req| {
            let pact = pact.clone();
            let matches = matches.clone();
            let config = config.clone();

            async {
              handle_mock_request_error(
                handle_request(req, pact, matches, config).await
              )
            }
          })
        )
      }
    }));

  Ok((
    // This is the future that drives the server:
    async {
      let _ = server
        .with_graceful_shutdown(shutdown)
        .await;
    },
    socket_addr
  ))
}

#[cfg(test)]
mod tests {
  use super::*;
  use hyper::HeaderMap;
  use hyper::header::{ACCEPT, USER_AGENT, CONTENT_TYPE};
  use expectest::prelude::*;
  use expectest::expect;

  #[tokio::test]
  async fn can_fetch_results_on_current_thread() {
    let (shutdown_tx, shutdown_rx) = futures::channel::oneshot::channel();
    let matches = Arc::new(Mutex::new(vec![]));

    let (future, _) = create_and_bind(
      RequestResponsePact::default(),
      ([0, 0, 0, 0], 0 as u16).into(),
      async {
          shutdown_rx.await.ok();
      },
      matches.clone(),
      &MockServer::default()
    ).await.unwrap();

    let join_handle = tokio::task::spawn(future);

    shutdown_tx.send(()).unwrap();

    // Server has shut down, now flush the server future from runtime
    join_handle.await.unwrap();

    // 0 matches have been produced
    let all_matches = matches.lock().unwrap().clone();
    assert_eq!(all_matches, vec![]);
  }

  #[test]
  fn handle_hyper_headers_with_multiple_values() {
    let mut headers = HeaderMap::new();
    headers.append(ACCEPT, "application/xml, application/json".parse().unwrap());
    headers.append(USER_AGENT, "test".parse().unwrap());
    headers.append(USER_AGENT, "test2".parse().unwrap());
    headers.append(CONTENT_TYPE, "text/plain".parse().unwrap());
    let result = extract_headers(&headers);
    expect!(result).to(be_ok().value(Some(hashmap! {
      "accept".to_string() => vec!["application/xml".to_string(), "application/json".to_string()],
      "user-agent".to_string() => vec!["test".to_string(), "test2".to_string()],
      "content-type".to_string() => vec!["text/plain".to_string()]
    })));
  }
}
<commit_msg>feat: add all the CORS headers<commit_after>use crate::matching::{MatchResult, match_request};

use pact_matching::models::{RequestResponsePact, Request, OptionalBody, HttpPart};
use pact_matching::models::parse_query_string;

use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use log::{error, warn, info, debug};
use hyper::{Body, Response, Server, Error};
use hyper::http::response::{Builder as ResponseBuilder};
use hyper::http::header::{HeaderName, HeaderValue};
use hyper::service::service_fn;
use hyper::service::make_service_fn;
use serde_json::json;
use maplit::*;
use hyper::server::conn::AddrIncoming;
use rustls::ServerConfig;
use crate::mock_server::{MockServer, MockServerConfig};

#[derive(Debug, Clone)]
enum InteractionError {
    RequestHeaderEncodingError,
    RequestBodyError,
    ResponseHeaderEncodingError,
    ResponseBodyError
}

fn extract_path(uri: &hyper::Uri) -> String {
    uri.path_and_query()
        .map(|path_and_query| path_and_query.path())
        .unwrap_or("/")
        .into()
}

fn extract_query_string(uri: &hyper::Uri) -> Option<HashMap<String, Vec<String>>> {
    uri.path_and_query()
        .and_then(|path_and_query| path_and_query.query())
        .and_then(|query| parse_query_string(&query.into()))
}

fn extract_headers(headers: &hyper::HeaderMap) -> Result<Option<HashMap<String, Vec<String>>>, InteractionError> {
  if !headers.is_empty() {
    let result: Result<HashMap<String, Vec<String>>, InteractionError> = headers.keys()
      .map(|name| -> Result<(String, Vec<String>), InteractionError> {
        let values = headers.get_all(name);
        let parsed_vals: Vec<Result<String, InteractionError>> = values.iter()
          .map(|val| val.to_str()
            .map(|v| v.to_string())
            .map_err(|err| {
              warn!("Failed to parse HTTP header value: {}", err);
              InteractionError::RequestHeaderEncodingError
            })
          ).collect();
        if parsed_vals.iter().find(|val| val.is_err()).is_some() {
          Err(InteractionError::RequestHeaderEncodingError)
        } else {
          Ok((name.as_str().into(), parsed_vals.iter().cloned()
            .map(|val| val.unwrap_or_default())
            .flat_map(|val| val.split(",").map(|v| v.to_string()).collect::<Vec<String>>())
            .map(|val| val.trim().to_string())
            .collect()))
        }
      })
      .collect();

    result.map(|map| Some(map))
  } else {
    Ok(None)
  }
}

fn extract_body(bytes: bytes::Bytes, request: &Request) -> OptionalBody {
    if bytes.len() > 0 {
      OptionalBody::Present(bytes.to_vec(), request.content_type().map(|c| c.to_string()))
    } else {
      OptionalBody::Empty
    }
}

async fn hyper_request_to_pact_request(req: hyper::Request<Body>) -> Result<Request, InteractionError> {
    let method = req.method().to_string();
    let path = extract_path(req.uri());
    let query = extract_query_string(req.uri());
    let headers = extract_headers(req.headers())?;

    let body_bytes = hyper::body::to_bytes(req.into_body())
        .await
        .map_err(|_| InteractionError::RequestBodyError)?;

    let request = Request {
      method,
      path,
      query,
      headers,
      .. Request::default()
    };

    Ok(Request {
      body: extract_body(body_bytes, &request),
      .. request.clone()
    })
}

fn set_hyper_headers(builder: &mut ResponseBuilder, headers: &Option<HashMap<String, Vec<String>>>) -> Result<(), InteractionError> {
    let hyper_headers = builder.headers_mut().unwrap();
    match headers {
        Some(header_map) => {
            for (k, v) in header_map {
                for val in v {
                    // FIXME?: Headers are not sent in "raw" mode.
                    // Names are converted to lower case and values are parsed.
                    hyper_headers.append(
                        HeaderName::from_bytes(k.as_bytes())
                            .map_err(|err| {
                                error!("Invalid header name '{}' ({})", k, err);
                                InteractionError::ResponseHeaderEncodingError
                            })?,
                        val.parse::<HeaderValue>()
                            .map_err(|err| {
                                error!("Invalid header value '{}': '{}' ({})", k, val, err);
                                InteractionError::ResponseHeaderEncodingError
                            })?
                    );
                }
            }
        },
        _ => {}
    }
    Ok(())
}

fn error_body(request: &Request, error: &String) -> String {
    let body = json!({ "error" : format!("{} : {:?}", error, request) });
    body.to_string()
}

fn match_result_to_hyper_response(
  request: &Request,
  match_result: MatchResult,
  config: &MockServerConfig
) -> Result<Response<Body>, InteractionError> {
  let cors_preflight = config.cors_preflight;

  match match_result {
    MatchResult::RequestMatch(ref interaction) => {
      let response = pact_matching::generate_response(&interaction.response, &hashmap!{});
      info!("Request matched, sending response {}", response);
      if interaction.response.has_text_body() {
        debug!("     body: '{}'", interaction.response.body.str_value());
      }

      let mut builder = Response::builder()
        .status(response.status)
        .header(hyper::header::ACCESS_CONTROL_ALLOW_ORIGIN, "*")
        .header(hyper::header::ACCESS_CONTROL_ALLOW_HEADERS, "*")
        .header(hyper::header::ACCESS_CONTROL_ALLOW_METHODS, "GET, HEAD, POST, PUT, DELETE, CONNECT, OPTIONS, TRACE, PATCH")
        .header(hyper::header::ACCESS_CONTROL_EXPOSE_HEADERS, "Location, Link");

      set_hyper_headers(&mut builder, &response.headers)?;

      builder.body(match response.body {
        OptionalBody::Present(ref s, _) => Body::from(s.clone()),
        _ => Body::empty()
      })
        .map_err(|_| InteractionError::ResponseBodyError)
    },
    _ => {
      debug!("Request did not match: {}", match_result);
      if cors_preflight && request.method.to_uppercase() == "OPTIONS" {
        info!("Responding to CORS pre-flight request");
        let origin = if cors_preflight {
          match request.headers {
            Some(ref h) => h.iter()
              .find(|kv| kv.0.to_lowercase() == "referer")
              .map(|kv| kv.1.clone().join(", ")).unwrap_or("*".to_string()),
            None => "*".to_string()
          }
        } else { "*".to_string() };
        Response::builder()
          .status(204)
          .header(hyper::header::ACCESS_CONTROL_ALLOW_ORIGIN, origin)
          .header(hyper::header::ACCESS_CONTROL_ALLOW_METHODS, "GET, HEAD, POST, PUT, DELETE, CONNECT, OPTIONS, TRACE, PATCH")
          .header(hyper::header::ACCESS_CONTROL_ALLOW_HEADERS, "*")
          .header(hyper::header::ACCESS_CONTROL_ALLOW_METHODS, "DELETE, GET, PATCH, POST, PUT")
          .header(hyper::header::ACCESS_CONTROL_EXPOSE_HEADERS, "Location, Link")
          .body(Body::empty())
          .map_err(|_| InteractionError::ResponseBodyError)
      } else {
        Response::builder()
          .status(500)
          .header(hyper::header::ACCESS_CONTROL_ALLOW_ORIGIN, "*")
          .header(hyper::header::CONTENT_TYPE, "application/json; charset=utf-8")
          .header("X-Pact", match_result.match_key())
          .body(Body::from(error_body(&request, &match_result.match_key())))
          .map_err(|_| InteractionError::ResponseBodyError)
      }
    }
  }
}

async fn handle_request(
  req: hyper::Request<Body>,
  pact: Arc<RequestResponsePact>,
  matches: Arc<Mutex<Vec<MatchResult>>>,
  config: Arc<MockServerConfig>
) -> Result<Response<Body>, InteractionError> {
    debug!("Creating pact request from hyper request");

    let pact_request = hyper_request_to_pact_request(req).await?;
    info!("Received request {}", pact_request);
    if pact_request.has_text_body() {
      debug!("     body: '{}'", pact_request.body.str_value());
    }

    let match_result = match_request(&pact_request, &pact.interactions);

    matches.lock().unwrap().push(match_result.clone());

    match_result_to_hyper_response(&pact_request, match_result, &config)
}

// TODO: Should instead use some form of X-Pact headers
fn handle_mock_request_error(result: Result<Response<Body>, InteractionError>) -> Result<Response<Body>, Error> {
    match result {
        Ok(response) => Ok(response),
        Err(error) => {
            let response = match error {
                InteractionError::RequestHeaderEncodingError => Response::builder()
                    .status(400)
                    .body(Body::from("Found an invalid header encoding")),
                InteractionError::RequestBodyError => Response::builder()
                    .status(500)
                    .body(Body::from("Could not process request body")),
                InteractionError::ResponseBodyError => Response::builder()
                    .status(500)
                    .body(Body::from("Could not process response body")),
                InteractionError::ResponseHeaderEncodingError => Response::builder()
                    .status(500)
                    .body(Body::from("Could not set response header"))
            };
            Ok(response.unwrap())
        }
    }
}

// Create and bind the server, but do not start it.
// Returns a future that drives the server.
// The reason that the function itself is still async (even if it performs
// no async operations) is that it needs a tokio context to be able to call try_bind.
pub(crate) async fn create_and_bind(
  pact: RequestResponsePact,
  addr: std::net::SocketAddr,
  shutdown: impl std::future::Future<Output = ()>,
  matches: Arc<Mutex<Vec<MatchResult>>>,
  mock_server: &MockServer
) -> Result<(impl std::future::Future<Output = ()>, std::net::SocketAddr), hyper::Error> {
  let pact = Arc::new(pact);
  let config = Arc::new(mock_server.config.clone());

  let server = Server::try_bind(&addr)?
    .serve(make_service_fn(move |_| {
      let pact = pact.clone();
      let matches = matches.clone();
      let config = config.clone();

      async {
        Ok::<_, hyper::Error>(
          service_fn(move |req| {
            let pact = pact.clone();
            let matches = matches.clone();
            let config = config.clone();

            async {
              handle_mock_request_error(
                handle_request(req, pact, matches, config).await
              )
            }
          })
        )
      }
    }));

  let socket_addr = server.local_addr();

  Ok((
      // This is the future that drives the server:
      async {
          let _ = server
              .with_graceful_shutdown(shutdown)
              .await;
      },
      socket_addr
  ))
}

pub(crate) async fn create_and_bind_tls(
  pact: RequestResponsePact,
  addr: std::net::SocketAddr,
  shutdown: impl std::future::Future<Output = ()>,
  matches: Arc<Mutex<Vec<MatchResult>>>,
  tls: &ServerConfig,
  mock_server: &MockServer
) -> Result<(impl std::future::Future<Output = ()>, std::net::SocketAddr), hyper::Error> {
  let pact = Arc::new(pact);
  let config = Arc::new(mock_server.config.clone());

  let incoming = AddrIncoming::bind(&addr)?;
  let socket_addr = incoming.local_addr();
  let server = Server::builder(crate::tls::TlsAcceptor::new(tls.clone(), incoming))
    .serve(make_service_fn(move |_| {
      let pact = pact.clone();
      let matches = matches.clone();
      let config = config.clone();

      async {
        Ok::<_, hyper::Error>(
          service_fn(move |req| {
            let pact = pact.clone();
            let matches = matches.clone();
            let config = config.clone();

            async {
              handle_mock_request_error(
                handle_request(req, pact, matches, config).await
              )
            }
          })
        )
      }
    }));

  Ok((
    // This is the future that drives the server:
    async {
      let _ = server
        .with_graceful_shutdown(shutdown)
        .await;
    },
    socket_addr
  ))
}

#[cfg(test)]
mod tests {
  use super::*;
  use hyper::HeaderMap;
  use hyper::header::{ACCEPT, USER_AGENT, CONTENT_TYPE};
  use expectest::prelude::*;
  use expectest::expect;

  #[tokio::test]
  async fn can_fetch_results_on_current_thread() {
    let (shutdown_tx, shutdown_rx) = futures::channel::oneshot::channel();
    let matches = Arc::new(Mutex::new(vec![]));

    let (future, _) = create_and_bind(
      RequestResponsePact::default(),
      ([0, 0, 0, 0], 0 as u16).into(),
      async {
          shutdown_rx.await.ok();
      },
      matches.clone(),
      &MockServer::default()
    ).await.unwrap();

    let join_handle = tokio::task::spawn(future);

    shutdown_tx.send(()).unwrap();

    // Server has shut down, now flush the server future from runtime
    join_handle.await.unwrap();

    // 0 matches have been produced
    let all_matches = matches.lock().unwrap().clone();
    assert_eq!(all_matches, vec![]);
  }

  #[test]
  fn handle_hyper_headers_with_multiple_values() {
    let mut headers = HeaderMap::new();
    headers.append(ACCEPT, "application/xml, application/json".parse().unwrap());
    headers.append(USER_AGENT, "test".parse().unwrap());
    headers.append(USER_AGENT, "test2".parse().unwrap());
    headers.append(CONTENT_TYPE, "text/plain".parse().unwrap());
    let result = extract_headers(&headers);
    expect!(result).to(be_ok().value(Some(hashmap! {
      "accept".to_string() => vec!["application/xml".to_string(), "application/json".to_string()],
      "user-agent".to_string() => vec!["test".to_string(), "test2".to_string()],
      "content-type".to_string() => vec!["text/plain".to_string()]
    })));
  }
}
<|endoftext|>