<commit_before>//! The Backtracking stategy.

use std::f64;
use std::hash::Hash;
use std::collections::HashSet;
use std::marker::PhantomData;

use super::Type;

pub trait State<S, A> {
    /// Type of the problem.
    ///
    /// Supported values are `Max` and `Min` for optimization problems, and
    /// `All` if you want to retrieve all the posible solutions.
    fn problem_type(&self) -> Type;

    /// Size of your problem. Usually referred to as `n`.
    ///
    /// It should decrease when going forward and increase when going backward.
    fn size(&self) -> usize;

    /// Final state - when going forward is imposible.
    fn is_final(&self) -> bool {
        self.size() == 0
    }

    /// List of different ways the problem can go forward (and backwards after).
    fn alternatives(&self) -> Vec<A>;

    fn forward(&mut self, a: A);
    fn backward(&mut self, a: A);

    /// Current state's value.
    fn value(&self) -> f64;

    /// An estimation of the best value the problem could reach if it chose the
    /// specified alternative.
    fn estimated_value(&self, _: A) -> f64 {
        match self.problem_type() {
            Type::Max => f64::MAX,
            Type::Min => f64::MIN,
            _ => 0.0, // In this case, the value is never used.
        }
    }

    /// Solution to a final state.
    fn solution(self) -> Option<S>;
}

pub struct Algorithm<P, S, A>
    where P: State<S, A>
{
    // This two are here so `S` and `A` are used.
    phans: PhantomData<S>,
    phana: PhantomData<A>,

    solution_count: usize,

    solutions: HashSet<P>,
    best_value: f64,
    success: bool,
    state: P,
}

impl<P, S, A> Algorithm<P, S, A>
    where P: State<S, A> + Clone + Eq + Hash + Ord,
          A: Clone
{
    /// Create a new algorithm to solve `state`.
    pub fn new(state: P) -> Self {
        Algorithm {
            phans: PhantomData,
            phana: PhantomData,

            solution_count: 100,

            solutions: HashSet::new(),
            best_value: match state.problem_type() {
                Type::Max => f64::MIN,
                Type::Min => f64::MAX,
                _ => 0.0, // In this case, the value is never used.
            },
            success: false,
            state: state,
        }
    }

    /// Change the number of solutions to calculate.
    ///
    /// If the default value is too small (e.g. your problem ramificates a lot)
    /// or too big (you don't want innecesary calculations) you can change the
    /// algorithm so it adjusts to your case.
    ///
    /// If it is set to 1, the algorithm will stop after the first solution.
    ///
    /// Default is 100.
    pub fn solution_count(mut self, count: usize) -> Self {
        self.solution_count = count;
        self
    }

    /// All the solutions calculated with the algorithm.
    pub fn all_solutions(&self) -> HashSet<P> {
        self.solutions.clone()
    }

    /// Store the current solution in the 'solutions' set if it's better than
    /// any of the allready stored, or if the problem is of type 'All'.
    fn update_solutions(&mut self) {
        let value = self.state.value();
        let problem_type = self.state.problem_type();

        if (problem_type != Type::Min && problem_type != Type::Max) ||
           (problem_type == Type::Min && value < self.best_value) ||
           (problem_type == Type::Max && value > self.best_value) {

            self.solutions.insert(self.state.clone());
            self.best_value = value;
        }
    }

    /// Decide if alternative `a` is not worthy of being explored.
    ///
    /// Here we need a well written `State::estimated_value`.
    fn is_to_prune(&self, a: A) -> bool {
        match self.state.problem_type() {
            Type::Max => self.state.estimated_value(a) <= self.best_value,
            Type::Min => self.state.estimated_value(a) >= self.best_value,
            _ => false,
        }
    }

    pub fn solve(&mut self) {
        if self.state.is_final() {
            self.update_solutions();
            self.success = self.solutions.len() >= self.solution_count;

        } else {
            let alternatives = self.state
                .alternatives()
                .into_iter()
                .filter(|a| !self.is_to_prune(a.clone()))
                .collect::<Vec<A>>();

            for alternative in alternatives {
                self.state.forward(alternative.clone());
                self.solve();
                self.state.backward(alternative.clone());

                if self.success {
                    break;
                }
            }
        }
    }
}
<commit_msg>Add part of backtracking's documentation<commit_after>//! The Backtracking stategy.
//!
//! From [Wikipedia][1]:
//!
//! > Backtracking is a general algorithm for finding all (or some) solutions
//! > to computational problems, notably _constraint satisfaction problems_,
//! > that builds solution candidates, and abandons each partial candidate
//! > - **backtracks** - as soon as it determines that it cannot be a valid
//! > solution.
//!
//! To use this strategy you need to implement the [`bt::State`][2] trait and
//! everything else is handled for you.
//!
//! [1]: https://en.wikipedia.org/wiki/Backtracking
//! [2]: trait.State.html

use std::f64;
use std::hash::Hash;
use std::collections::HashSet;
use std::marker::PhantomData;

use super::Type;

/// Backtracking problem state.
///
/// In order to use this strategy you have to implement this trait. There are
/// two generic types:
///
/// * `A`: Alternatives the algorithm uses to explore many posible solutions.
/// * `S`: The final solution, the output of the algorithm.
///
/// In backtracking, this is called _state_ instead of _problem_ because it
/// isn't a mere description, it **holds the values** while the algorithm is
/// running. When a `State` is created, it has to hold the problem's
/// **initial state**.
///
/// Maybe the two most important methods are `State::forward` and
/// `State::backward` because they define **how the `State` changes** when the
/// algorithm decides to take a certain alternative. Calling `forward` and
/// then `backward` must lead to the exact same state as the one before calling
/// them.
///
/// Once you have defined the **initial state**, use [`bt::Algorithm`][1] to
/// solve it.
///
/// [1]: struct.Algorithm.html
pub trait State<S, A> {
    /// Type of the problem.
    ///
    /// Supported values are `Max` and `Min` for optimization problems, and
    /// `All` if you want to retrieve all the posible solutions.
    fn problem_type(&self) -> Type;

    /// Size of your problem. Usually referred to as `n`.
    ///
    /// It should decrease when going forward and increase when going backward.
    fn size(&self) -> usize;

    /// Final state - when going forward is imposible.
    ///
    /// The default implementation is valid for a reduced number of problems.
    /// You may have other conditions to stop the algorithm: your budget has
    /// been spent, you don't have room left...
    fn is_final(&self) -> bool {
        self.size() == 0
    }

    /// List of different ways the problem can go forward (and backwards after).
    fn alternatives(&self) -> Vec<A>;

    /// Apply a change with the `a` alternative.
    ///
    /// The state must change its properties, according to what the `a`
    /// alternative does.
    fn forward(&mut self, a: A);

    /// Discard a change taken with the `a` alternative.
    ///
    /// It has to revert the state to the previos one, just before calling the
    /// `State::forward` method.
    fn backward(&mut self, a: A);

    /// Current state's value.
    ///
    /// Only called when `State::is_final`, gives the algorithm information
    /// about how good this final state is.
    fn value(&self) -> f64;

    /// An estimation of the best value the problem could reach if it chose the
    /// specified alternative.
    fn estimated_value(&self, _: A) -> f64 {
        match self.problem_type() {
            Type::Max => f64::MAX,
            Type::Min => f64::MIN,
            _ => 0.0, // In this case, the value is never used.
        }
    }

    /// Solution to a final state.
    fn solution(self) -> Option<S>;
}

pub struct Algorithm<P, S, A>
    where P: State<S, A>
{
    // This two are here so `S` and `A` are used.
    phans: PhantomData<S>,
    phana: PhantomData<A>,

    solution_count: usize,

    solutions: HashSet<P>,
    best_value: f64,
    success: bool,
    state: P,
}

impl<P, S, A> Algorithm<P, S, A>
    where P: State<S, A> + Clone + Eq + Hash + Ord,
          A: Clone
{
    /// Create a new algorithm to solve `state`.
    pub fn new(state: P) -> Self {
        Algorithm {
            phans: PhantomData,
            phana: PhantomData,

            solution_count: 100,

            solutions: HashSet::new(),
            best_value: match state.problem_type() {
                Type::Max => f64::MIN,
                Type::Min => f64::MAX,
                _ => 0.0, // In this case, the value is never used.
            },
            success: false,
            state: state,
        }
    }

    /// Change the number of solutions to calculate.
    ///
    /// If the default value is too small (e.g. your problem ramificates a lot)
    /// or too big (you don't want innecesary calculations) you can change the
    /// algorithm so it adjusts to your case.
    ///
    /// If it is set to 1, the algorithm will stop after the first solution.
    ///
    /// Default is 100.
    pub fn solution_count(mut self, count: usize) -> Self {
        self.solution_count = count;
        self
    }

    /// All the solutions calculated with the algorithm.
    pub fn all_solutions(&self) -> HashSet<P> {
        self.solutions.clone()
    }

    /// Store the current solution in the 'solutions' set if it's better than
    /// any of the allready stored, or if the problem is of type 'All'.
    fn update_solutions(&mut self) {
        let value = self.state.value();
        let problem_type = self.state.problem_type();

        if (problem_type != Type::Min && problem_type != Type::Max) ||
           (problem_type == Type::Min && value < self.best_value) ||
           (problem_type == Type::Max && value > self.best_value) {

            self.solutions.insert(self.state.clone());
            self.best_value = value;
        }
    }

    /// Decide if alternative `a` is not worthy of being explored.
    ///
    /// Here we need a well written `State::estimated_value`.
    fn is_to_prune(&self, a: A) -> bool {
        match self.state.problem_type() {
            Type::Max => self.state.estimated_value(a) <= self.best_value,
            Type::Min => self.state.estimated_value(a) >= self.best_value,
            _ => false,
        }
    }

    pub fn solve(&mut self) {
        if self.state.is_final() {
            self.update_solutions();
            self.success = self.solutions.len() >= self.solution_count;

        } else {
            let alternatives = self.state
                .alternatives()
                .into_iter()
                .filter(|a| !self.is_to_prune(a.clone()))
                .collect::<Vec<A>>();

            for alternative in alternatives {
                self.state.forward(alternative.clone());
                self.solve();
                self.state.backward(alternative.clone());

                if self.success {
                    break;
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2016 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.

// This test case tests the incremental compilation hash (ICH) implementation
// for `for` loops.

// The general pattern followed here is: Change one thing between rev1 and rev2
// and make sure that the hash has changed, then change nothing between rev2 and
// rev3 and make sure that the hash has not changed.

// must-compile-successfully
// revisions: cfail1 cfail2 cfail3
// compile-flags: -Z query-dep-graph

#![allow(warnings)]
#![feature(rustc_attrs)]
#![crate_type="rlib"]

// Change loop body ------------------------------------------------------------
#[cfg(cfail1)]
fn change_loop_body() {
    let mut _x = 0;
    for _ in 0..1 {
        _x = 1;
        break;
    }
}

#[cfg(not(cfail1))]
#[rustc_clean(label="Hir", cfg="cfail2")]
#[rustc_clean(label="Hir", cfg="cfail3")]
#[rustc_dirty(label="HirBody", cfg="cfail2")]
#[rustc_clean(label="HirBody", cfg="cfail3")]
fn change_loop_body() {
    let mut _x = 0;
    for _ in 0..1 {
        _x = 2;
        break;
    }
}

// Change iteration variable name ----------------------------------------------
#[cfg(cfail1)]
fn change_iteration_variable_name() {
    let mut _x = 0;
    for _i in 0..1 {
        _x = 1;
        break;
    }
}

#[cfg(not(cfail1))]
#[rustc_clean(label="Hir", cfg="cfail2")]
#[rustc_clean(label="Hir", cfg="cfail3")]
#[rustc_dirty(label="HirBody", cfg="cfail2")]
#[rustc_clean(label="HirBody", cfg="cfail3")]
fn change_iteration_variable_name() {
    let mut _x = 0;
    for _a in 0..1 {
        _x = 1;
        break;
    }
}

// Change iteration variable pattern -------------------------------------------
#[cfg(cfail1)]
fn change_iteration_variable_pattern() {
    let mut _x = 0;
    for _i in &[0, 1, 2] {
        _x = 1;
        break;
    }
}

#[cfg(not(cfail1))]
#[rustc_clean(label="Hir", cfg="cfail2")]
#[rustc_clean(label="Hir", cfg="cfail3")]
#[rustc_dirty(label="HirBody", cfg="cfail2")]
#[rustc_clean(label="HirBody", cfg="cfail3")]
fn change_iteration_variable_pattern() {
    let mut _x = 0;
    for &_i in &[0, 1, 2] {
        _x = 1;
        break;
    }
}

// Change iterable -------------------------------------------------------------
#[cfg(cfail1)]
fn change_iterable() {
    let mut _x = 0;
    for _ in &[0, 1, 2] {
        _x = 1;
        break;
    }
}

#[cfg(not(cfail1))]
#[rustc_clean(label="Hir", cfg="cfail2")]
#[rustc_clean(label="Hir", cfg="cfail3")]
#[rustc_dirty(label="HirBody", cfg="cfail2")]
#[rustc_clean(label="HirBody", cfg="cfail3")]
fn change_iterable() {
    let mut _x = 0;
    for _ in &[0, 1, 3] {
        _x = 1;
        break;
    }
}

// Add break -------------------------------------------------------------------
#[cfg(cfail1)]
fn add_break() {
    let mut _x = 0;
    for _ in 0..1 {
        _x = 1;
    }
}

#[cfg(not(cfail1))]
#[rustc_clean(label="Hir", cfg="cfail2")]
#[rustc_clean(label="Hir", cfg="cfail3")]
#[rustc_dirty(label="HirBody", cfg="cfail2")]
#[rustc_clean(label="HirBody", cfg="cfail3")]
fn add_break() {
    let mut _x = 0;
    for _ in 0..1 {
        _x = 1;
        break;
    }
}

// Add loop label --------------------------------------------------------------
#[cfg(cfail1)]
fn add_loop_label() {
    let mut _x = 0;
    for _ in 0..1 {
        _x = 1;
        break;
    }
}

#[cfg(not(cfail1))]
#[rustc_clean(label="Hir", cfg="cfail2")]
#[rustc_clean(label="Hir", cfg="cfail3")]
#[rustc_dirty(label="HirBody", cfg="cfail2")]
#[rustc_clean(label="HirBody", cfg="cfail3")]
fn add_loop_label() {
    let mut _x = 0;
    'label: for _ in 0..1 {
        _x = 1;
        break;
    }
}

// Add loop label to break -----------------------------------------------------
#[cfg(cfail1)]
fn add_loop_label_to_break() {
    let mut _x = 0;
    'label: for _ in 0..1 {
        _x = 1;
        break;
    }
}

#[cfg(not(cfail1))]
#[rustc_clean(label="Hir", cfg="cfail2")]
#[rustc_clean(label="Hir", cfg="cfail3")]
#[rustc_dirty(label="HirBody", cfg="cfail2")]
#[rustc_clean(label="HirBody", cfg="cfail3")]
fn add_loop_label_to_break() {
    let mut _x = 0;
    'label: for _ in 0..1 {
        _x = 1;
        break 'label;
    }
}

// Change break label ----------------------------------------------------------
#[cfg(cfail1)]
fn change_break_label() {
    let mut _x = 0;
    'outer: for _ in 0..1 {
        'inner: for _ in 0..1 {
            _x = 1;
            break 'inner;
        }
    }
}

#[cfg(not(cfail1))]
#[rustc_clean(label="Hir", cfg="cfail2")]
#[rustc_clean(label="Hir", cfg="cfail3")]
#[rustc_dirty(label="HirBody", cfg="cfail2")]
#[rustc_clean(label="HirBody", cfg="cfail3")]
fn change_break_label() {
    let mut _x = 0;
    'outer: for _ in 0..1 {
        'inner: for _ in 0..1 {
            _x = 1;
            break 'outer;
        }
    }
}

// Add loop label to continue --------------------------------------------------
#[cfg(cfail1)]
fn add_loop_label_to_continue() {
    let mut _x = 0;
    'label: for _ in 0..1 {
        _x = 1;
        continue;
    }
}

#[cfg(not(cfail1))]
#[rustc_clean(label="Hir", cfg="cfail2")]
#[rustc_clean(label="Hir", cfg="cfail3")]
#[rustc_dirty(label="HirBody", cfg="cfail2")]
#[rustc_clean(label="HirBody", cfg="cfail3")]
fn add_loop_label_to_continue() {
    let mut _x = 0;
    'label: for _ in 0..1 {
        _x = 1;
        continue 'label;
    }
}

// Change continue label ----------------------------------------------------------
#[cfg(cfail1)]
fn change_continue_label() {
    let mut _x = 0;
    'outer: for _ in 0..1 {
        'inner: for _ in 0..1 {
            _x = 1;
            continue 'inner;
        }
    }
}

#[cfg(not(cfail1))]
#[rustc_clean(label="Hir", cfg="cfail2")]
#[rustc_clean(label="Hir", cfg="cfail3")]
#[rustc_dirty(label="HirBody", cfg="cfail2")]
#[rustc_clean(label="HirBody", cfg="cfail3")]
fn change_continue_label() {
    let mut _x = 0;
    'outer: for _ in 0..1 {
        'inner: for _ in 0..1 {
            _x = 1;
            continue 'outer;
        }
    }
}

// Change continue to break ----------------------------------------------------
#[cfg(cfail1)]
fn change_continue_to_break() {
    let mut _x = 0;
    for _ in 0..1 {
        _x = 1;
        continue;
    }
}

#[cfg(not(cfail1))]
#[rustc_clean(label="Hir", cfg="cfail2")]
#[rustc_clean(label="Hir", cfg="cfail3")]
#[rustc_dirty(label="HirBody", cfg="cfail2")]
#[rustc_clean(label="HirBody", cfg="cfail3")]
fn change_continue_to_break() {
    let mut _x = 0;
    for _ in 0..1 {
        _x = 1;
        break;
    }
}
<commit_msg>Update for loop fingerprint hash tests<commit_after>// Copyright 2016 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.

// This test case tests the incremental compilation hash (ICH) implementation
// for `for` loops.

// The general pattern followed here is: Change one thing between rev1 and rev2
// and make sure that the hash has changed, then change nothing between rev2 and
// rev3 and make sure that the hash has not changed.

// must-compile-successfully
// revisions: cfail1 cfail2 cfail3
// compile-flags: -Z query-dep-graph

#![allow(warnings)]
#![feature(rustc_attrs)]
#![crate_type="rlib"]

// Change loop body ------------------------------------------------------------
#[cfg(cfail1)]
pub fn change_loop_body() {
    let mut _x = 0;
    for _ in 0..1 {
        _x = 1;
        break;
    }
}

#[cfg(not(cfail1))]
#[rustc_clean(cfg="cfail2", except="HirBody, MirValidated, MirOptimized")]
#[rustc_clean(cfg="cfail3")]
pub fn change_loop_body() {
    let mut _x = 0;
    for _ in 0..1 {
        _x = 2;
        break;
    }
}

// Change iteration variable name ----------------------------------------------
#[cfg(cfail1)]
pub fn change_iteration_variable_name() {
    let mut _x = 0;
    for _i in 0..1 {
        _x = 1;
        break;
    }
}

#[cfg(not(cfail1))]
#[rustc_clean(cfg="cfail2", except="HirBody, MirValidated, MirOptimized")]
#[rustc_clean(cfg="cfail3")]
pub fn change_iteration_variable_name() {
    let mut _x = 0;
    for _a in 0..1 {
        _x = 1;
        break;
    }
}

// Change iteration variable pattern -------------------------------------------
#[cfg(cfail1)]
pub fn change_iteration_variable_pattern() {
    let mut _x = 0;
    for _i in &[0, 1, 2] {
        _x = 1;
        break;
    }
}

#[cfg(not(cfail1))]
#[rustc_clean(cfg="cfail2", except="HirBody, MirValidated, MirOptimized, TypeckTables")]
#[rustc_clean(cfg="cfail3")]
pub fn change_iteration_variable_pattern() {
    let mut _x = 0;
    for &_i in &[0, 1, 2] {
        _x = 1;
        break;
    }
}

// Change iterable -------------------------------------------------------------
#[cfg(cfail1)]
pub fn change_iterable() {
    let mut _x = 0;
    for _ in &[0, 1, 2] {
        _x = 1;
        break;
    }
}

#[cfg(not(cfail1))]
#[rustc_clean(cfg="cfail2", except="HirBody, MirValidated, MirOptimized")]
#[rustc_clean(cfg="cfail3")]
pub fn change_iterable() {
    let mut _x = 0;
    for _ in &[0, 1, 3] {
        _x = 1;
        break;
    }
}

// Add break -------------------------------------------------------------------
#[cfg(cfail1)]
pub fn add_break() {
    let mut _x = 0;
    for _ in 0..1 {
        _x = 1;
    }
}

#[cfg(not(cfail1))]
#[rustc_clean(cfg="cfail2", except="HirBody, MirValidated, MirOptimized, TypeckTables")]
#[rustc_clean(cfg="cfail3")]
pub fn add_break() {
    let mut _x = 0;
    for _ in 0..1 {
        _x = 1;
        break;
    }
}

// Add loop label --------------------------------------------------------------
#[cfg(cfail1)]
pub fn add_loop_label() {
    let mut _x = 0;
    for _ in 0..1 {
        _x = 1;
        break;
    }
}

#[cfg(not(cfail1))]
#[rustc_clean(cfg="cfail2", except="HirBody")]
#[rustc_clean(cfg="cfail3")]
pub fn add_loop_label() {
    let mut _x = 0;
    'label: for _ in 0..1 {
        _x = 1;
        break;
    }
}

// Add loop label to break -----------------------------------------------------
#[cfg(cfail1)]
pub fn add_loop_label_to_break() {
    let mut _x = 0;
    'label: for _ in 0..1 {
        _x = 1;
        break;
    }
}

#[cfg(not(cfail1))]
#[rustc_clean(cfg="cfail2", except="HirBody")]
#[rustc_clean(cfg="cfail3")]
pub fn add_loop_label_to_break() {
    let mut _x = 0;
    'label: for _ in 0..1 {
        _x = 1;
        break 'label;
    }
}

// Change break label ----------------------------------------------------------
#[cfg(cfail1)]
pub fn change_break_label() {
    let mut _x = 0;
    'outer: for _ in 0..1 {
        'inner: for _ in 0..1 {
            _x = 1;
            break 'inner;
        }
    }
}

#[cfg(not(cfail1))]
#[rustc_clean(cfg="cfail2", except="HirBody, MirValidated, MirOptimized")]
#[rustc_clean(cfg="cfail3")]
pub fn change_break_label() {
    let mut _x = 0;
    'outer: for _ in 0..1 {
        'inner: for _ in 0..1 {
            _x = 1;
            break 'outer;
        }
    }
}

// Add loop label to continue --------------------------------------------------
#[cfg(cfail1)]
pub fn add_loop_label_to_continue() {
    let mut _x = 0;
    'label: for _ in 0..1 {
        _x = 1;
        continue;
    }
}

#[cfg(not(cfail1))]
#[rustc_clean(cfg="cfail2", except="HirBody")]
#[rustc_clean(cfg="cfail3")]
pub fn add_loop_label_to_continue() {
    let mut _x = 0;
    'label: for _ in 0..1 {
        _x = 1;
        continue 'label;
    }
}

// Change continue label ----------------------------------------------------------
#[cfg(cfail1)]
pub fn change_continue_label() {
    let mut _x = 0;
    'outer: for _ in 0..1 {
        'inner: for _ in 0..1 {
            _x = 1;
            continue 'inner;
        }
    }
}

#[cfg(not(cfail1))]
#[rustc_clean(cfg="cfail2", except="HirBody, MirValidated, MirOptimized")]
#[rustc_clean(cfg="cfail3")]
pub fn change_continue_label() {
    let mut _x = 0;
    'outer: for _ in 0..1 {
        'inner: for _ in 0..1 {
            _x = 1;
            continue 'outer;
        }
    }
}

// Change continue to break ----------------------------------------------------
#[cfg(cfail1)]
pub fn change_continue_to_break() {
    let mut _x = 0;
    for _ in 0..1 {
        _x = 1;
        continue;
    }
}

#[cfg(not(cfail1))]
#[rustc_clean(cfg="cfail2", except="HirBody, MirValidated, MirOptimized")]
#[rustc_clean(cfg="cfail3")]
pub fn change_continue_to_break() {
    let mut _x = 0;
    for _ in 0..1 {
        _x = 1;
        break;
    }
}
<|endoftext|>


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

#![crate_type = "bin"]
#![feature(collections, core, io, rand, os)]

extern crate capnp;
extern crate rand;

use capnp::{MessageReader, MessageBuilder};

pub mod common;

pub mod carsales_capnp {
  include!(concat!(env!("OUT_DIR"), "/carsales_capnp.rs"));
}
pub mod carsales;

pub mod catrank_capnp {
  include!(concat!(env!("OUT_DIR"), "/catrank_capnp.rs"));
}
pub mod catrank;

pub mod eval_capnp {
  include!(concat!(env!("OUT_DIR"), "/eval_capnp.rs"));
}

pub mod eval;

mod uncompressed {
    use capnp;

    pub fn write<T : ::std::old_io::Writer, U : capnp::message::MessageBuilder>(
        writer: &mut T,
        message: &U) {
        capnp::serialize::write_message(writer, message).unwrap();
    }

    pub fn write_buffered<T : ::std::old_io::Writer, U : capnp::message::MessageBuilder>(
        writer: &mut T,
        message: &U) {
        capnp::serialize::write_message(writer, message).unwrap();
    }

    pub fn new_buffered_reader<R: capnp::io::BufferedInputStream>(
        input_stream : &mut R,
        options : capnp::message::ReaderOptions) -> capnp::serialize::OwnedSpaceMessageReader {
        capnp::serialize::new_reader(input_stream, options).unwrap()
    }
}

mod packed {
    use capnp;
    use capnp::serialize_packed::{write_packed_message, write_packed_message_unbuffered};

    pub fn write<T : ::std::old_io::Writer, U : capnp::message::MessageBuilder>(
        writer: &mut T,
        message: &U) {
        write_packed_message_unbuffered(writer, message).unwrap();
    }

    pub fn write_buffered<T : capnp::io::BufferedOutputStream, U : capnp::message::MessageBuilder>(
        writer: &mut T,
        message: &U) {
        write_packed_message(writer, message).unwrap();
    }

    pub fn new_buffered_reader<R:capnp::io::BufferedInputStream>(
        input_stream : &mut R,
        options : capnp::message::ReaderOptions) -> capnp::serialize::OwnedSpaceMessageReader {
        capnp::serialize_packed::new_reader(input_stream, options).unwrap()
    }

}

const SCRATCH_SIZE : usize = 128 * 1024;

#[derive(Copy)]
pub struct NoScratch;

impl NoScratch {
    fn new_builder(&mut self, _idx : usize) -> capnp::message::MallocMessageBuilder {
        capnp::message::MallocMessageBuilder::new_default()
    }
}

pub struct UseScratch {
    scratch_space : ::std::vec::Vec<capnp::common::Word>
}

impl UseScratch {
    pub fn new() -> UseScratch {
        UseScratch {
            scratch_space : capnp::common::allocate_zeroed_words(SCRATCH_SIZE * 6)
        }
    }

    fn new_builder<'a>(&mut self, idx : usize) -> capnp::message::ScratchSpaceMallocMessageBuilder<'a> {
        assert!(idx < 6);
        unsafe {
            capnp::message::ScratchSpaceMallocMessageBuilder::new_default(
                ::std::mem::transmute(
                    ::std::raw::Slice { data : self.scratch_space.get_unchecked(idx * SCRATCH_SIZE),
                                      len : SCRATCH_SIZE }))
        }
    }
}

macro_rules! pass_by_object(
    ( $testcase:ident, $reuse:ident, $iters:expr ) => ({
            let mut rng = common::FastRand::new();
            for _ in range(0, $iters) {
                let mut message_req = $reuse.new_builder(0);
                let mut message_res = $reuse.new_builder(1);

                let expected = $testcase::setup_request(&mut rng,
                                                        message_req.init_root::<$testcase::RequestBuilder>());

                $testcase::handle_request(message_req.get_root::<$testcase::RequestBuilder>().as_reader(),
                                          message_res.init_root::<$testcase::ResponseBuilder>());

                if !$testcase::check_response(message_res.get_root::<$testcase::ResponseBuilder>().as_reader(),
                                              expected) {
                    panic!("Incorrect response.");
                }
            }
        });
    );

macro_rules! pass_by_bytes(
    ( $testcase:ident, $reuse:ident, $compression:ident, $iters:expr ) => ({
        let mut request_bytes : ::std::vec::Vec<u8> =
            ::std::iter::repeat(0u8).take(SCRATCH_SIZE * 8).collect();
        let mut response_bytes : ::std::vec::Vec<u8> =
            ::std::iter::repeat(0u8).take(SCRATCH_SIZE * 8).collect();
        let mut rng = common::FastRand::new();
        for _ in range(0, $iters) {
            let mut message_req = $reuse.new_builder(0);
            let mut message_res = $reuse.new_builder(1);

            let expected = {
                let request = message_req.init_root::<$testcase::RequestBuilder>();
                $testcase::setup_request(&mut rng, request)
            };

            {
                let response = message_res.init_root::<$testcase::ResponseBuilder>();

                {
                    let mut writer = capnp::io::ArrayOutputStream::new(request_bytes.as_mut_slice());
                    $compression::write_buffered(&mut writer, &message_req)
                }

                let message_reader = $compression::new_buffered_reader(
                    &mut capnp::io::ArrayInputStream::new(request_bytes.as_slice()),
                    capnp::message::DEFAULT_READER_OPTIONS);

                let request_reader : $testcase::RequestReader = message_reader.get_root();
                $testcase::handle_request(request_reader, response);
            }

            {
                let mut writer = capnp::io::ArrayOutputStream::new(response_bytes.as_mut_slice());
                $compression::write_buffered(&mut writer, &message_res)
            }

            let message_reader = $compression::new_buffered_reader(
                &mut capnp::io::ArrayInputStream::new(response_bytes.as_slice()),
                capnp::message::DEFAULT_READER_OPTIONS);

            let response_reader : $testcase::ResponseReader = message_reader.get_root();
            if !$testcase::check_response(response_reader, expected) {
                panic!("Incorrect response.");
            }
        }
    });
    );

macro_rules! server(
    ( $testcase:ident, $reuse:ident, $compression:ident, $iters:expr, $input:expr, $output:expr) => ({
            let mut out_buffered = capnp::io::BufferedOutputStreamWrapper::new(&mut $output);
            let mut in_buffered = capnp::io::BufferedInputStreamWrapper::new(&mut $input);
            for _ in range(0, $iters) {
                let mut message_res = $reuse.new_builder(0);

                {
                    let response = message_res.init_root::<$testcase::ResponseBuilder>();
                    let message_reader = $compression::new_buffered_reader(
                        &mut in_buffered,
                        capnp::message::DEFAULT_READER_OPTIONS);
                    let request_reader : $testcase::RequestReader = message_reader.get_root();
                    $testcase::handle_request(request_reader, response);
                }

                $compression::write_buffered(&mut out_buffered, &message_res);
                out_buffered.flush().unwrap();
            }
        });
    );

macro_rules! sync_client(
    ( $testcase:ident, $reuse:ident, $compression:ident, $iters:expr) => ({
            let mut out_stream = ::std::old_io::stdio::stdout_raw();
            let mut in_stream = ::std::old_io::stdio::stdin_raw();
            let mut in_buffered = capnp::io::BufferedInputStreamWrapper::new(&mut in_stream);
            let mut rng = common::FastRand::new();
            for _ in range(0, $iters) {
                let mut message_req = $reuse.new_builder(0);

                let expected = {
                    let request = message_req.init_root::<$testcase::RequestBuilder>();
                    $testcase::setup_request(&mut rng, request)
                };
                $compression::write(&mut out_stream, &message_req);

                let message_reader = $compression::new_buffered_reader(
                    &mut in_buffered,
                    capnp::message::DEFAULT_READER_OPTIONS);
                let response_reader : $testcase::ResponseReader = message_reader.get_root();
                assert!($testcase::check_response(response_reader, expected));

            }
        });
    );

macro_rules! pass_by_pipe(
    ( $testcase:ident, $reuse:ident, $compression:ident, $iters:expr) => ({
        use std::old_io::process;

        let mut args = ::std::os::args();
        args[2] = "client".to_string();

        let mut command = process::Command::new(args[0].as_slice());
        command.args(args.slice(1, args.len()));
        command.stdin(process::CreatePipe(true, false));
        command.stdout(process::CreatePipe(false, true));
        command.stderr(process::Ignored);
        match command.spawn() {
            Ok(ref mut p) => {
                let mut child_std_out = p.stdout.take().unwrap();
                let mut child_std_in = p.stdin.take().unwrap();

                server!($testcase, $reuse, $compression, $iters, child_std_out, child_std_in);
                println!("{}", p.wait().unwrap());
            }
            Err(e) => {
                println!("could not start process: {}", e);
            }
        }
    });
    );

macro_rules! do_testcase(
    ( $testcase:ident, $mode:expr, $reuse:ident, $compression:ident, $iters:expr ) => ({
            match $mode.as_slice() {
                "object" => pass_by_object!($testcase, $reuse, $iters),
                "bytes" => pass_by_bytes!($testcase, $reuse, $compression, $iters),
                "client" => sync_client!($testcase, $reuse, $compression, $iters),
                "server" => {
                    let mut input = ::std::old_io::stdio::stdin_raw();
                    let mut output = ::std::old_io::stdio::stdout_raw();
                    server!($testcase, $reuse, $compression, $iters, input, output)
                }
                "pipe" => pass_by_pipe!($testcase, $reuse, $compression, $iters),
                s => panic!("unrecognized mode: {}", s)
            }
        });
    );

macro_rules! do_testcase1(
    ( $testcase:expr, $mode:expr, $reuse:ident, $compression:ident, $iters:expr) => ({
            match $testcase.as_slice() {
                "carsales" => do_testcase!(carsales, $mode, $reuse, $compression, $iters),
                "catrank" => do_testcase!(catrank, $mode, $reuse, $compression, $iters),
                "eval" => do_testcase!(eval, $mode, $reuse, $compression, $iters),
                s => panic!("unrecognized test case: {}", s)
            }
        });
    );

macro_rules! do_testcase2(
    ( $testcase:expr, $mode:expr, $reuse:expr, $compression:ident, $iters:expr) => ({
            match $reuse.as_slice() {
                "no-reuse" => {
                    let mut scratch = NoScratch;
                    do_testcase1!($testcase, $mode, scratch, $compression, $iters)
                }
                "reuse" => {
                    let mut scratch = UseScratch::new();
                    do_testcase1!($testcase, $mode, scratch, $compression, $iters)
                }
                s => panic!("unrecognized reuse option: {}", s)
            }
        });
    );

pub fn main() {
    let args = ::std::os::args();

    if args.len() != 6 {
        println!("USAGE: {} CASE MODE REUSE COMPRESSION ITERATION_COUNT", args[0]);
        ::std::os::set_exit_status(1);
        return;
    }

    let iters = match args[5].parse::<u64>() {
        Some (n) => n,
        None => {
            println!("Could not parse a u64 from: {}", args[5]);
            ::std::os::set_exit_status(1);
            return;
        }
    };

    match args[4].as_slice() {
        "none" => do_testcase2!(args[1], args[2],  args[3], uncompressed, iters),
        "packed" => do_testcase2!(args[1], args[2], args[3], packed, iters),
        s => panic!("unrecognized compression: {}", s)
    }
}
<commit_msg>parse: Option -> Result<commit_after>// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#![crate_type = "bin"]
#![feature(collections, core, io, rand, os)]

extern crate capnp;
extern crate rand;

use capnp::{MessageReader, MessageBuilder};

pub mod common;

pub mod carsales_capnp {
  include!(concat!(env!("OUT_DIR"), "/carsales_capnp.rs"));
}
pub mod carsales;

pub mod catrank_capnp {
  include!(concat!(env!("OUT_DIR"), "/catrank_capnp.rs"));
}
pub mod catrank;

pub mod eval_capnp {
  include!(concat!(env!("OUT_DIR"), "/eval_capnp.rs"));
}

pub mod eval;

mod uncompressed {
    use capnp;

    pub fn write<T : ::std::old_io::Writer, U : capnp::message::MessageBuilder>(
        writer: &mut T,
        message: &U) {
        capnp::serialize::write_message(writer, message).unwrap();
    }

    pub fn write_buffered<T : ::std::old_io::Writer, U : capnp::message::MessageBuilder>(
        writer: &mut T,
        message: &U) {
        capnp::serialize::write_message(writer, message).unwrap();
    }

    pub fn new_buffered_reader<R: capnp::io::BufferedInputStream>(
        input_stream : &mut R,
        options : capnp::message::ReaderOptions) -> capnp::serialize::OwnedSpaceMessageReader {
        capnp::serialize::new_reader(input_stream, options).unwrap()
    }
}

mod packed {
    use capnp;
    use capnp::serialize_packed::{write_packed_message, write_packed_message_unbuffered};

    pub fn write<T : ::std::old_io::Writer, U : capnp::message::MessageBuilder>(
        writer: &mut T,
        message: &U) {
        write_packed_message_unbuffered(writer, message).unwrap();
    }

    pub fn write_buffered<T : capnp::io::BufferedOutputStream, U : capnp::message::MessageBuilder>(
        writer: &mut T,
        message: &U) {
        write_packed_message(writer, message).unwrap();
    }

    pub fn new_buffered_reader<R:capnp::io::BufferedInputStream>(
        input_stream : &mut R,
        options : capnp::message::ReaderOptions) -> capnp::serialize::OwnedSpaceMessageReader {
        capnp::serialize_packed::new_reader(input_stream, options).unwrap()
    }

}

const SCRATCH_SIZE : usize = 128 * 1024;

#[derive(Copy)]
pub struct NoScratch;

impl NoScratch {
    fn new_builder(&mut self, _idx : usize) -> capnp::message::MallocMessageBuilder {
        capnp::message::MallocMessageBuilder::new_default()
    }
}

pub struct UseScratch {
    scratch_space : ::std::vec::Vec<capnp::common::Word>
}

impl UseScratch {
    pub fn new() -> UseScratch {
        UseScratch {
            scratch_space : capnp::common::allocate_zeroed_words(SCRATCH_SIZE * 6)
        }
    }

    fn new_builder<'a>(&mut self, idx : usize) -> capnp::message::ScratchSpaceMallocMessageBuilder<'a> {
        assert!(idx < 6);
        unsafe {
            capnp::message::ScratchSpaceMallocMessageBuilder::new_default(
                ::std::mem::transmute(
                    ::std::raw::Slice { data : self.scratch_space.get_unchecked(idx * SCRATCH_SIZE),
                                      len : SCRATCH_SIZE }))
        }
    }
}

macro_rules! pass_by_object(
    ( $testcase:ident, $reuse:ident, $iters:expr ) => ({
            let mut rng = common::FastRand::new();
            for _ in range(0, $iters) {
                let mut message_req = $reuse.new_builder(0);
                let mut message_res = $reuse.new_builder(1);

                let expected = $testcase::setup_request(&mut rng,
                                                        message_req.init_root::<$testcase::RequestBuilder>());

                $testcase::handle_request(message_req.get_root::<$testcase::RequestBuilder>().as_reader(),
                                          message_res.init_root::<$testcase::ResponseBuilder>());

                if !$testcase::check_response(message_res.get_root::<$testcase::ResponseBuilder>().as_reader(),
                                              expected) {
                    panic!("Incorrect response.");
                }
            }
        });
    );

macro_rules! pass_by_bytes(
    ( $testcase:ident, $reuse:ident, $compression:ident, $iters:expr ) => ({
        let mut request_bytes : ::std::vec::Vec<u8> =
            ::std::iter::repeat(0u8).take(SCRATCH_SIZE * 8).collect();
        let mut response_bytes : ::std::vec::Vec<u8> =
            ::std::iter::repeat(0u8).take(SCRATCH_SIZE * 8).collect();
        let mut rng = common::FastRand::new();
        for _ in range(0, $iters) {
            let mut message_req = $reuse.new_builder(0);
            let mut message_res = $reuse.new_builder(1);

            let expected = {
                let request = message_req.init_root::<$testcase::RequestBuilder>();
                $testcase::setup_request(&mut rng, request)
            };

            {
                let response = message_res.init_root::<$testcase::ResponseBuilder>();

                {
                    let mut writer = capnp::io::ArrayOutputStream::new(request_bytes.as_mut_slice());
                    $compression::write_buffered(&mut writer, &message_req)
                }

                let message_reader = $compression::new_buffered_reader(
                    &mut capnp::io::ArrayInputStream::new(request_bytes.as_slice()),
                    capnp::message::DEFAULT_READER_OPTIONS);

                let request_reader : $testcase::RequestReader = message_reader.get_root();
                $testcase::handle_request(request_reader, response);
            }

            {
                let mut writer = capnp::io::ArrayOutputStream::new(response_bytes.as_mut_slice());
                $compression::write_buffered(&mut writer, &message_res)
            }

            let message_reader = $compression::new_buffered_reader(
                &mut capnp::io::ArrayInputStream::new(response_bytes.as_slice()),
                capnp::message::DEFAULT_READER_OPTIONS);

            let response_reader : $testcase::ResponseReader = message_reader.get_root();
            if !$testcase::check_response(response_reader, expected) {
                panic!("Incorrect response.");
            }
        }
    });
    );

macro_rules! server(
    ( $testcase:ident, $reuse:ident, $compression:ident, $iters:expr, $input:expr, $output:expr) => ({
            let mut out_buffered = capnp::io::BufferedOutputStreamWrapper::new(&mut $output);
            let mut in_buffered = capnp::io::BufferedInputStreamWrapper::new(&mut $input);
            for _ in range(0, $iters) {
                let mut message_res = $reuse.new_builder(0);

                {
                    let response = message_res.init_root::<$testcase::ResponseBuilder>();
                    let message_reader = $compression::new_buffered_reader(
                        &mut in_buffered,
                        capnp::message::DEFAULT_READER_OPTIONS);
                    let request_reader : $testcase::RequestReader = message_reader.get_root();
                    $testcase::handle_request(request_reader, response);
                }

                $compression::write_buffered(&mut out_buffered, &message_res);
                out_buffered.flush().unwrap();
            }
        });
    );

macro_rules! sync_client(
    ( $testcase:ident, $reuse:ident, $compression:ident, $iters:expr) => ({
            let mut out_stream = ::std::old_io::stdio::stdout_raw();
            let mut in_stream = ::std::old_io::stdio::stdin_raw();
            let mut in_buffered = capnp::io::BufferedInputStreamWrapper::new(&mut in_stream);
            let mut rng = common::FastRand::new();
            for _ in range(0, $iters) {
                let mut message_req = $reuse.new_builder(0);

                let expected = {
                    let request = message_req.init_root::<$testcase::RequestBuilder>();
                    $testcase::setup_request(&mut rng, request)
                };
                $compression::write(&mut out_stream, &message_req);

                let message_reader = $compression::new_buffered_reader(
                    &mut in_buffered,
                    capnp::message::DEFAULT_READER_OPTIONS);
                let response_reader : $testcase::ResponseReader = message_reader.get_root();
                assert!($testcase::check_response(response_reader, expected));

            }
        });
    );

macro_rules! pass_by_pipe(
    ( $testcase:ident, $reuse:ident, $compression:ident, $iters:expr) => ({
        use std::old_io::process;

        let mut args = ::std::os::args();
        args[2] = "client".to_string();

        let mut command = process::Command::new(args[0].as_slice());
        command.args(args.slice(1, args.len()));
        command.stdin(process::CreatePipe(true, false));
        command.stdout(process::CreatePipe(false, true));
        command.stderr(process::Ignored);
        match command.spawn() {
            Ok(ref mut p) => {
                let mut child_std_out = p.stdout.take().unwrap();
                let mut child_std_in = p.stdin.take().unwrap();

                server!($testcase, $reuse, $compression, $iters, child_std_out, child_std_in);
                println!("{}", p.wait().unwrap());
            }
            Err(e) => {
                println!("could not start process: {}", e);
            }
        }
    });
    );

macro_rules! do_testcase(
    ( $testcase:ident, $mode:expr, $reuse:ident, $compression:ident, $iters:expr ) => ({
            match $mode.as_slice() {
                "object" => pass_by_object!($testcase, $reuse, $iters),
                "bytes" => pass_by_bytes!($testcase, $reuse, $compression, $iters),
                "client" => sync_client!($testcase, $reuse, $compression, $iters),
                "server" => {
                    let mut input = ::std::old_io::stdio::stdin_raw();
                    let mut output = ::std::old_io::stdio::stdout_raw();
                    server!($testcase, $reuse, $compression, $iters, input, output)
                }
                "pipe" => pass_by_pipe!($testcase, $reuse, $compression, $iters),
                s => panic!("unrecognized mode: {}", s)
            }
        });
    );

macro_rules! do_testcase1(
    ( $testcase:expr, $mode:expr, $reuse:ident, $compression:ident, $iters:expr) => ({
            match $testcase.as_slice() {
                "carsales" => do_testcase!(carsales, $mode, $reuse, $compression, $iters),
                "catrank" => do_testcase!(catrank, $mode, $reuse, $compression, $iters),
                "eval" => do_testcase!(eval, $mode, $reuse, $compression, $iters),
                s => panic!("unrecognized test case: {}", s)
            }
        });
    );

macro_rules! do_testcase2(
    ( $testcase:expr, $mode:expr, $reuse:expr, $compression:ident, $iters:expr) => ({
            match $reuse.as_slice() {
                "no-reuse" => {
                    let mut scratch = NoScratch;
                    do_testcase1!($testcase, $mode, scratch, $compression, $iters)
                }
                "reuse" => {
                    let mut scratch = UseScratch::new();
                    do_testcase1!($testcase, $mode, scratch, $compression, $iters)
                }
                s => panic!("unrecognized reuse option: {}", s)
            }
        });
    );

pub fn main() {
    let args = ::std::os::args();

    if args.len() != 6 {
        println!("USAGE: {} CASE MODE REUSE COMPRESSION ITERATION_COUNT", args[0]);
        ::std::os::set_exit_status(1);
        return;
    }

    let iters = match args[5].parse::<u64>() {
        Ok(n) => n,
        Err(_) => {
            println!("Could not parse a u64 from: {}", args[5]);
            ::std::os::set_exit_status(1);
            return;
        }
    };

    match args[4].as_slice() {
        "none" => do_testcase2!(args[1], args[2],  args[3], uncompressed, iters),
        "packed" => do_testcase2!(args[1], args[2], args[3], packed, iters),
        s => panic!("unrecognized compression: {}", s)
    }
}
<|endoftext|>


<commit_before>pub mod parse;

use rustc_serialize::json::Json;

use super::{Document, Value};

#[derive(Debug, PartialEq)]
pub enum FilterParseError {
    ExpectedObject,
    ExpectedString,
    ExpectedArray,
    UnknownFilterType(String),
    NoFilter,
}

#[derive(Debug, PartialEq)]
pub enum Filter {
    Term{field: String, value: Value},
    Terms{field: String, values: Vec<Value>},
    Prefix{field: String, value: String},
    Missing{field: String},
    And{children: Vec<Filter>},
    Or{children: Vec<Filter>},
    Not{child: Box<Filter>},
}

#[derive(Debug, PartialEq)]
pub enum QueryParseError {
    ExpectedObject,
    ExpectedString,
    ExpectedArray,
    UnknownQueryType(String),
    NoQuery,
    FilteredNoFilter,
    FilteredNoQuery,
    FilterParseError(FilterParseError),
    MissingQueryString,
    MultiMatchMissingFields,
    InvalidQueryOperator,
    InvalidQueryBoost,
}

#[derive(Debug, PartialEq)]
pub enum QueryOperator {
    Or,
    And,
}

impl Default for QueryOperator {
    fn default() -> QueryOperator {
        QueryOperator::Or
    }
}

#[derive(Debug, PartialEq)]
pub enum Query {
    MatchAll{boost: f64},
    Match{field: String, query: String, operator: QueryOperator, boost: f64},
    MultiMatch{fields: Vec<String>, query: String, operator: QueryOperator, boost: f64},
    Filtered{query: Box<Query>, filter: Box<Filter>},
}

impl Filter {
    pub fn matches(&self, doc: &Document) -> bool {
        match *self {
            Filter::Term{ref field, ref value} => {
                if let Some(field_value) = doc.fields.get(field) {
                    return field_value == value;
                }

                false
            }
            Filter::Terms{ref field, ref values} => {
                if let Some(field_value) = doc.fields.get(field) {
                    for value in values.iter() {
                        if field_value == value {
                            return true;
                        }
                    }
                }

                false
            }
            Filter::Prefix{ref field, ref value} => {
                if let Some(field_value) = doc.fields.get(field) {
                    if let Value::String(ref field_value) = *field_value {
                        return field_value.starts_with(value);
                    }
                }

                false
            }
            Filter::Missing{ref field} => {
                match doc.fields.get(field) {
                    Some(&Value::Null) => true,
                    None => true,
                    _ => false,
                }
            }
            Filter::And{ref children} => {
                for child in children.iter() {
                    if !child.matches(doc) {
                        return false;
                    }
                }

                true
            }
            Filter::Or{ref children} => {
                for child in children.iter() {
                    if child.matches(doc) {
                        return true;
                    }
                }

                false
            }
            Filter::Not{ref child} => !child.matches(doc),
        }
    }
}

impl Query {
    pub fn rank(&self, doc: &Document) -> Option<f64> {
        match *self {
            Query::MatchAll{boost} => Some(boost),
            Query::Match{ref field, ref query, ref operator, boost} => {
                if let Some(&Value::String(ref field_value)) = doc.fields.get(field) {
                    let mut field_value = field_value.to_lowercase();
                    let mut query = query.to_lowercase();

                    if field_value.contains(&query) {
                        return Some(boost);
                    }
                }

                None
            }
            Query::MultiMatch{ref fields, ref query, ref operator, boost} => {
                for field in fields.iter() {
                    if let Some(&Value::String(ref field_value)) = doc.fields.get(field) {
                        let mut field_value = field_value.to_lowercase();
                        let mut query = query.to_lowercase();

                        if field_value.contains(&query) {
                            return Some(boost);
                        }
                    }
                }

                None
            }
            Query::Filtered{ref query, ref filter} => {
                if filter.matches(doc) {
                    return query.rank(doc)
                } else {
                    None
                }
            }
        }
    }

    pub fn matches(&self, doc: &Document) -> bool {
        match *self {
            Query::MatchAll{ref boost} => true,
            Query::Match{ref field, ref query, ref operator, ref boost} => {
                if let Some(&Value::String(ref field_value)) = doc.fields.get(field) {
                    let mut field_value = field_value.to_lowercase();
                    let mut query = query.to_lowercase();

                    return field_value.contains(&query);
                }

                false
            }
            Query::MultiMatch{ref fields, ref query, ref operator, ref boost} => {
                for field in fields.iter() {
                    if let Some(&Value::String(ref field_value)) = doc.fields.get(field) {
                        let mut field_value = field_value.to_lowercase();
                        let mut query = query.to_lowercase();

                        if field_value.contains(&query) {
                            return true;
                        }
                    }
                }

                false
            }
            Query::Filtered{ref query, ref filter} => {
                if filter.matches(doc) {
                    query.matches(doc)
                } else {
                    false
                }
            }
        }
    }
}
<commit_msg>Perform analysis on query strings and improve match algorithm<commit_after>pub mod parse;

use rustc_serialize::json::Json;

use super::analysis::Analyzer;
use super::{Document, Value};

#[derive(Debug, PartialEq)]
pub enum FilterParseError {
    ExpectedObject,
    ExpectedString,
    ExpectedArray,
    UnknownFilterType(String),
    NoFilter,
}

#[derive(Debug, PartialEq)]
pub enum Filter {
    Term{field: String, value: Value},
    Terms{field: String, values: Vec<Value>},
    Prefix{field: String, value: String},
    Missing{field: String},
    And{children: Vec<Filter>},
    Or{children: Vec<Filter>},
    Not{child: Box<Filter>},
}

#[derive(Debug, PartialEq)]
pub enum QueryParseError {
    ExpectedObject,
    ExpectedString,
    ExpectedArray,
    UnknownQueryType(String),
    NoQuery,
    FilteredNoFilter,
    FilteredNoQuery,
    FilterParseError(FilterParseError),
    MissingQueryString,
    MultiMatchMissingFields,
    InvalidQueryOperator,
    InvalidQueryBoost,
}

#[derive(Debug, PartialEq)]
pub enum QueryOperator {
    Or,
    And,
}

impl Default for QueryOperator {
    fn default() -> QueryOperator {
        QueryOperator::Or
    }
}

#[derive(Debug, PartialEq)]
pub enum Query {
    MatchAll{boost: f64},
    Match{field: String, query: String, operator: QueryOperator, boost: f64},
    MultiMatch{fields: Vec<String>, query: String, operator: QueryOperator, boost: f64},
    Filtered{query: Box<Query>, filter: Box<Filter>},
}

impl Filter {
    pub fn matches(&self, doc: &Document) -> bool {
        match *self {
            Filter::Term{ref field, ref value} => {
                if let Some(field_value) = doc.fields.get(field) {
                    return field_value == value;
                }

                false
            }
            Filter::Terms{ref field, ref values} => {
                if let Some(field_value) = doc.fields.get(field) {
                    for value in values.iter() {
                        if field_value == value {
                            return true;
                        }
                    }
                }

                false
            }
            Filter::Prefix{ref field, ref value} => {
                if let Some(field_value) = doc.fields.get(field) {
                    if let Value::String(ref field_value) = *field_value {
                        return field_value.starts_with(value);
                    }
                }

                false
            }
            Filter::Missing{ref field} => {
                match doc.fields.get(field) {
                    Some(&Value::Null) => true,
                    None => true,
                    _ => false,
                }
            }
            Filter::And{ref children} => {
                for child in children.iter() {
                    if !child.matches(doc) {
                        return false;
                    }
                }

                true
            }
            Filter::Or{ref children} => {
                for child in children.iter() {
                    if child.matches(doc) {
                        return true;
                    }
                }

                false
            }
            Filter::Not{ref child} => !child.matches(doc),
        }
    }
}

impl Query {
    pub fn rank(&self, doc: &Document) -> Option<f64> {
        match *self {
            Query::MatchAll{boost} => Some(boost),
            Query::Match{ref field, ref query, ref operator, boost} => {
                let query = Analyzer::Standard.run(query.clone());
                let mut matches = 0;

                for token in query.iter() {
                    let mut token_matched = false;

                    if let Some(&Value::TSVector(ref field_value)) = doc.fields.get(field) {
                        for field_token in field_value.iter() {
                            if token == field_token {
                                matches += 1;
                                token_matched = true;
                            }
                        }
                    }

                    // All tokens must match for queries with and operator
                    if operator == &QueryOperator::And && !token_matched {
                        return None;
                    }
                }

                if matches > 0 {
                    Some(matches as f64)
                } else {
                    None
                }
            }
            Query::MultiMatch{ref fields, ref query, ref operator, boost} => {
                let query = Analyzer::Standard.run(query.clone());
                let mut matches = 0;

                for token in query.iter() {
                    let mut token_matched = false;

                    for field in fields.iter() {
                        if let Some(&Value::TSVector(ref field_value)) = doc.fields.get(field) {
                            for field_token in field_value.iter() {
                                if token == field_token {
                                    matches += 1;
                                    token_matched = true;
                                }
                            }
                        }
                    }

                    // All tokens must match for queries with and operator
                    if operator == &QueryOperator::And && !token_matched {
                        return None;
                    }
                }

                if matches > 0 {
                    Some(matches as f64)
                } else {
                    None
                }
            }
            Query::Filtered{ref query, ref filter} => {
                if filter.matches(doc) {
                    return query.rank(doc)
                } else {
                    None
                }
            }
        }
    }

    pub fn matches(&self, doc: &Document) -> bool {
        match *self {
            Query::MatchAll{ref boost} => true,
            Query::Match{ref field, ref query, ref operator, ref boost} => {
                if let Some(&Value::String(ref field_value)) = doc.fields.get(field) {
                    let mut field_value = field_value.to_lowercase();
                    let mut query = query.to_lowercase();

                    return field_value.contains(&query);
                }

                false
            }
            Query::MultiMatch{ref fields, ref query, ref operator, ref boost} => {
                for field in fields.iter() {
                    if let Some(&Value::String(ref field_value)) = doc.fields.get(field) {
                        let mut field_value = field_value.to_lowercase();
                        let mut query = query.to_lowercase();

                        if field_value.contains(&query) {
                            return true;
                        }
                    }
                }

                false
            }
            Query::Filtered{ref query, ref filter} => {
                if filter.matches(doc) {
                    query.matches(doc)
                } else {
                    false
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>extern crate glm;

use shape::*;
use circle::*;

pub struct Rectangle {
    pub dims: glm::DVec2,
    pub center: glm::DVec2,
}

impl Rectangle {
    pub fn get_lines(&self) -> [Line; 4] {
        let tl = self.center + glm::dvec2(-self.dims.x / 2., -self.dims.y / 2.);
        let bl = self.center + glm::dvec2(-self.dims.x / 2., self.dims.y / 2.);
        let br = self.center + glm::dvec2(self.dims.x / 2., self.dims.y / 2.);
        let tr = self.center + glm::dvec2(self.dims.x / 2., -self.dims.y / 2.);

        [(tl, bl), (tl, tr), (bl, br), (br, tr)]
    }
}

impl Intersect<Rectangle> for Rectangle {
    fn intersects(&self, other: &Rectangle) -> bool {
        let half_x = self.dims.x / 2.;
        let half_y = self.dims.y / 2.;

        if self.center.x - half_x > other.center.x + other.dims.x / 2. {
            false
        } else if self.center.x + half_x < other.center.x - other.dims.x / 2. {
            false
        } else if self.center.y - half_y > other.center.y + other.dims.y / 2. {
            false
        } else if self.center.y + half_y < other.center.y - other.dims.y / 2. {
            false
        } else {
            true
        }
    }
}

impl Shape for Rectangle {
    fn get_center(&self) -> glm::DVec2 {
        self.center
    }

    fn contains(&self, point: &glm::DVec2) -> bool {
        let half_x = self.dims.x / 2.;
        let half_y = self.dims.y / 2.;

        if self.center.x - half_x > point.x {
            false
        } else if self.center.x + half_x < point.x {
            false
        } else if self.center.y - half_y > point.y {
            false
        } else if self.center.y + half_y < point.y {
            false
        } else {
            true
        }
    }
}

impl Intersect<Circle> for Rectangle {
    fn intersects(&self, other: &Circle) -> bool {
        other.intersects(self)
    }
}

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

    use super::*;
    use shape::*;
    use circle::*;

    #[test]
    fn rectangle_no_contains() {
        let rectangle = Rectangle {
            dims: glm::dvec2(2_f64, 3_f64),
            center: glm::dvec2(3_f64, 0_f64),
        };
        let point = glm::dvec2(3.5_f64, -1_f64);
        assert!(rectangle.contains(&point));
    }

    #[test]
    fn rectangle_contains() {
        let rectangle = Rectangle {
            dims: glm::dvec2(2_f64, 3_f64),
            center: glm::dvec2(3_f64, 0_f64),
        };
        let point = glm::dvec2(3.5_f64, 2_f64);
        assert!(!rectangle.contains(&point));
    }

    #[test]
    fn rectangle_rectangle_no_intersect() {
        let rectangle_a = Rectangle {
            dims: glm::dvec2(2_f64, 3_f64),
            center: glm::dvec2(3_f64, 0_f64),
        };

        let rectangle_b = Rectangle {
            dims: glm::dvec2(5_f64, 2_f64),
            center: glm::dvec2(0_f64, 8_f64),
        };
        assert!(!rectangle_a.intersects(&rectangle_b));
        assert!(!rectangle_b.intersects(&rectangle_a));
    }

    #[test]
    fn rectangle_rectangle_intersect() {
        let rectangle_a = Rectangle {
            dims: glm::dvec2(2_f64, 3_f64),
            center: glm::dvec2(3_f64, 0_f64),
        };

        let rectangle_b = Rectangle {
            dims: glm::dvec2(1_f64, 2_f64),
            center: glm::dvec2(2.5_f64, 1_f64),
        };
        assert!(rectangle_a.intersects(&rectangle_b));
        assert!(rectangle_b.intersects(&rectangle_a));
    }

    #[test]
    fn rectangle_circle_no_intersect() {
        let rectangle = Rectangle {
            dims: glm::dvec2(5_f64, 2_f64),
            center: glm::dvec2(6_f64, 0_f64),
        };

        let circle = Circle {
            radius: 3_f64,
            center: glm::dvec2(1_f64, 3_f64),
        };

        assert!(!rectangle.intersects(&circle));
    }

    #[test]
    fn rectangle_inside_circle() {
        let rectangle = Rectangle {
            dims: glm::dvec2(1_f64, 2_f64),
            center: glm::dvec2(3_f64, 5_f64),
        };

        let circle = Circle {
            radius: 5_f64,
            center: glm::dvec2(2_f64, 3_f64),
        };

        assert!(rectangle.intersects(&circle));
    }

    #[test]
    fn circle_inside_rectangle() {
        let rectangle = Rectangle {
            dims: glm::dvec2(5_f64, 7_f64),
            center: glm::dvec2(4_f64, 2_f64),
        };

        let circle = Circle {
            radius: 1_f64,
            center: glm::dvec2(5_f64, 3_f64),
        };

        assert!(rectangle.intersects(&circle));
    }

    #[test]
    fn rectangle_circle_intersect() {
        let rectangle = Rectangle {
            dims: glm::dvec2(2_f64, 2_f64),
            center: glm::dvec2(5_f64, 3_f64),
        };

        let circle = Circle {
            radius: 2_f64,
            center: glm::dvec2(2_f64, 3_f64),
        };

        assert!(rectangle.intersects(&circle));
    }
}
<commit_msg>moved impl of Shape for Rectangle above impl for Intersect<commit_after>extern crate glm;

use shape::*;
use circle::*;

pub struct Rectangle {
    pub dims: glm::DVec2,
    pub center: glm::DVec2,
}

impl Rectangle {
    pub fn get_lines(&self) -> [Line; 4] {
        let tl = self.center + glm::dvec2(-self.dims.x / 2., -self.dims.y / 2.);
        let bl = self.center + glm::dvec2(-self.dims.x / 2., self.dims.y / 2.);
        let br = self.center + glm::dvec2(self.dims.x / 2., self.dims.y / 2.);
        let tr = self.center + glm::dvec2(self.dims.x / 2., -self.dims.y / 2.);

        [(tl, bl), (tl, tr), (bl, br), (br, tr)]
    }
}

impl Shape for Rectangle {
    fn get_center(&self) -> glm::DVec2 {
        self.center
    }

    fn contains(&self, point: &glm::DVec2) -> bool {
        let half_x = self.dims.x / 2.;
        let half_y = self.dims.y / 2.;

        if self.center.x - half_x > point.x {
            false
        } else if self.center.x + half_x < point.x {
            false
        } else if self.center.y - half_y > point.y {
            false
        } else if self.center.y + half_y < point.y {
            false
        } else {
            true
        }
    }
}

impl Intersect<Rectangle> for Rectangle {
    fn intersects(&self, other: &Rectangle) -> bool {
        let half_x = self.dims.x / 2.;
        let half_y = self.dims.y / 2.;

        if self.center.x - half_x > other.center.x + other.dims.x / 2. {
            false
        } else if self.center.x + half_x < other.center.x - other.dims.x / 2. {
            false
        } else if self.center.y - half_y > other.center.y + other.dims.y / 2. {
            false
        } else if self.center.y + half_y < other.center.y - other.dims.y / 2. {
            false
        } else {
            true
        }
    }
}

impl Intersect<Circle> for Rectangle {
    fn intersects(&self, other: &Circle) -> bool {
        other.intersects(self)
    }
}

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

    use super::*;
    use shape::*;
    use circle::*;

    #[test]
    fn rectangle_no_contains() {
        let rectangle = Rectangle {
            dims: glm::dvec2(2_f64, 3_f64),
            center: glm::dvec2(3_f64, 0_f64),
        };
        let point = glm::dvec2(3.5_f64, -1_f64);
        assert!(rectangle.contains(&point));
    }

    #[test]
    fn rectangle_contains() {
        let rectangle = Rectangle {
            dims: glm::dvec2(2_f64, 3_f64),
            center: glm::dvec2(3_f64, 0_f64),
        };
        let point = glm::dvec2(3.5_f64, 2_f64);
        assert!(!rectangle.contains(&point));
    }

    #[test]
    fn rectangle_rectangle_no_intersect() {
        let rectangle_a = Rectangle {
            dims: glm::dvec2(2_f64, 3_f64),
            center: glm::dvec2(3_f64, 0_f64),
        };

        let rectangle_b = Rectangle {
            dims: glm::dvec2(5_f64, 2_f64),
            center: glm::dvec2(0_f64, 8_f64),
        };
        assert!(!rectangle_a.intersects(&rectangle_b));
        assert!(!rectangle_b.intersects(&rectangle_a));
    }

    #[test]
    fn rectangle_rectangle_intersect() {
        let rectangle_a = Rectangle {
            dims: glm::dvec2(2_f64, 3_f64),
            center: glm::dvec2(3_f64, 0_f64),
        };

        let rectangle_b = Rectangle {
            dims: glm::dvec2(1_f64, 2_f64),
            center: glm::dvec2(2.5_f64, 1_f64),
        };
        assert!(rectangle_a.intersects(&rectangle_b));
        assert!(rectangle_b.intersects(&rectangle_a));
    }

    #[test]
    fn rectangle_circle_no_intersect() {
        let rectangle = Rectangle {
            dims: glm::dvec2(5_f64, 2_f64),
            center: glm::dvec2(6_f64, 0_f64),
        };

        let circle = Circle {
            radius: 3_f64,
            center: glm::dvec2(1_f64, 3_f64),
        };

        assert!(!rectangle.intersects(&circle));
    }

    #[test]
    fn rectangle_inside_circle() {
        let rectangle = Rectangle {
            dims: glm::dvec2(1_f64, 2_f64),
            center: glm::dvec2(3_f64, 5_f64),
        };

        let circle = Circle {
            radius: 5_f64,
            center: glm::dvec2(2_f64, 3_f64),
        };

        assert!(rectangle.intersects(&circle));
    }

    #[test]
    fn circle_inside_rectangle() {
        let rectangle = Rectangle {
            dims: glm::dvec2(5_f64, 7_f64),
            center: glm::dvec2(4_f64, 2_f64),
        };

        let circle = Circle {
            radius: 1_f64,
            center: glm::dvec2(5_f64, 3_f64),
        };

        assert!(rectangle.intersects(&circle));
    }

    #[test]
    fn rectangle_circle_intersect() {
        let rectangle = Rectangle {
            dims: glm::dvec2(2_f64, 2_f64),
            center: glm::dvec2(5_f64, 3_f64),
        };

        let circle = Circle {
            radius: 2_f64,
            center: glm::dvec2(2_f64, 3_f64),
        };

        assert!(rectangle.intersects(&circle));
    }
}
<|endoftext|>


<commit_before>//! Reexports of crates, that are part of the public api, for convenience

pub use calloop;
#[cfg(feature = "dbus")]
pub use dbus;
#[cfg(feature = "backend_drm")]
pub use drm;
#[cfg(feature = "backend_drm_gbm")]
pub use gbm;
#[cfg(feature = "backend_drm_gbm")]
pub use image;
#[cfg(feature = "backend_libinput")]
pub use input;
#[cfg(any(feature = "backend_udev", feature = "backend_drm"))]
pub use nix;
#[cfg(feature = "backend_udev")]
pub use udev;
#[cfg(feature = "wayland_frontend")]
pub use wayland_commons;
#[cfg(feature = "wayland_frontend")]
pub use wayland_protocols;
#[cfg(feature = "wayland_frontend")]
pub use wayland_server;
<commit_msg>reexports: reexport winit<commit_after>//! Reexports of crates, that are part of the public api, for convenience

pub use calloop;
#[cfg(feature = "dbus")]
pub use dbus;
#[cfg(feature = "backend_drm")]
pub use drm;
#[cfg(feature = "backend_drm_gbm")]
pub use gbm;
#[cfg(feature = "backend_drm_gbm")]
pub use image;
#[cfg(feature = "backend_libinput")]
pub use input;
#[cfg(any(feature = "backend_udev", feature = "backend_drm"))]
pub use nix;
#[cfg(feature = "backend_udev")]
pub use udev;
#[cfg(feature = "wayland_frontend")]
pub use wayland_commons;
#[cfg(feature = "wayland_frontend")]
pub use wayland_protocols;
#[cfg(feature = "wayland_frontend")]
pub use wayland_server;
#[cfg(feature = "backend_winit")]
pub use winit;<|endoftext|>


<commit_before>// Copyright (C) 2018 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: MPL-2.0
//

use failure::Error;
use reqwest::Client;
use reqwest::header::{ContentType, Headers, UserAgent};

use std::time::Duration;

use firmware::Metadata;
use runtime_settings::RuntimeSettings;
use settings::Settings;

use update_package::UpdatePackage;

header! { (ApiContentType, "Api-Content-Type") => [String] }
header! { (ApiRetries, "Api-Retries") => [usize] }
header! { (AddExtraPoll, "Add-Extra-Poll") => [i64] }

pub struct Api<'a> {
    settings: &'a Settings,
    firmware: &'a Metadata,
    runtime_settings: &'a RuntimeSettings,
}

#[derive(Debug)]
pub enum ProbeResponse {
    NoUpdate,
    Update(UpdatePackage),
    ExtraPoll(i64),
}

impl<'a> Api<'a> {
    pub fn new(
        settings: &'a Settings,
        runtime_settings: &'a RuntimeSettings,
        firmware: &'a Metadata,
    ) -> Api<'a> {
        Api {
            settings,
            runtime_settings,
            firmware,
        }
    }

    fn client(&self) -> Result<Client, Error> {
        let mut headers = Headers::new();

        headers.set(UserAgent::new("updatehub/next"));
        headers.set(ContentType::json());
        headers.set(ApiContentType("application/vnd.updatehub-v1+json".into()));

        Ok(Client::builder()
            .timeout(Duration::from_secs(10))
            .default_headers(headers)
            .build()?)
    }

    pub fn probe(&self) -> Result<ProbeResponse, Error> {
        use reqwest::StatusCode;

        let mut response = self.client()?
            .post(&format!(
                "{}/upgrades",
                &self.settings.network.server_address
            ))
            .header(ApiRetries(self.runtime_settings.polling.retries))
            .json(&self.firmware)
            .send()?;

        match response.status() {
            StatusCode::NotFound => Ok(ProbeResponse::NoUpdate),
            StatusCode::Ok => {
                if let Some(extra_poll) = response.headers().get::<AddExtraPoll>() {
                    return Ok(ProbeResponse::ExtraPoll(extra_poll.0));
                }

                Ok(ProbeResponse::Update(
                    UpdatePackage::parse(&response.text()?)?,
                ))
            }
            _ => bail!("Invalid response. Status: {}", response.status()),
        }
    }

    pub fn download_object(&self, package_uid: &str, object: &str) -> Result<(), Error> {
        // FIXME: Discuss the need of packages inside the route
        let mut client = self.client()?.get(&format!(
            "{}/products/{}/packages/{}/objects/{}",
            &self.settings.network.server_address, &self.firmware.product_uid, package_uid, object
        ));

        let _ = client.send()?;

        Ok(())
    }
}

#[cfg(test)]
pub mod tests {
    use super::*;
    use mockito::Mock;

    pub enum FakeServer {
        NoUpdate,
        HasUpdate,
        ExtraPoll,
        ErrorOnce,
        InvalidHardware,
    }

    pub fn create_mock_server(server: FakeServer) -> Mock {
        use mockito::{mock, Matcher};
        use update_package::tests::get_update_json;

        match server {
            FakeServer::NoUpdate => mock("POST", "/upgrades")
                .match_header("Content-Type", "application/json")
                .match_header("Api-Content-Type", "application/vnd.updatehub-v1+json")
                .match_body(Matcher::JSON(json!(
                        {
                            "product_uid": "229ffd7e08721d716163fc81a2dbaf6c90d449f0a3b009b6a2defe8a0b0d7381",
                            "version": "1.1",
                            "hardware": "board",
                            "device_identity": {
                                "id1":["value1"],
                                "id2":["value2"]
                            },
                            "device_attributes": {
                                "attr1":["attrvalue1"],
                                "attr2":["attrvalue2"]
                            }
                        }
                    )))
                .with_status(404)
                .create(),

            FakeServer::HasUpdate => mock("POST", "/upgrades")
                .match_header("Content-Type", "application/json")
                .match_header("Api-Content-Type", "application/vnd.updatehub-v1+json")
                .match_body(Matcher::JSON(json!(
                        {
                            "product_uid": "229ffd7e08721d716163fc81a2dbaf6c90d449f0a3b009b6a2defe8a0b0d7381",
                            "version": "1.1",
                            "hardware": "board",
                            "device_identity": {
                                "id1":["value2"],
                                "id2":["value2"]
                            },
                            "device_attributes": {
                                "attr1":["attrvalue1"],
                                "attr2":["attrvalue2"]
                            }
                        }
                    )))
                .with_status(200)
                .with_body(&get_update_json().to_string())
                .create(),

            FakeServer::ExtraPoll => mock("POST", "/upgrades")
                .match_header("Content-Type", "application/json")
                .match_header("Api-Content-Type", "application/vnd.updatehub-v1+json")
                .match_body(Matcher::JSON(json!(
                                             {
                                                 "product_uid": "229ffd7e08721d716163fc81a2dbaf6c90d449f0a3b009b6a2defe8a0b0d7381",
                                                 "version": "1.1",
                                                 "hardware": "board",
                                                 "device_identity": {
                                                     "id1":["value3"],
                                                     "id2":["value2"]
                                                 },
                                                 "device_attributes": {
                                                     "attr1":["attrvalue1"],
                                                     "attr2":["attrvalue2"]
                                                 }
                                             }
                                         )))
                .with_status(200)
                .with_header("Add-Extra-Poll", "10")
                .create(),

            FakeServer::ErrorOnce => mock("POST", "/upgrades")
                .match_header("Content-Type", "application/json")
                .match_header("Api-Content-Type", "application/vnd.updatehub-v1+json")
                .match_header("Api-Retries", "1")
                .match_body(Matcher::JSON(json!(
                                             {
                                                 "product_uid": "229ffd7e08721d716163fc81a2dbaf6c90d449f0a3b009b6a2defe8a0b0d7381",
                                                 "version": "1.1",
                                                 "hardware": "board",
                                                 "device_identity": {
                                                     "id1":["value1"],
                                                     "id2":["value2"]
                                                 },
                                                 "device_attributes": {
                                                     "attr1":["attrvalue1"],
                                                     "attr2":["attrvalue2"]
                                                 },
                                             }
                                         )))
                .with_status(404)
                .create(),
            FakeServer::InvalidHardware => mock("POST", "/upgrades")
                .match_header("Content-Type", "application/json")
                .match_header("Api-Content-Type", "application/vnd.updatehub-v1+json")
                .match_body(Matcher::JSON(json!(
                                             {
                                                 "product_uid": "229ffd7e08721d716163fc81a2dbaf6c90d449f0a3b009b6a2defe8a0b0d7381",
                                                 "version": "1.1",
                                                 "hardware": "invalid",
                                                 "device_identity": {
                                                     "id1":["value4"],
                                                     "id2":["value2"]
                                                 },
                                                 "device_attributes": {
                                                     "attr1":["attrvalue1"],
                                                     "attr2":["attrvalue2"]
                                                 }
                                             }
                                         )))
                .with_status(200)
                .with_body(&get_update_json().to_string())
                .create(),
        }
    }

    #[test]
    fn probe_requirements() {
        use firmware::tests::{create_fake_metadata, FakeDevice};

        let mock = create_mock_server(FakeServer::NoUpdate);
        let _ = Api::new(
            &Settings::default(),
            &RuntimeSettings::default(),
            &Metadata::new(&create_fake_metadata(FakeDevice::NoUpdate)).unwrap(),
        ).probe();
        mock.assert();
    }

    #[test]
    fn download_object() {
        use firmware::tests::{create_fake_metadata, FakeDevice};
        use mockito::mock;

        let metadata = Metadata::new(&create_fake_metadata(FakeDevice::NoUpdate)).unwrap();

        let m = mock(
            "GET",
            format!(
                "/products/{}/packages/{}/objects/{}",
                metadata.product_uid, "package_id", "object"
            ).as_str(),
        ).match_header("Content-Type", "application/json")
            .match_header("Api-Content-Type", "application/vnd.updatehub-v1+json")
            .with_status(200)
            .create();

        let _ = Api::new(&Settings::default(), &RuntimeSettings::default(), &metadata)
            .download_object("package_id", "object")
            .unwrap();

        m.assert();
    }
}
<commit_msg>client: tests: Reduce duplication in mocked server<commit_after>// Copyright (C) 2018 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: MPL-2.0
//

use failure::Error;
use reqwest::Client;
use reqwest::header::{ContentType, Headers, UserAgent};

use std::time::Duration;

use firmware::Metadata;
use runtime_settings::RuntimeSettings;
use settings::Settings;

use update_package::UpdatePackage;

header! { (ApiContentType, "Api-Content-Type") => [String] }
header! { (ApiRetries, "Api-Retries") => [usize] }
header! { (AddExtraPoll, "Add-Extra-Poll") => [i64] }

pub struct Api<'a> {
    settings: &'a Settings,
    firmware: &'a Metadata,
    runtime_settings: &'a RuntimeSettings,
}

#[derive(Debug)]
pub enum ProbeResponse {
    NoUpdate,
    Update(UpdatePackage),
    ExtraPoll(i64),
}

impl<'a> Api<'a> {
    pub fn new(
        settings: &'a Settings,
        runtime_settings: &'a RuntimeSettings,
        firmware: &'a Metadata,
    ) -> Api<'a> {
        Api {
            settings,
            runtime_settings,
            firmware,
        }
    }

    fn client(&self) -> Result<Client, Error> {
        let mut headers = Headers::new();

        headers.set(UserAgent::new("updatehub/next"));
        headers.set(ContentType::json());
        headers.set(ApiContentType("application/vnd.updatehub-v1+json".into()));

        Ok(Client::builder()
            .timeout(Duration::from_secs(10))
            .default_headers(headers)
            .build()?)
    }

    pub fn probe(&self) -> Result<ProbeResponse, Error> {
        use reqwest::StatusCode;

        let mut response = self.client()?
            .post(&format!(
                "{}/upgrades",
                &self.settings.network.server_address
            ))
            .header(ApiRetries(self.runtime_settings.polling.retries))
            .json(&self.firmware)
            .send()?;

        match response.status() {
            StatusCode::NotFound => Ok(ProbeResponse::NoUpdate),
            StatusCode::Ok => {
                if let Some(extra_poll) = response.headers().get::<AddExtraPoll>() {
                    return Ok(ProbeResponse::ExtraPoll(extra_poll.0));
                }

                Ok(ProbeResponse::Update(
                    UpdatePackage::parse(&response.text()?)?,
                ))
            }
            _ => bail!("Invalid response. Status: {}", response.status()),
        }
    }

    pub fn download_object(&self, package_uid: &str, object: &str) -> Result<(), Error> {
        // FIXME: Discuss the need of packages inside the route
        let mut client = self.client()?.get(&format!(
            "{}/products/{}/packages/{}/objects/{}",
            &self.settings.network.server_address, &self.firmware.product_uid, package_uid, object
        ));

        let _ = client.send()?;

        Ok(())
    }
}

#[cfg(test)]
pub mod tests {
    use super::*;
    use mockito::Mock;

    pub enum FakeServer {
        NoUpdate,
        HasUpdate,
        ExtraPoll,
        ErrorOnce,
        InvalidHardware,
    }

    pub fn create_mock_server(server: FakeServer) -> Mock {
        use mockito::{mock, Matcher};
        use update_package::tests::get_update_json;

        fn fake_device_reply_body(identity: usize, hardware: &str) -> Matcher {
            Matcher::JSON(json!(
                        {
                            "product_uid": "229ffd7e08721d716163fc81a2dbaf6c90d449f0a3b009b6a2defe8a0b0d7381",
                            "version": "1.1",
                            "hardware": hardware,
                            "device_identity": {
                                "id1":[format!("value{}", identity)],
                                "id2":["value2"]
                            },
                            "device_attributes": {
                                "attr1":["attrvalue1"],
                                "attr2":["attrvalue2"]
                            }
                        }
            ))
        }

        match server {
            FakeServer::NoUpdate => mock("POST", "/upgrades")
                .match_header("Content-Type", "application/json")
                .match_header("Api-Content-Type", "application/vnd.updatehub-v1+json")
                .match_body(fake_device_reply_body(1, "board"))
                .with_status(404)
                .create(),

            FakeServer::HasUpdate => mock("POST", "/upgrades")
                .match_header("Content-Type", "application/json")
                .match_header("Api-Content-Type", "application/vnd.updatehub-v1+json")
                .match_body(fake_device_reply_body(2, "board"))
                .with_status(200)
                .with_body(&get_update_json().to_string())
                .create(),

            FakeServer::ExtraPoll => mock("POST", "/upgrades")
                .match_header("Content-Type", "application/json")
                .match_header("Api-Content-Type", "application/vnd.updatehub-v1+json")
                .match_body(fake_device_reply_body(3, "board"))
                .with_status(200)
                .with_header("Add-Extra-Poll", "10")
                .create(),

            FakeServer::ErrorOnce => mock("POST", "/upgrades")
                .match_header("Content-Type", "application/json")
                .match_header("Api-Content-Type", "application/vnd.updatehub-v1+json")
                .match_header("Api-Retries", "1")
                .match_body(fake_device_reply_body(1, "board"))
                .with_status(404)
                .create(),
            FakeServer::InvalidHardware => mock("POST", "/upgrades")
                .match_header("Content-Type", "application/json")
                .match_header("Api-Content-Type", "application/vnd.updatehub-v1+json")
                .match_body(fake_device_reply_body(4, "invalid"))
                .with_status(200)
                .with_body(&get_update_json().to_string())
                .create(),
        }
    }

    #[test]
    fn probe_requirements() {
        use firmware::tests::{create_fake_metadata, FakeDevice};

        let mock = create_mock_server(FakeServer::NoUpdate);
        let _ = Api::new(
            &Settings::default(),
            &RuntimeSettings::default(),
            &Metadata::new(&create_fake_metadata(FakeDevice::NoUpdate)).unwrap(),
        ).probe();
        mock.assert();
    }

    #[test]
    fn download_object() {
        use firmware::tests::{create_fake_metadata, FakeDevice};
        use mockito::mock;

        let metadata = Metadata::new(&create_fake_metadata(FakeDevice::NoUpdate)).unwrap();

        let m = mock(
            "GET",
            format!(
                "/products/{}/packages/{}/objects/{}",
                metadata.product_uid, "package_id", "object"
            ).as_str(),
        ).match_header("Content-Type", "application/json")
            .match_header("Api-Content-Type", "application/vnd.updatehub-v1+json")
            .with_status(200)
            .create();

        let _ = Api::new(&Settings::default(), &RuntimeSettings::default(), &metadata)
            .download_object("package_id", "object")
            .unwrap();

        m.assert();
    }
}
<|endoftext|>