<commit_before>use std::error::Error;

use tokio::runtime::Runtime;
use log::warn;
use hyper::{Uri, Body, Request};
use hyper::header::CONTENT_TYPE;
use hyper::client::{Client, HttpConnector};
use hyper_tls::HttpsConnector;

use crate::Notice;
use crate::AirbrakeConfig;

pub struct AirbrakeClient {
    client: Client<HttpsConnector<HttpConnector>>,
    config: AirbrakeConfig
}

impl AirbrakeClient {
    pub fn new(config: AirbrakeConfig) -> AirbrakeClient {
        let connector = HttpsConnector::new();
        let client = Client::builder().build(connector);

        AirbrakeClient {
            client: client,
            config: config
        }
    }

    fn request<T>(&self, uri: Uri, payload: T) -> Request<Body>
    where T: Into<Body>
    {
        Request::post(uri)
            .header(CONTENT_TYPE, "application/json")
            .body(payload.into())
            .unwrap()
    }

    async fn send(&self, request: Request<Body>) -> () {
        let response = self.client.request(request).await;
        match response {
            Ok ( response ) => (),
            Err ( x ) => warn!("notification failed")
        }
    }

    pub fn notify(&self, notice: Notice) {
        let endpoint = self.config.endpoint_uri();
        let request = self.request(endpoint, notice);

        let mut runtime = Runtime::new().unwrap();
        runtime.block_on(self.send(request));
    }
}

#[cfg(test)]
mod tests {
    use std::io::Error;
    use super::Notifier;
    use config::Config;

    #[test]
    fn close_doesnt_panic() {
        let mut notifier = Notifier::new(Config::new());
        notifier.close();
    }

    #[test]
    #[should_panic(expected="attempted to close an already closed Airbrake notifier")]
    fn double_close_panics() {
        let mut notifier = Notifier::new(Config::new());
        notifier.close();
        notifier.close();
    }

    #[test]
    #[should_panic(expected="attempted to send through a closed Airbrake notifier")]
    fn notify_with_closed_notifier_panics() {
        let mut notifier = Notifier::new(Config::new());
        notifier.close();
        notifier.notify(Error::last_os_error());
    }
}
<commit_msg>Set message on error that should never happen<commit_after>use std::error::Error;

use tokio::runtime::Runtime;
use log::warn;
use hyper::{Uri, Body, Request};
use hyper::header::CONTENT_TYPE;
use hyper::client::{Client, HttpConnector};
use hyper_tls::HttpsConnector;

use crate::Notice;
use crate::AirbrakeConfig;

pub struct AirbrakeClient {
    client: Client<HttpsConnector<HttpConnector>>,
    config: AirbrakeConfig
}

impl AirbrakeClient {
    pub fn new(config: AirbrakeConfig) -> AirbrakeClient {
        let connector = HttpsConnector::new();
        let client = Client::builder().build(connector);

        AirbrakeClient {
            client: client,
            config: config
        }
    }

    fn request<T>(&self, uri: Uri, payload: T) -> Request<Body>
    where T: Into<Body>
    {
        Request::post(uri)
            .header(CONTENT_TYPE, "application/json")
            .body(payload.into())
            .expect("Request creation failed unexpectedly")
    }

    async fn send(&self, request: Request<Body>) -> () {
        let response = self.client.request(request).await;
        match response {
            Ok ( response ) => (),
            Err ( x ) => warn!("notification failed")
        }
    }

    pub fn notify(&self, notice: Notice) {
        let endpoint = self.config.endpoint_uri();
        let request = self.request(endpoint, notice);

        let mut runtime = Runtime::new().unwrap();
        runtime.block_on(self.send(request));
    }
}

#[cfg(test)]
mod tests {
    use std::io::Error;
    use super::Notifier;
    use config::Config;

    #[test]
    fn close_doesnt_panic() {
        let mut notifier = Notifier::new(Config::new());
        notifier.close();
    }

    #[test]
    #[should_panic(expected="attempted to close an already closed Airbrake notifier")]
    fn double_close_panics() {
        let mut notifier = Notifier::new(Config::new());
        notifier.close();
        notifier.close();
    }

    #[test]
    #[should_panic(expected="attempted to send through a closed Airbrake notifier")]
    fn notify_with_closed_notifier_panics() {
        let mut notifier = Notifier::new(Config::new());
        notifier.close();
        notifier.notify(Error::last_os_error());
    }
}
<|endoftext|>


<commit_before>extern crate yaml_rust;

use std::error::Error;
use std::path::Path;
use util;
use self::yaml_rust::{YamlLoader, Yaml};

// I may have gotten a little carried away with these macros..

// Due to limitations of the macro system and the YAML library, we must use a generic solution
// to retrieve values dynamically.
fn yaml_to_string(yaml: &Yaml) -> Option<String> {
    use self::yaml_rust::Yaml::*;

    match *yaml {
        Real(ref string) | String(ref string) =>
            Some(string.clone()),
        Integer(num) => Some(format!("{}", num)),
        _ => None,
    }
}

macro_rules! gen_value {
    // Option
    ($parent:expr, $disp_name:expr, None) => {{
        yaml_to_string(&$parent[$disp_name])
            .and_then(|s| s.parse().ok())
    }};

    // Option with minimum
    ($parent:expr, $disp_name:expr, [$min:expr, None]) => {{
        let result = gen_value!($parent, $disp_name, None);
        result.map(|v| if v < $min { $min } else { v })
    }};

    // Value with minimum
    ($parent:expr, $disp_name:expr, [$min:expr, $default:expr]) => {{
        let result = gen_value!($parent, $disp_name, $default);
        if result < $min { $min } else { result }
    }};

    // Value
    ($parent:expr, $disp_name:expr, $default:expr) => {{
        yaml_to_string(&$parent[$disp_name])
            .and_then(|s| s.parse().ok())
            .unwrap_or($default)
    }};
}

macro_rules! create_config_section {
    ($yaml_name:expr, $name:ident, $($field:ident: $field_t:ty => $disp_name:expr => $default:tt,)+) => {
        #[derive(Debug)]
        pub struct $name {
            $(pub $field: $field_t,)+
        }

        impl $name {
            pub fn new(doc: &Yaml) -> $name {
                let parent = &doc[$yaml_name];

                $name {
                    $($field: gen_value!(parent, $disp_name, $default),)+
                }
            }
        }
    };
}

macro_rules! try_opt {
    ($value:expr) => {{
        match $value {
            Some(v) => v,
            None    => return None,
        }
    }};
}

macro_rules! create_config_arr {
    ($yaml_name:expr, $name:ident, $($field:ident: $field_type:ty => $disp_name:expr,)+) => {
        #[derive(Debug)]
        pub struct $name {
            $(pub $field: $field_type,)+
        }

        impl $name {
            pub fn parse(doc: &Yaml) -> Vec<$name> {
                let parent = &doc[$yaml_name];

                parent.as_vec()
                    .unwrap_or(&Vec::new())
                    .iter()
                    .filter_map(|field| {
                        Some($name {
                            $($field:
                                try_opt!(
                                    yaml_to_string(&field[$disp_name])
                                    .and_then(|s| s.parse().ok())
                                ),)+
                        })
                    })
                    .collect()
            }
        }
    };
}

create_config_arr!("Feed Percentages", FeedPercentage,
    name:  String => "Name",
    spike: f32    => "Spike",
);

create_config_section!("Global", Global,
	spike:                   f32        => "Spike"                                => 0.20,
	unskewed_reset_pcnt:     f32        => "Unskewed Reset Percentage"            => [0.0, 0.15],
	unskewed_adjust_pcnt:    f32        => "Unskewed Adjust Percentage"           => [0.0, 0.01],
	low_listener_increase:   f32        => "Low Listener Increase Percentage"     => [0.0, 0.005],
	high_listener_dec:       f32        => "High Listener Decrease Percentage"    => [0.0, 0.02],
	high_listener_dec_every: f32        => "High Listener Decrease Per Listeners" => [1.0, 100.0],
	update_time:             f32        => "Update Time"                          => [5.0, 6.0],
	minimum_listeners:       u32        => "Minimum Listeners"                    => 15,
	state_feeds_id:          Option<u8> => "State Feeds ID"                       => None,
);

#[derive(Debug)]
pub struct Config {
    pub global:           Global,
    pub feed_percentages: Vec<FeedPercentage>,
    pub blacklist:        Vec<String>,
}

pub fn load_from_file(path: &Path) -> Result<Config, Box<Error>> {
    let doc = YamlLoader::load_from_str(&util::read_file(path)?)?;
    let doc = &doc[0]; // We don't care about multiple documents

    Ok(Config {
        global:           Global::new(&doc),
        feed_percentages: FeedPercentage::parse(&doc),
        blacklist:        Vec::new(),
    })
}<commit_msg>Increase default spike percentage<commit_after>extern crate yaml_rust;

use std::error::Error;
use std::path::Path;
use util;
use self::yaml_rust::{YamlLoader, Yaml};

// I may have gotten a little carried away with these macros..

// Due to limitations of the macro system and the YAML library, we must use a generic solution
// to retrieve values dynamically.
fn yaml_to_string(yaml: &Yaml) -> Option<String> {
    use self::yaml_rust::Yaml::*;

    match *yaml {
        Real(ref string) | String(ref string) =>
            Some(string.clone()),
        Integer(num) => Some(format!("{}", num)),
        _ => None,
    }
}

macro_rules! gen_value {
    // Option
    ($parent:expr, $disp_name:expr, None) => {{
        yaml_to_string(&$parent[$disp_name])
            .and_then(|s| s.parse().ok())
    }};

    // Option with minimum
    ($parent:expr, $disp_name:expr, [$min:expr, None]) => {{
        let result = gen_value!($parent, $disp_name, None);
        result.map(|v| if v < $min { $min } else { v })
    }};

    // Value with minimum
    ($parent:expr, $disp_name:expr, [$min:expr, $default:expr]) => {{
        let result = gen_value!($parent, $disp_name, $default);
        if result < $min { $min } else { result }
    }};

    // Value
    ($parent:expr, $disp_name:expr, $default:expr) => {{
        yaml_to_string(&$parent[$disp_name])
            .and_then(|s| s.parse().ok())
            .unwrap_or($default)
    }};
}

macro_rules! create_config_section {
    ($yaml_name:expr, $name:ident, $($field:ident: $field_t:ty => $disp_name:expr => $default:tt,)+) => {
        #[derive(Debug)]
        pub struct $name {
            $(pub $field: $field_t,)+
        }

        impl $name {
            pub fn new(doc: &Yaml) -> $name {
                let parent = &doc[$yaml_name];

                $name {
                    $($field: gen_value!(parent, $disp_name, $default),)+
                }
            }
        }
    };
}

macro_rules! try_opt {
    ($value:expr) => {{
        match $value {
            Some(v) => v,
            None    => return None,
        }
    }};
}

macro_rules! create_config_arr {
    ($yaml_name:expr, $name:ident, $($field:ident: $field_type:ty => $disp_name:expr,)+) => {
        #[derive(Debug)]
        pub struct $name {
            $(pub $field: $field_type,)+
        }

        impl $name {
            pub fn parse(doc: &Yaml) -> Vec<$name> {
                let parent = &doc[$yaml_name];

                parent.as_vec()
                    .unwrap_or(&Vec::new())
                    .iter()
                    .filter_map(|field| {
                        Some($name {
                            $($field:
                                try_opt!(
                                    yaml_to_string(&field[$disp_name])
                                    .and_then(|s| s.parse().ok())
                                ),)+
                        })
                    })
                    .collect()
            }
        }
    };
}

create_config_arr!("Feed Percentages", FeedPercentage,
    name:  String => "Name",
    spike: f32    => "Spike",
);

create_config_section!("Global", Global,
	spike:                   f32        => "Spike"                                => 0.25,
	unskewed_reset_pcnt:     f32        => "Unskewed Reset Percentage"            => [0.0, 0.15],
	unskewed_adjust_pcnt:    f32        => "Unskewed Adjust Percentage"           => [0.0, 0.01],
	low_listener_increase:   f32        => "Low Listener Increase Percentage"     => [0.0, 0.005],
	high_listener_dec:       f32        => "High Listener Decrease Percentage"    => [0.0, 0.02],
	high_listener_dec_every: f32        => "High Listener Decrease Per Listeners" => [1.0, 100.0],
	update_time:             f32        => "Update Time"                          => [5.0, 6.0],
	minimum_listeners:       u32        => "Minimum Listeners"                    => 15,
	state_feeds_id:          Option<u8> => "State Feeds ID"                       => None,
);

#[derive(Debug)]
pub struct Config {
    pub global:           Global,
    pub feed_percentages: Vec<FeedPercentage>,
    pub blacklist:        Vec<String>,
}

pub fn load_from_file(path: &Path) -> Result<Config, Box<Error>> {
    let doc = YamlLoader::load_from_str(&util::read_file(path)?)?;
    let doc = &doc[0]; // We don't care about multiple documents

    Ok(Config {
        global:           Global::new(&doc),
        feed_percentages: FeedPercentage::parse(&doc),
        blacklist:        Vec::new(),
    })
}<|endoftext|>


<commit_before>use mio::Token;

#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub struct Cookie(u64);

impl Cookie {
    #[inline(always)]
    pub fn new(token: Token, nonce: u64) -> Cookie {
        Cookie((token.0 << 48) as u64 | (nonce & 0xFFFFFFFFFFFF))
    }

    #[inline(always)]
    pub fn token(self) -> Token {
        Token((self.0 >> 48) as usize)
    }

    #[inline(always)]
    pub fn nonce(self) -> u64 {
        self.0 & 0xFFFFFFFFFFFF
    }
}
<commit_msg>add cookie tests<commit_after>use mio::Token;

#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub struct Cookie(u64);

impl Cookie {
    #[inline(always)]
    pub fn new(token: Token, nonce: u64) -> Cookie {
        // TODO: panic on overflow?
        Cookie((token.0 << 48) as u64 | (nonce & 0xFFFFFFFFFFFF))
    }

    #[inline(always)]
    pub fn token(self) -> Token {
        Token((self.0 >> 48) as usize)
    }

    #[inline(always)]
    pub fn nonce(self) -> u64 {
        self.0 & 0xFFFFFFFFFFFF
    }
}

mod tests {
    use super::Cookie;
    use mio::Token;

    #[test]
    fn round_trip() {
        let a = Cookie::new(Token(1), 1);
        assert_eq!(a.token(), Token(1));
        assert_eq!(a.nonce(), 1);
        let a = Cookie::new(Token((1 << 16) - 1), (1 << 48) - 1);
        assert_eq!(a.token(), Token((1 << 16) - 1));
        assert_eq!(a.nonce(), (1 << 48) - 1);
    }

    #[test]
    fn overflow() {
        let a = Cookie::new(Token((1 << 16) + 1), (1 << 48) + 1);
        assert_eq!(a.token(), Token(1));
        assert_eq!(a.nonce(), 1);
    }
}
<|endoftext|>


<commit_before>use na::Vector3;

trait Point2 {
    fn from_origin(&self) -> Vector2<f64>;
}

trait Point3 {
    fn from_origin(&self) -> Vector3<f64>;
}

trait SpatialTree<T : Point3> {
    fn find_closest_point(&self, p : Point3) -> T;
    fn find_closest_point_within_range(&self, p : Point3, range : f64) -> T;
    fn find_k_nearest_points(&self, p : Point3, k: u32) -> Box<Iterator<Item=T>>;
    fn find_k_nearest_points_within_range(&self, p : Point3, k : u32, range : f64) -> Box<Iterator<Item=T>>;
}<commit_msg>Added plane, improved naming and added some domain comments<commit_after>use na::Vector3;

/// A two dimensional point with a defined vector from the origin
trait Point2 {
    fn from_origin(&self) -> Vector2<f64>;
}

/// A three dimensional point with a defined vector from the origin
trait Point3 {
    fn from_origin(&self) -> Vector3<f64>;
}

/// A three dimensional plane defined by a point3 and a vector3
struct Plane3 {
    position : Point3,
    normal : Vector3<f64>
}

/// An approach capable of answering spatial queries
trait SpatialQueryStrategy<T : Point3> {
    fn find_closest_point(&self, p : Point3) -> T;
    fn find_closest_point_within_range(&self, p : Point3, range : f64) -> T;
    fn find_k_nearest_points(&self, p : Point3, k: u32) -> Box<Iterator<Item=T>>;
    fn find_k_nearest_points_within_range(&self, p : Point3, k : u32, range : f64) -> Box<Iterator<Item=T>>;
}<|endoftext|>


<commit_before>#![allow(dead_code)]
use std::{cmp, result, io, fmt};
use std::error;
use std::boxed::Box;

use protobuf::ProtobufError;

#[derive(Debug)]
pub enum Error {
    Io(io::Error),
    NotFound,
    Store(StorageError),
    Other(Box<error::Error + Send + Sync>),
}

#[derive(Debug, PartialEq)]
pub enum StorageError {
    Compacted,
    Unavailable,
    SnapshotOutOfDate,
}

impl StorageError {
    pub fn string(&self) -> &str {
        match self {
            &StorageError::Compacted => "log compacted",
            &StorageError::Unavailable => "log unavailable",
            &StorageError::SnapshotOutOfDate => "snapshot out of date",
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &Error::Io(ref e) => fmt::Display::fmt(e, f),
            &Error::Store(ref e) => fmt::Display::fmt(e, f),
            &Error::Other(ref e) => fmt::Display::fmt(e, f),
            &Error::NotFound => fmt::Display::fmt("not found", f),
        }
    }
}

impl fmt::Display for StorageError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(self.string(), f)
    }
}

impl cmp::PartialEq for Error {
    fn eq(&self, other: &Error) -> bool {
        match (self, other) {
            (&Error::NotFound, &Error::NotFound) => true,
            (&Error::Store(ref e1), &Error::Store(ref e2)) => e1 == e2,
            (&Error::Io(ref e1), &Error::Io(ref e2)) => e1.kind() == e2.kind(),
            // should not compare directly
            (&Error::Other(ref e1), &Error::Other(ref e2)) => {
                panic!("Shoud not compare boxed error directly {}， {}", e1, e2)
            }
            _ => false,
        }
    }
}

impl error::Error for StorageError {
    fn description(&self) -> &str {
        self.string()
    }

    fn cause(&self) -> Option<&error::Error> {
        None
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match self {
            // not sure that cause should be included in message
            &Error::Io(ref e) => e.description(),
            &Error::Store(ref e) => e.description(),
            &Error::Other(ref e) => e.description(),
            &Error::NotFound => "not found",
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match self {
            &Error::Io(ref e) => Some(e),
            &Error::Other(ref e) => e.cause(),
            _ => None,
        }
    }
}

// other can convert e to Error::Other.
pub fn other<E>(e: E) -> Error
    where E: Into<Box<error::Error + Send + Sync>>
{
    Error::Other(e.into())
}

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

impl From<ProtobufError> for Error {
    fn from(err: ProtobufError) -> Error {
        other(err)
    }
}

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

#[cfg(test)]
mod tests {
    use std::error;
    use protobuf::ProtobufError;

    use super::*;

    #[test]
    fn test_error() {
        let e = ProtobufError::WireError("a error".to_string());
        let err: Error = other(e);
        assert!(error::Error::cause(&err).is_none());

        let err1 = other("hello world");
        assert!(error::Error::cause(&err1).is_none());
    }

    #[test]
    fn test_equal() {
        assert_eq!(Error::NotFound, Error::NotFound);
        assert!(Error::NotFound != Error::Store(StorageError::Compacted));
        assert_eq!(Error::Store(StorageError::Compacted),
                   Error::Store(StorageError::Compacted));
    }
}
<commit_msg>raft: use quick_error<commit_after>#![allow(dead_code)]
use std::{cmp, result, io};

quick_error! {
    #[derive(Debug)]
    pub enum Error {
        Io(err: io::Error) {
            from()
            cause(err)
            description(err.description())
        }
        Store(err: StorageError) {
            from()
            cause(err)
            description(err.description())
        }
        StepLocalMsg {
            description("raft: cannot step raft local message")
        }
        StepPeerNotFound {
            description("raft: cannot step as peer not found")
        }
    }
}

quick_error! {
    #[derive(Debug, PartialEq)]
    pub enum StorageError {
        Compacted {
            description("log compacted")
        }
        Unavailable {
            description("log unavailable")
        }
        SnapshotOutOfDate {
            description("snapshot out of date")
        }
    }
}

impl cmp::PartialEq for Error {
    fn eq(&self, other: &Error) -> bool {
        match (self, other) {
            (&Error::StepPeerNotFound, &Error::StepPeerNotFound) => true,
            (&Error::Store(ref e1), &Error::Store(ref e2)) => e1 == e2,
            (&Error::Io(ref e1), &Error::Io(ref e2)) => e1.kind() == e2.kind(),
            (&Error::StepLocalMsg, &Error::StepLocalMsg) => true,
            _ => false,
        }
    }
}

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

#[cfg(test)]
mod tests {
    use std::io;
    use super::*;

    #[test]
    fn test_equal() {
        assert_eq!(Error::StepPeerNotFound, Error::StepPeerNotFound);
        assert!(Error::StepPeerNotFound != Error::Store(StorageError::Compacted));
        assert_eq!(Error::Store(StorageError::Compacted),
                   Error::Store(StorageError::Compacted));
        assert_eq!(Error::StepLocalMsg, Error::StepLocalMsg);
        assert_eq!(Error::from(io::Error::new(io::ErrorKind::Other, "oh no!")),
                   Error::from(io::Error::new(io::ErrorKind::Other, "oh yes!")));
    }
}
<|endoftext|>


<commit_before>//! Functions for filtering images.
// TODO: unify with image::sample

use image::{
    GenericImage,
    ImageBuffer,
    Luma,
    Pixel
};

use integralimage::{
    column_running_sum,
    row_running_sum
};

use traits::{
    Clamp,
    ToFloat
};

/// Convolves an 8bpp grayscale image with a kernel of width (2 * x_radius + 1)
/// and height (2 * y_radius + 1) whose entries are equal and
/// sum to one. i.e. each output pixel is the unweighted mean of
/// a rectangular region surrounding its corresponding input pixel.
/// We handle locations where the kernel would extend past the image's
/// boundary by treating the image as if its boundary pixels were
/// repeated indefinitely.
// TODO: for small kernels we probably want to do the convolution
// TODO: directly instead of using an integral image.
// TODO: more formats!
// TODO: number of operations is constant with kernel size,
// TODO: but this is still _really_ slow. fix!
pub fn box_filter<I: GenericImage<Pixel=Luma<u8>> + 'static>(
    image: &I,
    x_radius: u32,
    y_radius: u32)
    -> ImageBuffer<Luma<u8>, Vec<u8>>
    where I::Pixel: 'static,
          <I::Pixel as Pixel>::Subpixel: 'static {

    let (width, height) = image.dimensions();
    let mut out = ImageBuffer::new(width, height);

    let kernel_width = 2 * x_radius + 1;
    let kernel_height = 2 * y_radius + 1;

    let mut row_buffer = vec![0; (width + 2 * x_radius) as usize];
    for y in 0..height {
        row_running_sum(image, y, &mut row_buffer, x_radius);
        let val = row_buffer[(2 * x_radius) as usize]/kernel_width;
        out.put_pixel(0, y, Luma([val as u8]));
        for x in 1..width {
            // TODO: This way we pay rounding errors for each of the
            // TODO: x and y convolutions. Is there a better way?
            let u = (x + 2 * x_radius) as usize;
            let l = (x - 1) as usize;
            let val = (row_buffer[u] - row_buffer[l])/kernel_width;
            out.put_pixel(x, y, Luma([val as u8]));
        }
    }

    let mut col_buffer = vec![0; (height + 2 * y_radius) as usize];
    for x in 0..width {
        column_running_sum(&out, x, &mut col_buffer, y_radius);
        let val = col_buffer[(2 * y_radius) as usize]/kernel_height;
        out.put_pixel(x, 0, Luma([val as u8]));
        for y in 1..height {
            let u = (y + 2 * y_radius) as usize;
            let l = (y - 1) as usize;
            let val = (col_buffer[u] - col_buffer[l])/kernel_height;
            out.put_pixel(x, y, Luma([val as u8]));
        }
    }

    out
}

///	Returns horizontal correlations between an image and a 1d kernel.
/// Pads by continuity.
pub fn horizontal_filter<I: GenericImage + 'static>(
    image: &I, kernel: &[f32])
    -> ImageBuffer<I::Pixel, Vec<<I::Pixel as Pixel>::Subpixel>>
    where I::Pixel: 'static,
          <I::Pixel as Pixel>::Subpixel: ToFloat + Clamp + 'static {

    let (width, height) = image.dimensions();
    let mut out = copy(image);

    if width == 0 || height == 0 {
        return out;
    }

    // TODO: Add Default instances for pixels
    let pix = image.get_pixel(0, 0);
    let num_channels = I::Pixel::channel_count() as usize;
    let k = kernel.len() as u32;
    let mut buffer = vec![pix; (width + k/2 + k/2) as usize];

	for y in 0..height {
        let left_pad  = image.get_pixel(0, y);
        let right_pad = image.get_pixel(width - 1, y);

	    pad_buffer(&mut buffer, k/2, left_pad, right_pad);

        for x in 0..width {
            buffer[(x + k/2) as usize] = image.get_pixel(x, y);
        }

        for x in k/2..(width + k/2) {
            let mut acc = vec![0f32; num_channels];

            for z in 0..k {
                let p = buffer[(x + z - k/2) as usize];
                let weight = kernel[z as usize];
                accumulate(&mut acc, &p, weight, num_channels);
            }

            for c in acc.iter_mut() {
                *c /= k as f32;
            }

            let mut out_pixel = pix;
            clamp_acc(&acc, &mut out_pixel, num_channels);

            out.put_pixel(x - k/2, y, out_pixel);
        }
	}

    out
}

///	Returns horizontal correlations between an image and a 1d kernel.
/// Pads by continuity.
// TODO: shares too much code with horizontal_filter
pub fn vertical_filter<I: GenericImage + 'static>(
    image: &I, kernel: &[f32])
    -> ImageBuffer<I::Pixel, Vec<<I::Pixel as Pixel>::Subpixel>>
    where I::Pixel: 'static,
          <I::Pixel as Pixel>::Subpixel: ToFloat + Clamp + 'static {

    let (width, height) = image.dimensions();
    let mut out = copy(image);

    if width == 0 || height == 0 {
        return out;
    }

    let pix = image.get_pixel(0, 0);
    let num_channels = I::Pixel::channel_count() as usize;
    let k = kernel.len() as u32;
    let mut buffer = vec![pix; (height + k/2 + k/2) as usize];

	for x in 0..width {
        let left_pad  = image.get_pixel(x, 0);
        let right_pad = image.get_pixel(x, height - 1);

	    pad_buffer(&mut buffer, k/2, left_pad, right_pad);

        for y in 0..height {
            buffer[(y + k/2) as usize] = image.get_pixel(x, y);
        }

        for y in k/2..(height + k/2) {
            let mut acc = vec![0f32; num_channels];

            for z in 0..k {
                let p = buffer[(y + z - k/2) as usize];
                let weight = kernel[z as usize];
                accumulate(&mut acc, &p, weight, num_channels);
            }

            for c in acc.iter_mut() {
                *c /= k as f32;
            }

            let mut out_pixel = pix;
            clamp_acc(&acc, &mut out_pixel, num_channels);

            out.put_pixel(x, y - k/2, out_pixel);
        }
	}

    out
}

pub fn copy<I: GenericImage + 'static>(image: &I)
    -> ImageBuffer<I::Pixel, Vec<<I::Pixel as Pixel>::Subpixel>>
    where I::Pixel: 'static,
          <I::Pixel as Pixel>::Subpixel: 'static {
    let mut out = ImageBuffer::new(image.width(), image.height());
    out.copy_from(image, 0, 0);
    out
}

fn accumulate<P>(acc: &mut [f32], pixel: &P, weight: f32, num_channels: usize)
    where P: Pixel + 'static, <P as Pixel>::Subpixel : ToFloat {
    for i in 0..num_channels {
        acc[i as usize]
            += pixel.channels()[i].to_float() * weight;
    }
}

fn clamp_acc<P>(acc: &[f32], pixel: &mut P, num_channels: usize)
    where P: Pixel + 'static, <P as Pixel>::Subpixel: Clamp{
    for i in 0..num_channels {
        pixel.channels_mut()[i] = P::Subpixel::clamp(acc[i]);
    }
}

/// Fills the left margin entries in buffer with left_val and the
// right margin entries with right_val
fn pad_buffer<T>(buffer: &mut[T], margin: u32, left_val: T, right_val: T)
    where T : Copy {
    for i in 0..margin {
        buffer[i as usize] = left_val;
    }

    for i in (buffer.len() - margin as usize)..buffer.len() {
        buffer[i] = right_val;
    }
}

#[cfg(test)]
mod test {

    use super::{
        box_filter,
        horizontal_filter,
        pad_buffer,
        vertical_filter
    };
    use utils::{
        gray_bench_image
    };
    use image::{
        GrayImage,
        ImageBuffer
    };
    use test;

    #[test]
    fn test_box_filter() {
        let image: GrayImage = ImageBuffer::from_raw(3, 3, vec![
            1u8, 2u8, 3u8,
            4u8, 5u8, 6u8,
            7u8, 8u8, 9u8]).unwrap();

        // For this image we get the same answer from the two 1d
        // convolutions as from doing the 2d convolution in one step
        // (but we needn't in general, as in the former case we're
        // clipping to an integer value twice).
        let expected: GrayImage = ImageBuffer::from_raw(3, 3, vec![
            2u8, 3u8, 3u8,
            4u8, 5u8, 5u8,
            6u8, 7u8, 7u8]).unwrap();

        assert_pixels_eq!(box_filter(&image, 1, 1), expected);
    }

    #[bench]
    fn bench_box_filter(b: &mut test::Bencher) {
        let image = gray_bench_image(500, 500);
        b.iter(|| {
            let filtered = box_filter(&image, 7, 7);
            test::black_box(filtered);
            });
    }

    #[test]
    fn test_pad_buffer() {
        let mut a = [0, 1, 2, 3, 0];
        pad_buffer(&mut a, 1, 4, 5);
        assert_eq!(a, [4, 1, 2, 3, 5]);
        pad_buffer(&mut a, 2, 8, 9);
        assert_eq!(a, [8, 8, 2, 9, 9]);

        let mut b = [0, 1, 2, 0];
        pad_buffer(&mut b, 1, 4, 5);
        assert_eq!(b, [4, 1, 2, 5]);
        pad_buffer(&mut b, 2, 8, 9);
        assert_eq!(b, [8, 8, 9, 9]);
    }

    #[test]
    fn test_horizontal_filter() {
        let image: GrayImage = ImageBuffer::from_raw(3, 3, vec![
            1u8, 4u8, 1u8,
            4u8, 7u8, 4u8,
            1u8, 4u8, 1u8]).unwrap();

        let expected: GrayImage = ImageBuffer::from_raw(3, 3, vec![
            2u8, 2u8, 2u8,
            5u8, 5u8, 5u8,
            2u8, 2u8, 2u8]).unwrap();

        let kernel = vec![1f32, 1f32, 1f32];
        let filtered = horizontal_filter(&image, &kernel);

        assert_pixels_eq!(filtered, expected);
    }

    #[bench]
    fn bench_horizontal_filter(b: &mut test::Bencher) {
        let image = gray_bench_image(500, 500);
        let kernel = vec![1f32; 5];
        b.iter(|| {
            let filtered = horizontal_filter(&image, &kernel);
            test::black_box(filtered);
            });
    }

    #[test]
    fn test_vertical_filter() {
        let image: GrayImage = ImageBuffer::from_raw(3, 3, vec![
            1u8, 4u8, 1u8,
            4u8, 7u8, 4u8,
            1u8, 4u8, 1u8]).unwrap();

        let expected: GrayImage = ImageBuffer::from_raw(3, 3, vec![
            2u8, 5u8, 2u8,
            2u8, 5u8, 2u8,
            2u8, 5u8, 2u8]).unwrap();

        let kernel = vec![1f32, 1f32, 1f32];
        let filtered = vertical_filter(&image, &kernel);

        assert_pixels_eq!(filtered, expected);
    }

    #[bench]
    fn bench_vertical_filter(b: &mut test::Bencher) {
        let image = gray_bench_image(500, 500);
        let kernel = vec![1f32; 5];
        b.iter(|| {
            let filtered = vertical_filter(&image, &kernel);
            test::black_box(filtered);
            });
    }
}
<commit_msg>Add separable_filter and separable_filter_equal. Fix horizontal_filter and vertical_filter to not divide output intensity by kernel size<commit_after>//! Functions for filtering images.
// TODO: unify with image::sample

use image::{
    GenericImage,
    ImageBuffer,
    Luma,
    Pixel
};

use integralimage::{
    column_running_sum,
    row_running_sum
};

use traits::{
    Clamp,
    ToFloat
};

/// Convolves an 8bpp grayscale image with a kernel of width (2 * x_radius + 1)
/// and height (2 * y_radius + 1) whose entries are equal and
/// sum to one. i.e. each output pixel is the unweighted mean of
/// a rectangular region surrounding its corresponding input pixel.
/// We handle locations where the kernel would extend past the image's
/// boundary by treating the image as if its boundary pixels were
/// repeated indefinitely.
// TODO: for small kernels we probably want to do the convolution
// TODO: directly instead of using an integral image.
// TODO: more formats!
// TODO: number of operations is constant with kernel size,
// TODO: but this is still _really_ slow. fix!
pub fn box_filter<I: GenericImage<Pixel=Luma<u8>> + 'static>(
    image: &I,
    x_radius: u32,
    y_radius: u32)
    -> ImageBuffer<Luma<u8>, Vec<u8>>
    where I::Pixel: 'static,
          <I::Pixel as Pixel>::Subpixel: 'static {

    let (width, height) = image.dimensions();
    let mut out = ImageBuffer::new(width, height);

    let kernel_width = 2 * x_radius + 1;
    let kernel_height = 2 * y_radius + 1;

    let mut row_buffer = vec![0; (width + 2 * x_radius) as usize];
    for y in 0..height {
        row_running_sum(image, y, &mut row_buffer, x_radius);
        let val = row_buffer[(2 * x_radius) as usize]/kernel_width;
        out.put_pixel(0, y, Luma([val as u8]));
        for x in 1..width {
            // TODO: This way we pay rounding errors for each of the
            // TODO: x and y convolutions. Is there a better way?
            let u = (x + 2 * x_radius) as usize;
            let l = (x - 1) as usize;
            let val = (row_buffer[u] - row_buffer[l])/kernel_width;
            out.put_pixel(x, y, Luma([val as u8]));
        }
    }

    let mut col_buffer = vec![0; (height + 2 * y_radius) as usize];
    for x in 0..width {
        column_running_sum(&out, x, &mut col_buffer, y_radius);
        let val = col_buffer[(2 * y_radius) as usize]/kernel_height;
        out.put_pixel(x, 0, Luma([val as u8]));
        for y in 1..height {
            let u = (y + 2 * y_radius) as usize;
            let l = (y - 1) as usize;
            let val = (col_buffer[u] - col_buffer[l])/kernel_height;
            out.put_pixel(x, y, Luma([val as u8]));
        }
    }

    out
}

/// Returns 2d correlation of view with the outer product of the 1d
/// kernels h_filter and v_filter.
pub fn separable_filter<I: GenericImage + 'static>(
    image: &I, h_kernel: &[f32], v_kernel: &[f32])
    -> ImageBuffer<I::Pixel, Vec<<I::Pixel as Pixel>::Subpixel>>
    where I::Pixel: 'static,
          <I::Pixel as Pixel>::Subpixel: ToFloat + Clamp + 'static {
    let h = horizontal_filter(image, h_kernel);
    let v = vertical_filter(&h, v_kernel);
    v
}

/// Returns 2d correlation of view with the outer product of the 1d
/// kernel filter with itself
pub fn separable_filter_equal<I: GenericImage + 'static>(
    image: &I, kernel: &[f32])
    -> ImageBuffer<I::Pixel, Vec<<I::Pixel as Pixel>::Subpixel>>
    where I::Pixel: 'static,
          <I::Pixel as Pixel>::Subpixel: ToFloat + Clamp + 'static {
    separable_filter(image, kernel, kernel)
}

///	Returns horizontal correlations between an image and a 1d kernel.
/// Pads by continuity.
pub fn horizontal_filter<I: GenericImage + 'static>(
    image: &I, kernel: &[f32])
    -> ImageBuffer<I::Pixel, Vec<<I::Pixel as Pixel>::Subpixel>>
    where I::Pixel: 'static,
          <I::Pixel as Pixel>::Subpixel: ToFloat + Clamp + 'static {

    let (width, height) = image.dimensions();
    let mut out = copy(image);

    if width == 0 || height == 0 {
        return out;
    }

    // TODO: Add Default instances for pixels
    let pix = image.get_pixel(0, 0);
    let num_channels = I::Pixel::channel_count() as usize;
    let k = kernel.len() as u32;
    let mut buffer = vec![pix; (width + k/2 + k/2) as usize];

	for y in 0..height {
        let left_pad  = image.get_pixel(0, y);
        let right_pad = image.get_pixel(width - 1, y);

	    pad_buffer(&mut buffer, k/2, left_pad, right_pad);

        for x in 0..width {
            buffer[(x + k/2) as usize] = image.get_pixel(x, y);
        }

        for x in k/2..(width + k/2) {
            let mut acc = vec![0f32; num_channels];

            for z in 0..k {
                let p = buffer[(x + z - k/2) as usize];
                let weight = kernel[z as usize];
                accumulate(&mut acc, &p, weight, num_channels);
            }

            let mut out_pixel = pix;
            clamp_acc(&acc, &mut out_pixel, num_channels);

            out.put_pixel(x - k/2, y, out_pixel);
        }
	}

    out
}

///	Returns horizontal correlations between an image and a 1d kernel.
/// Pads by continuity.
// TODO: shares too much code with horizontal_filter
pub fn vertical_filter<I: GenericImage + 'static>(
    image: &I, kernel: &[f32])
    -> ImageBuffer<I::Pixel, Vec<<I::Pixel as Pixel>::Subpixel>>
    where I::Pixel: 'static,
          <I::Pixel as Pixel>::Subpixel: ToFloat + Clamp + 'static {

    let (width, height) = image.dimensions();
    let mut out = copy(image);

    if width == 0 || height == 0 {
        return out;
    }

    let pix = image.get_pixel(0, 0);
    let num_channels = I::Pixel::channel_count() as usize;
    let k = kernel.len() as u32;
    let mut buffer = vec![pix; (height + k/2 + k/2) as usize];

	for x in 0..width {
        let left_pad  = image.get_pixel(x, 0);
        let right_pad = image.get_pixel(x, height - 1);

	    pad_buffer(&mut buffer, k/2, left_pad, right_pad);

        for y in 0..height {
            buffer[(y + k/2) as usize] = image.get_pixel(x, y);
        }

        for y in k/2..(height + k/2) {
            let mut acc = vec![0f32; num_channels];

            for z in 0..k {
                let p = buffer[(y + z - k/2) as usize];
                let weight = kernel[z as usize];
                accumulate(&mut acc, &p, weight, num_channels);
            }

            let mut out_pixel = pix;
            clamp_acc(&acc, &mut out_pixel, num_channels);

            out.put_pixel(x, y - k/2, out_pixel);
        }
	}

    out
}

pub fn copy<I: GenericImage + 'static>(image: &I)
    -> ImageBuffer<I::Pixel, Vec<<I::Pixel as Pixel>::Subpixel>>
    where I::Pixel: 'static,
          <I::Pixel as Pixel>::Subpixel: 'static {
    let mut out = ImageBuffer::new(image.width(), image.height());
    out.copy_from(image, 0, 0);
    out
}

fn accumulate<P>(acc: &mut [f32], pixel: &P, weight: f32, num_channels: usize)
    where P: Pixel + 'static, <P as Pixel>::Subpixel : ToFloat {
    for i in 0..num_channels {
        acc[i as usize]
            += pixel.channels()[i].to_float() * weight;
    }
}

fn clamp_acc<P>(acc: &[f32], pixel: &mut P, num_channels: usize)
    where P: Pixel + 'static, <P as Pixel>::Subpixel: Clamp{
    for i in 0..num_channels {
        pixel.channels_mut()[i] = P::Subpixel::clamp(acc[i]);
    }
}

/// Fills the left margin entries in buffer with left_val and the
// right margin entries with right_val
fn pad_buffer<T>(buffer: &mut[T], margin: u32, left_val: T, right_val: T)
    where T : Copy {
    for i in 0..margin {
        buffer[i as usize] = left_val;
    }

    for i in (buffer.len() - margin as usize)..buffer.len() {
        buffer[i] = right_val;
    }
}

#[cfg(test)]
mod test {

    use super::{
        box_filter,
        horizontal_filter,
        pad_buffer,
        separable_filter,
        separable_filter_equal,
        vertical_filter
    };
    use utils::{
        gray_bench_image
    };
    use image::{
        GrayImage,
        ImageBuffer
    };
    use test;

    #[test]
    fn test_box_filter() {
        let image: GrayImage = ImageBuffer::from_raw(3, 3, vec![
            1u8, 2u8, 3u8,
            4u8, 5u8, 6u8,
            7u8, 8u8, 9u8]).unwrap();

        // For this image we get the same answer from the two 1d
        // convolutions as from doing the 2d convolution in one step
        // (but we needn't in general, as in the former case we're
        // clipping to an integer value twice).
        let expected: GrayImage = ImageBuffer::from_raw(3, 3, vec![
            2u8, 3u8, 3u8,
            4u8, 5u8, 5u8,
            6u8, 7u8, 7u8]).unwrap();

        assert_pixels_eq!(box_filter(&image, 1, 1), expected);
    }

    #[bench]
    fn bench_box_filter(b: &mut test::Bencher) {
        let image = gray_bench_image(500, 500);
        b.iter(|| {
            let filtered = box_filter(&image, 7, 7);
            test::black_box(filtered);
            });
    }

    #[test]
    fn test_pad_buffer() {
        let mut a = [0, 1, 2, 3, 0];
        pad_buffer(&mut a, 1, 4, 5);
        assert_eq!(a, [4, 1, 2, 3, 5]);
        pad_buffer(&mut a, 2, 8, 9);
        assert_eq!(a, [8, 8, 2, 9, 9]);

        let mut b = [0, 1, 2, 0];
        pad_buffer(&mut b, 1, 4, 5);
        assert_eq!(b, [4, 1, 2, 5]);
        pad_buffer(&mut b, 2, 8, 9);
        assert_eq!(b, [8, 8, 9, 9]);
    }

    #[test]
    fn test_separable_filter() {
        let image: GrayImage = ImageBuffer::from_raw(3, 3, vec![
            1u8, 2u8, 3u8,
            4u8, 5u8, 6u8,
            7u8, 8u8, 9u8]).unwrap();

        // Lazily copying the box_filter test case
        let expected: GrayImage = ImageBuffer::from_raw(3, 3, vec![
            2u8, 3u8, 3u8,
            4u8, 5u8, 5u8,
            6u8, 7u8, 7u8]).unwrap();

        let kernel = vec![1f32/3f32; 3];
        let filtered = separable_filter_equal(&image, &kernel);

        assert_pixels_eq!(filtered, expected);
    }

    #[bench]
    fn bench_separable_filter(b: &mut test::Bencher) {
        let image = gray_bench_image(500, 500);
        let h_kernel = vec![1f32/5f32; 5];
        let v_kernel = vec![0.1f32, 0.4f32, 0.3f32, 0.1f32, 0.1f32];
        b.iter(|| {
            let filtered = separable_filter(&image, &h_kernel, &v_kernel);
            test::black_box(filtered);
            });
    }

    #[test]
    fn test_horizontal_filter() {
        let image: GrayImage = ImageBuffer::from_raw(3, 3, vec![
            1u8, 4u8, 1u8,
            4u8, 7u8, 4u8,
            1u8, 4u8, 1u8]).unwrap();

        let expected: GrayImage = ImageBuffer::from_raw(3, 3, vec![
            2u8, 2u8, 2u8,
            5u8, 5u8, 5u8,
            2u8, 2u8, 2u8]).unwrap();

        let kernel = vec![1f32/3f32, 1f32/3f32, 1f32/3f32];
        let filtered = horizontal_filter(&image, &kernel);

        assert_pixels_eq!(filtered, expected);
    }

    #[bench]
    fn bench_horizontal_filter(b: &mut test::Bencher) {
        let image = gray_bench_image(500, 500);
        let kernel = vec![1f32/5f32; 5];
        b.iter(|| {
            let filtered = horizontal_filter(&image, &kernel);
            test::black_box(filtered);
            });
    }

    #[test]
    fn test_vertical_filter() {
        let image: GrayImage = ImageBuffer::from_raw(3, 3, vec![
            1u8, 4u8, 1u8,
            4u8, 7u8, 4u8,
            1u8, 4u8, 1u8]).unwrap();

        let expected: GrayImage = ImageBuffer::from_raw(3, 3, vec![
            2u8, 5u8, 2u8,
            2u8, 5u8, 2u8,
            2u8, 5u8, 2u8]).unwrap();

        let kernel = vec![1f32/3f32, 1f32/3f32, 1f32/3f32];
        let filtered = vertical_filter(&image, &kernel);

        assert_pixels_eq!(filtered, expected);
    }

    #[bench]
    fn bench_vertical_filter(b: &mut test::Bencher) {
        let image = gray_bench_image(500, 500);
        let kernel = vec![1f32/5f32; 5];
        b.iter(|| {
            let filtered = vertical_filter(&image, &kernel);
            test::black_box(filtered);
            });
    }
}
<|endoftext|>


<commit_before>
use std::fmt;
use std::io::Read;  // Trait providing read_to_string()
use std::env;

use hyper;
use serde;
use serde_json;

// use Groups;

pub const API_VERSION: u16 = 3;

#[derive(Default, Clone, Copy, Debug)]
pub struct Pagination {
    pub page: u16,
    pub per_page: u16,
}

#[derive(Default)]
pub struct GitLab {
    scheme: String,
    domain: String,
    port: u16,
    private_token: String,
    pagination: Option<Pagination>,
    client: hyper::Client,
}

// Explicitly implement Debug trait for GitLab so we can hide the token.
impl fmt::Debug for GitLab {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "GitLab {{ scheme: {}, domain: {}, port: {}, private_token: XXXXXXXXXXXXXXXXXXXX, \
                pagination: {:?} }}",
               self.scheme,
               self.domain,
               self.port,
               self.pagination)
    }
}

impl GitLab {
    pub fn _new(scheme: &str, domain: &str, port: u16, private_token: &str) -> GitLab {
        GitLab {
            scheme: scheme.to_string(),
            domain: domain.to_string(),
            port: port,
            private_token: private_token.to_string(),
            pagination: None,
            client: match env::var("HTTP_PROXY") {
                Ok(proxy) => {
                    let proxy: Vec<&str> = proxy.trim_left_matches("http://").split(':').collect();
                    let hostname = proxy[0].to_string();
                    let port = proxy[1];

                    hyper::Client::with_http_proxy(hostname, port.parse().unwrap())
                }
                Err(_) => hyper::Client::new(),
            },
        }
    }

    pub fn new_insecure(domain: &str, private_token: &str) -> GitLab {
        warn!("Using insecure http:// protocol: Token will be sent in clear!");
        GitLab::_new("http", domain, 80, private_token)
    }

    pub fn new(domain: &str, private_token: &str) -> GitLab {
        GitLab::_new("https", domain, 443, private_token)
    }

    /// Build a URL used to access GitLab instance, including some parameters.
    ///
    /// # Examples
    ///
    /// Example from GitLab: https://docs.gitlab.com/ce/api/#basic-usage
    ///
    /// ```
    /// use gitlab_api::GitLab;
    ///
    /// let expected_url = "https://gitlab.example.com:\
    ///                     443/api/v3/groups?order_by=path&private_token=XXXXXXXXXXXXX";
    ///
    /// let gl = GitLab::new("gitlab.example.com", "XXXXXXXXXXXXX");
    ///
    /// assert_eq!(gl.build_url("groups?order_by=path"), expected_url);
    /// ```
    pub fn build_url(&self, query: &str) -> String {
        let params_splitter = if query.find('?').is_some() { "&" } else { "?" };
        let mut url = format!("{}://{}:{}/api/v{}/{}{}private_token={}",
                              self.scheme,
                              self.domain,
                              self.port,
                              API_VERSION,
                              query,
                              params_splitter,
                              self.private_token);

        self.pagination.as_ref().map(|pagination| {
            url.push_str(&format!("&page={}&per_page={}", pagination.page, pagination.per_page));
        });

        url
    }

    // pub fn attempt_connection(&self) -> Result<hyper::client::Response, hyper::Error> {
    //     let url = self.build_url("version");
    //     // Close connections after each GET.
    //     self.client.get(&url).header(hyper::header::Connection::close()).send()
    // }

    pub fn set_pagination(&mut self, pagination: Pagination) {
        self.pagination = Some(pagination);
    }

    pub fn get<T>(&self, query: &str) -> Result<T, serde_json::Error>
        where T: serde::Deserialize
    {
        // FIXME: Properly handle any errors. Use chain_error crate.

        let url = self.build_url(query);
        info!("url: {:?}", url);

        // Close connections after each GET.
        let mut res: hyper::client::Response = self.client
            .get(&url)
            .header(hyper::header::Connection::close())
            .send()
            .unwrap();
        info!("res.status: {:?}", res.status);
        debug!("res.headers: {:?}", res.headers);
        debug!("res.url: {:?}", res.url);

        let mut body = String::new();
        res.read_to_string(&mut body).unwrap();
        debug!("body:\n{:?}", body);

        assert_eq!(res.status, hyper::status::StatusCode::Ok);

        serde_json::from_str(body.as_str())
    }

    pub fn version(&self) -> ::Version {
        self.get("version").unwrap()
    }

    // pub fn groups(&self) -> Result<Groups, serde_json::Error> {
    //     self.get("groups")
    // }

    pub fn projects(&self) -> ::projects::ProjectsLister {
        ::projects::ProjectsLister::new(&self)
    }

    // pub fn groups(&mut self, listing: ::groups::Listing) -> Result<Groups, serde_json::Error> {
    //     let query = listing.build_query();
    //     // self.get(&query)
    //     unimplemented!();
    // }

    // pub fn owned_groups(&mut self) -> Result<Groups, serde_json::Error> {
    //     let query = ::groups::owned_groups::Listing::new().build_query();
    //     info!("query: {:?}", query);
    //     self.get(&query)
    // }
}

// #[cfg(test)]
// mod tests {
//
// #[test]
// fn it_works() {
// }
// }
//
<commit_msg>Clippy: `needless_borrow`<commit_after>
use std::fmt;
use std::io::Read;  // Trait providing read_to_string()
use std::env;

use hyper;
use serde;
use serde_json;

// use Groups;

pub const API_VERSION: u16 = 3;

#[derive(Default, Clone, Copy, Debug)]
pub struct Pagination {
    pub page: u16,
    pub per_page: u16,
}

#[derive(Default)]
pub struct GitLab {
    scheme: String,
    domain: String,
    port: u16,
    private_token: String,
    pagination: Option<Pagination>,
    client: hyper::Client,
}

// Explicitly implement Debug trait for GitLab so we can hide the token.
impl fmt::Debug for GitLab {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "GitLab {{ scheme: {}, domain: {}, port: {}, private_token: XXXXXXXXXXXXXXXXXXXX, \
                pagination: {:?} }}",
               self.scheme,
               self.domain,
               self.port,
               self.pagination)
    }
}

impl GitLab {
    pub fn _new(scheme: &str, domain: &str, port: u16, private_token: &str) -> GitLab {
        GitLab {
            scheme: scheme.to_string(),
            domain: domain.to_string(),
            port: port,
            private_token: private_token.to_string(),
            pagination: None,
            client: match env::var("HTTP_PROXY") {
                Ok(proxy) => {
                    let proxy: Vec<&str> = proxy.trim_left_matches("http://").split(':').collect();
                    let hostname = proxy[0].to_string();
                    let port = proxy[1];

                    hyper::Client::with_http_proxy(hostname, port.parse().unwrap())
                }
                Err(_) => hyper::Client::new(),
            },
        }
    }

    pub fn new_insecure(domain: &str, private_token: &str) -> GitLab {
        warn!("Using insecure http:// protocol: Token will be sent in clear!");
        GitLab::_new("http", domain, 80, private_token)
    }

    pub fn new(domain: &str, private_token: &str) -> GitLab {
        GitLab::_new("https", domain, 443, private_token)
    }

    /// Build a URL used to access GitLab instance, including some parameters.
    ///
    /// # Examples
    ///
    /// Example from GitLab: https://docs.gitlab.com/ce/api/#basic-usage
    ///
    /// ```
    /// use gitlab_api::GitLab;
    ///
    /// let expected_url = "https://gitlab.example.com:\
    ///                     443/api/v3/groups?order_by=path&private_token=XXXXXXXXXXXXX";
    ///
    /// let gl = GitLab::new("gitlab.example.com", "XXXXXXXXXXXXX");
    ///
    /// assert_eq!(gl.build_url("groups?order_by=path"), expected_url);
    /// ```
    pub fn build_url(&self, query: &str) -> String {
        let params_splitter = if query.find('?').is_some() { "&" } else { "?" };
        let mut url = format!("{}://{}:{}/api/v{}/{}{}private_token={}",
                              self.scheme,
                              self.domain,
                              self.port,
                              API_VERSION,
                              query,
                              params_splitter,
                              self.private_token);

        self.pagination.as_ref().map(|pagination| {
            url.push_str(&format!("&page={}&per_page={}", pagination.page, pagination.per_page));
        });

        url
    }

    // pub fn attempt_connection(&self) -> Result<hyper::client::Response, hyper::Error> {
    //     let url = self.build_url("version");
    //     // Close connections after each GET.
    //     self.client.get(&url).header(hyper::header::Connection::close()).send()
    // }

    pub fn set_pagination(&mut self, pagination: Pagination) {
        self.pagination = Some(pagination);
    }

    pub fn get<T>(&self, query: &str) -> Result<T, serde_json::Error>
        where T: serde::Deserialize
    {
        // FIXME: Properly handle any errors. Use chain_error crate.

        let url = self.build_url(query);
        info!("url: {:?}", url);

        // Close connections after each GET.
        let mut res: hyper::client::Response = self.client
            .get(&url)
            .header(hyper::header::Connection::close())
            .send()
            .unwrap();
        info!("res.status: {:?}", res.status);
        debug!("res.headers: {:?}", res.headers);
        debug!("res.url: {:?}", res.url);

        let mut body = String::new();
        res.read_to_string(&mut body).unwrap();
        debug!("body:\n{:?}", body);

        assert_eq!(res.status, hyper::status::StatusCode::Ok);

        serde_json::from_str(body.as_str())
    }

    pub fn version(&self) -> ::Version {
        self.get("version").unwrap()
    }

    // pub fn groups(&self) -> Result<Groups, serde_json::Error> {
    //     self.get("groups")
    // }

    pub fn projects(&self) -> ::projects::ProjectsLister {
        ::projects::ProjectsLister::new(self)
    }

    // pub fn groups(&mut self, listing: ::groups::Listing) -> Result<Groups, serde_json::Error> {
    //     let query = listing.build_query();
    //     // self.get(&query)
    //     unimplemented!();
    // }

    // pub fn owned_groups(&mut self) -> Result<Groups, serde_json::Error> {
    //     let query = ::groups::owned_groups::Listing::new().build_query();
    //     info!("query: {:?}", query);
    //     self.get(&query)
    // }
}

// #[cfg(test)]
// mod tests {
//
// #[test]
// fn it_works() {
// }
// }
//
<|endoftext|>


<commit_before>
use std::fmt;
use std::io::Read;  // Trait providing read_to_string()
use std::env;

use url;
use hyper;
use serde;
use serde_json;

// use Groups;

use ::errors::*;

pub const API_VERSION: u16 = 3;

#[derive(Default, Clone, Copy, Debug)]
pub struct Pagination {
    pub page: u16,
    pub per_page: u16,
}

pub struct GitLab {
    url: url::Url,
    private_token: String,
    pagination: Option<Pagination>,
    client: hyper::Client,
}

// Explicitly implement Debug trait for GitLab so we can hide the token.
impl fmt::Debug for GitLab {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "GitLab {{ scheme: {}, domain: {}, port: {}, private_token: XXXXXXXXXXXXXXXXXXXX, \
                pagination: {:?} }}",
               self.url.scheme(),
               self.url.domain().unwrap_or("bad hostname provided"),
               self.url
                   .port()
                   .map(|port_u16| port_u16.to_string())
                   .unwrap_or("no port provided".to_string()),
               self.pagination)
    }
}

fn validate_url(scheme: &str, domain: &str, port: u16) -> Result<url::Url> {

    match domain.find('.') {
        None => {
            // pass
        }
        Some(index) => {
            if index == 0 {
                bail!(format!("invalid domain: '{}' cannot start with a dot", domain));
            }
        }
    };

    if domain.ends_with('.') {
        bail!(format!("invalid domain: '{}' cannot end with a dot", domain));
    }

    let url_string = format!("{}://{}/api/v{}/", scheme, domain, API_VERSION);
    let mut url = url::Url::parse(&url_string)
        .chain_err(|| format!("failure to parse URL '{}'", url_string))?;
    url.set_port(Some(port)).expect("bad port provided");

    {
        let url_host = url.host_str();
        if url_host.is_none() {
            bail!("failure to get URL's hostname");
        }
        if url_host.unwrap() != domain {
            bail!(format!("invalid hostname '{}'", domain));
        }
    }

    Ok(url)
}

impl GitLab {
    pub fn _new(scheme: &str, domain: &str, port: u16, private_token: &str) -> Result<GitLab> {
        if private_token.len() != 20 {
            bail!(format!("private token should be a 20 characters string (not {})",
                          private_token.len()));
        }

        let url: url::Url = validate_url(scheme, domain, port).chain_err(|| "invalid URL")?;

        Ok(GitLab {
            url: url,
            private_token: private_token.to_string(),
            pagination: None,
            client: match env::var("HTTP_PROXY") {
                Ok(proxy) => {
                    let proxy: Vec<&str> = proxy.trim_left_matches("http://").split(':').collect();
                    let hostname = proxy[0].to_string();
                    let port = proxy[1].parse()
                        .chain_err(|| format!("failure to set port to {}", port))?;

                    hyper::Client::with_http_proxy(hostname, port)
                }
                Err(_) => hyper::Client::new(),
            },
        })
    }

    pub fn new_insecure(domain: &str, private_token: &str) -> Result<GitLab> {
        warn!("Using insecure http:// protocol: Token will be sent in clear!");
        GitLab::_new("http", domain, 80, private_token)
    }

    pub fn new(domain: &str, private_token: &str) -> Result<GitLab> {
        GitLab::_new("https", domain, 443, private_token)
    }

    pub fn port(mut self, port: u16) -> Self {
        self.url.set_port(Some(port)).unwrap();
        self
    }

    pub fn scheme(mut self, scheme: &str) -> Self {
        self.url.set_scheme(scheme).unwrap();
        self
    }

    /// Build a URL used to access GitLab instance, including some parameters.
    ///
    /// # Examples
    ///
    /// Example from GitLab: https://docs.gitlab.com/ce/api/#basic-usage
    ///
    /// ```
    /// use gitlab_api::GitLab;
    ///
    /// let expected_url = "https://gitlab.example.com\
    ///                     /api/v3/groups?order_by=path&private_token=XXXXXXXXXXXXXXXXXXXX";
    ///
    /// let gl = GitLab::new("gitlab.example.com", "XXXXXXXXXXXXXXXXXXXX").unwrap();
    ///
    /// assert_eq!(gl.build_url("groups?order_by=path").unwrap(), expected_url);
    /// ```
    pub fn build_url(&self, query: &str) -> Result<String> {
        let mut new_url = self.url
            .clone()
            .join(query)
            .chain_err(|| {
                format!("Failure to join query '{}' to url {}",
                        query,
                        self.url.as_str())
            })?;
        new_url.query_pairs_mut().append_pair("private_token", &self.private_token);
        self.pagination.as_ref().map(|pagination| {
            new_url.query_pairs_mut().append_pair("page", &pagination.page.to_string());
            new_url.query_pairs_mut().append_pair("per_page", &pagination.per_page.to_string());
        });

        Ok(new_url.into_string())
    }

    // pub fn attempt_connection(&self) -> Result<hyper::client::Response, hyper::Error> {
    //     let url = self.build_url("version");
    //     // Close connections after each GET.
    //     self.client.get(&url).header(hyper::header::Connection::close()).send()
    // }

    /// Set pagination information
    ///
    /// # Examples
    ///
    /// ```
    /// use gitlab_api::{GitLab, Pagination};
    ///
    /// let expected_url = "https://gitlab.example.com\
    ///                     /api/v3/groups?order_by=path&\
    ///                     private_token=XXXXXXXXXXXXXXXXXXXX&page=2&per_page=5";
    ///
    /// let mut gl = GitLab::new("gitlab.example.com", "XXXXXXXXXXXXXXXXXXXX").unwrap();
    /// gl.set_pagination(Pagination {page: 2, per_page: 5});
    /// assert_eq!(gl.build_url("groups?order_by=path").unwrap(), expected_url);
    /// ```
    pub fn set_pagination(&mut self, pagination: Pagination) {
        self.pagination = Some(pagination);
    }

    pub fn get<T>(&self, query: &str) -> Result<T>
        where T: serde::Deserialize
    {
        let url = self.build_url(query)
            .chain_err(|| format!("failure to build url for query '{}'", query))?;
        info!("url: {:?}", url);

        // Close connections after each GET.
        let mut res: hyper::client::Response = self.client
            .get(&url)
            .header(hyper::header::Connection::close())
            .send()
            .chain_err(|| format!("cannot send request '{}' to {:?}", query, self))?;
        info!("res.status: {:?}", res.status);
        debug!("res.headers: {:?}", res.headers);
        debug!("res.url: {:?}", res.url);

        let mut body = String::new();
        res.read_to_string(&mut body).chain_err(|| "cannot read response body")?;
        debug!("body:\n{:?}", body);

        // assert_eq!(res.status, hyper::status::StatusCode::Ok);
        if res.status != hyper::status::StatusCode::Ok {
            bail!(format!("status code '{}', not '200 OK'", res.status));
        }

        serde_json::from_str(body.as_str())
            .chain_err(|| format!("cannot build Rust struct from JSON data: {}", body))
    }

    pub fn version(&self) -> Result<::Version> {
        self.get("version").chain_err(|| "cannot query 'version'")
    }

    pub fn groups(&self) -> ::groups::GroupsLister {
        ::groups::GroupsLister::new(self)
    }

    pub fn projects(&self) -> ::projects::ProjectsLister {
        ::projects::ProjectsLister::new(self)
    }

    pub fn issues(&self) -> ::issues::IssuesLister {
        ::issues::IssuesLister::new(self)
    }

    pub fn merge_requests(&self, project_id: i64) -> ::merge_requests::MergeRequestsLister {
        ::merge_requests::MergeRequestsLister::new(self, project_id)
    }

    // pub fn groups(&mut self, listing: ::groups::Listing) -> Result<Groups, serde_json::Error> {
    //     let query = listing.build_query();
    //     // self.get(&query)
    //     unimplemented!();
    // }

    // pub fn owned_groups(&mut self) -> Result<Groups, serde_json::Error> {
    //     let query = ::groups::owned_groups::Listing::new().build_query();
    //     info!("query: {:?}", query);
    //     self.get(&query)
    // }
}

#[cfg(test)]
mod tests {
    use std::fmt;
    use gitlab::*;
    use errors::*;

    fn verify_ok<T>(result: &Result<T>) {
        if let &Err(ref e) = result {
            println!("error: {}", e);

            for e in e.iter().skip(1) {
                println!("caused by: {}", e);
            }

            // The backtrace is not always generated. Try to run this example
            // with `RUST_BACKTRACE=1`.
            if let Some(backtrace) = e.backtrace() {
                println!("backtrace: {:?}", backtrace);
            }
        }
        assert!(result.is_ok());
    }

    fn verify_err<T>(result: &Result<T>)
        where T: fmt::Debug
    {
        match result {
            &Err(_) => {
                // pass
            }
            &Ok(ref t) => {
                panic!(format!("Expected an Err(), got an Ok(t), with t: {:?}", t));
            }
        }
    }

    #[test]
    fn impl_debug_for_gitlab() {
        let gl = GitLab::new("gitlab.com", "XXXXXXXXXXXXXXXXXXXX").unwrap();

        let debug = format!("{:?}", gl);
        assert_eq!("GitLab { scheme: https, domain: gitlab.com, port: no port provided, private_token: XXXXXXXXXXXXXXXXXXXX, pagination: None }", debug);

        let gl = gl.scheme("http").port(80);
        let debug = format!("{:?}", gl);
        assert_eq!("GitLab { scheme: http, domain: gitlab.com, port: no port provided, private_token: XXXXXXXXXXXXXXXXXXXX, pagination: None }", debug);

        let mut gl = gl.port(81);
        let debug = format!("{:?}", gl);
        assert_eq!("GitLab { scheme: http, domain: gitlab.com, port: 81, private_token: XXXXXXXXXXXXXXXXXXXX, pagination: None }", debug);

        gl.set_pagination(Pagination {page: 2, per_page: 5});
        let debug = format!("{:?}", gl);
        assert_eq!("GitLab { scheme: http, domain: gitlab.com, port: 81, private_token: XXXXXXXXXXXXXXXXXXXX, pagination: Some(Pagination { page: 2, per_page: 5 }) }", debug);
    }

    #[test]
    fn new_valid() {
        let gl = GitLab::new("gitlab.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_ok(&gl);

        let gl = GitLab::new_insecure("gitlab.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_ok(&gl);

        let gl = GitLab::new("localhost", "XXXXXXXXXXXXXXXXXXXX");
        verify_ok(&gl);

        let gl = GitLab::new_insecure("localhost", "XXXXXXXXXXXXXXXXXXXX");
        verify_ok(&gl);
    }

    #[test]
    fn new_invalid_url_1() {
        let gl = GitLab::new("", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);

        let gl = GitLab::new_insecure("", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);
    }

    #[test]
    fn new_invalid_url_2() {
        let gl = GitLab::new("gitla/b.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);

        let gl = GitLab::new_insecure("gitla/b.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);
    }

    #[test]
    fn new_invalid_url_3() {
        let gl = GitLab::new("/gitlab.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);

        let gl = GitLab::new_insecure("/gitlab.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);
    }

    #[test]
    fn new_invalid_url_4() {
        let gl = GitLab::new("http:/gitlab.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);

        let gl = GitLab::new_insecure("http:/gitlab.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);
    }

    #[test]
    fn new_invalid_url_5() {
        let gl = GitLab::new("http:///gitlab.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);

        let gl = GitLab::new_insecure("http:///gitlab.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);
    }

    #[test]
    fn new_invalid_url_6() {
        let gl = GitLab::new(".gitlab", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);

        let gl = GitLab::new_insecure(".gitlab", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);
    }

    #[test]
    fn new_invalid_url_7() {
        let gl = GitLab::new(".gitlab.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);

        let gl = GitLab::new_insecure(".gitlab.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);
    }

    #[test]
    fn new_invalid_url_8() {
        let gl = GitLab::new("gitlab.", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);

        let gl = GitLab::new_insecure("gitlab.", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);
    }

    #[test]
    fn new_invalid_url_10() {
        let gl = GitLab::new("gitlab.com.", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);

        let gl = GitLab::new_insecure("gitlab.com.", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);
    }

    #[test]
    fn new_invalid_token() {
        let gl = GitLab::new("gitlab.com", "");
        assert!(gl.is_err());

        let gl = GitLab::new("gitlab.com", "X");
        assert!(gl.is_err());

        let gl = GitLab::new("gitlab.com", "XXXXXXXXXXXXXXXXXXX");
        assert!(gl.is_err());

        let gl = GitLab::new("gitlab.com", "XXXXXXXXXXXXXXXXXXXXX");
        assert!(gl.is_err());
    }
}
<commit_msg>Cleanup commented-out code<commit_after>
use std::fmt;
use std::io::Read;  // Trait providing read_to_string()
use std::env;

use url;
use hyper;
use serde;
use serde_json;

// use Groups;

use ::errors::*;

pub const API_VERSION: u16 = 3;

#[derive(Default, Clone, Copy, Debug)]
pub struct Pagination {
    pub page: u16,
    pub per_page: u16,
}

pub struct GitLab {
    url: url::Url,
    private_token: String,
    pagination: Option<Pagination>,
    client: hyper::Client,
}

// Explicitly implement Debug trait for GitLab so we can hide the token.
impl fmt::Debug for GitLab {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "GitLab {{ scheme: {}, domain: {}, port: {}, private_token: XXXXXXXXXXXXXXXXXXXX, \
                pagination: {:?} }}",
               self.url.scheme(),
               self.url.domain().unwrap_or("bad hostname provided"),
               self.url
                   .port()
                   .map(|port_u16| port_u16.to_string())
                   .unwrap_or("no port provided".to_string()),
               self.pagination)
    }
}

fn validate_url(scheme: &str, domain: &str, port: u16) -> Result<url::Url> {

    match domain.find('.') {
        None => {
            // pass
        }
        Some(index) => {
            if index == 0 {
                bail!(format!("invalid domain: '{}' cannot start with a dot", domain));
            }
        }
    };

    if domain.ends_with('.') {
        bail!(format!("invalid domain: '{}' cannot end with a dot", domain));
    }

    let url_string = format!("{}://{}/api/v{}/", scheme, domain, API_VERSION);
    let mut url = url::Url::parse(&url_string)
        .chain_err(|| format!("failure to parse URL '{}'", url_string))?;
    url.set_port(Some(port)).expect("bad port provided");

    {
        let url_host = url.host_str();
        if url_host.is_none() {
            bail!("failure to get URL's hostname");
        }
        if url_host.unwrap() != domain {
            bail!(format!("invalid hostname '{}'", domain));
        }
    }

    Ok(url)
}

impl GitLab {
    pub fn _new(scheme: &str, domain: &str, port: u16, private_token: &str) -> Result<GitLab> {
        if private_token.len() != 20 {
            bail!(format!("private token should be a 20 characters string (not {})",
                          private_token.len()));
        }

        let url: url::Url = validate_url(scheme, domain, port).chain_err(|| "invalid URL")?;

        Ok(GitLab {
            url: url,
            private_token: private_token.to_string(),
            pagination: None,
            client: match env::var("HTTP_PROXY") {
                Ok(proxy) => {
                    let proxy: Vec<&str> = proxy.trim_left_matches("http://").split(':').collect();
                    let hostname = proxy[0].to_string();
                    let port = proxy[1].parse()
                        .chain_err(|| format!("failure to set port to {}", port))?;

                    hyper::Client::with_http_proxy(hostname, port)
                }
                Err(_) => hyper::Client::new(),
            },
        })
    }

    pub fn new_insecure(domain: &str, private_token: &str) -> Result<GitLab> {
        warn!("Using insecure http:// protocol: Token will be sent in clear!");
        GitLab::_new("http", domain, 80, private_token)
    }

    pub fn new(domain: &str, private_token: &str) -> Result<GitLab> {
        GitLab::_new("https", domain, 443, private_token)
    }

    pub fn port(mut self, port: u16) -> Self {
        self.url.set_port(Some(port)).unwrap();
        self
    }

    pub fn scheme(mut self, scheme: &str) -> Self {
        self.url.set_scheme(scheme).unwrap();
        self
    }

    /// Build a URL used to access GitLab instance, including some parameters.
    ///
    /// # Examples
    ///
    /// Example from GitLab: https://docs.gitlab.com/ce/api/#basic-usage
    ///
    /// ```
    /// use gitlab_api::GitLab;
    ///
    /// let expected_url = "https://gitlab.example.com\
    ///                     /api/v3/groups?order_by=path&private_token=XXXXXXXXXXXXXXXXXXXX";
    ///
    /// let gl = GitLab::new("gitlab.example.com", "XXXXXXXXXXXXXXXXXXXX").unwrap();
    ///
    /// assert_eq!(gl.build_url("groups?order_by=path").unwrap(), expected_url);
    /// ```
    pub fn build_url(&self, query: &str) -> Result<String> {
        let mut new_url = self.url
            .clone()
            .join(query)
            .chain_err(|| {
                format!("Failure to join query '{}' to url {}",
                        query,
                        self.url.as_str())
            })?;
        new_url.query_pairs_mut().append_pair("private_token", &self.private_token);
        self.pagination.as_ref().map(|pagination| {
            new_url.query_pairs_mut().append_pair("page", &pagination.page.to_string());
            new_url.query_pairs_mut().append_pair("per_page", &pagination.per_page.to_string());
        });

        Ok(new_url.into_string())
    }

    // pub fn attempt_connection(&self) -> Result<hyper::client::Response, hyper::Error> {
    //     let url = self.build_url("version");
    //     // Close connections after each GET.
    //     self.client.get(&url).header(hyper::header::Connection::close()).send()
    // }

    /// Set pagination information
    ///
    /// # Examples
    ///
    /// ```
    /// use gitlab_api::{GitLab, Pagination};
    ///
    /// let expected_url = "https://gitlab.example.com\
    ///                     /api/v3/groups?order_by=path&\
    ///                     private_token=XXXXXXXXXXXXXXXXXXXX&page=2&per_page=5";
    ///
    /// let mut gl = GitLab::new("gitlab.example.com", "XXXXXXXXXXXXXXXXXXXX").unwrap();
    /// gl.set_pagination(Pagination {page: 2, per_page: 5});
    /// assert_eq!(gl.build_url("groups?order_by=path").unwrap(), expected_url);
    /// ```
    pub fn set_pagination(&mut self, pagination: Pagination) {
        self.pagination = Some(pagination);
    }

    pub fn get<T>(&self, query: &str) -> Result<T>
        where T: serde::Deserialize
    {
        let url = self.build_url(query)
            .chain_err(|| format!("failure to build url for query '{}'", query))?;
        info!("url: {:?}", url);

        // Close connections after each GET.
        let mut res: hyper::client::Response = self.client
            .get(&url)
            .header(hyper::header::Connection::close())
            .send()
            .chain_err(|| format!("cannot send request '{}' to {:?}", query, self))?;
        info!("res.status: {:?}", res.status);
        debug!("res.headers: {:?}", res.headers);
        debug!("res.url: {:?}", res.url);

        let mut body = String::new();
        res.read_to_string(&mut body).chain_err(|| "cannot read response body")?;
        debug!("body:\n{:?}", body);

        if res.status != hyper::status::StatusCode::Ok {
            bail!(format!("status code '{}', not '200 OK'", res.status));
        }

        serde_json::from_str(body.as_str())
            .chain_err(|| format!("cannot build Rust struct from JSON data: {}", body))
    }

    pub fn version(&self) -> Result<::Version> {
        self.get("version").chain_err(|| "cannot query 'version'")
    }

    pub fn groups(&self) -> ::groups::GroupsLister {
        ::groups::GroupsLister::new(self)
    }

    pub fn projects(&self) -> ::projects::ProjectsLister {
        ::projects::ProjectsLister::new(self)
    }

    pub fn issues(&self) -> ::issues::IssuesLister {
        ::issues::IssuesLister::new(self)
    }

    pub fn merge_requests(&self, project_id: i64) -> ::merge_requests::MergeRequestsLister {
        ::merge_requests::MergeRequestsLister::new(self, project_id)
    }

    // pub fn groups(&mut self, listing: ::groups::Listing) -> Result<Groups, serde_json::Error> {
    //     let query = listing.build_query();
    //     // self.get(&query)
    //     unimplemented!();
    // }

    // pub fn owned_groups(&mut self) -> Result<Groups, serde_json::Error> {
    //     let query = ::groups::owned_groups::Listing::new().build_query();
    //     info!("query: {:?}", query);
    //     self.get(&query)
    // }
}

#[cfg(test)]
mod tests {
    use std::fmt;
    use gitlab::*;
    use errors::*;

    fn verify_ok<T>(result: &Result<T>) {
        if let &Err(ref e) = result {
            println!("error: {}", e);

            for e in e.iter().skip(1) {
                println!("caused by: {}", e);
            }

            // The backtrace is not always generated. Try to run this example
            // with `RUST_BACKTRACE=1`.
            if let Some(backtrace) = e.backtrace() {
                println!("backtrace: {:?}", backtrace);
            }
        }
        assert!(result.is_ok());
    }

    fn verify_err<T>(result: &Result<T>)
        where T: fmt::Debug
    {
        match result {
            &Err(_) => {
                // pass
            }
            &Ok(ref t) => {
                panic!(format!("Expected an Err(), got an Ok(t), with t: {:?}", t));
            }
        }
    }

    #[test]
    fn impl_debug_for_gitlab() {
        let gl = GitLab::new("gitlab.com", "XXXXXXXXXXXXXXXXXXXX").unwrap();

        let debug = format!("{:?}", gl);
        assert_eq!("GitLab { scheme: https, domain: gitlab.com, port: no port provided, private_token: XXXXXXXXXXXXXXXXXXXX, pagination: None }", debug);

        let gl = gl.scheme("http").port(80);
        let debug = format!("{:?}", gl);
        assert_eq!("GitLab { scheme: http, domain: gitlab.com, port: no port provided, private_token: XXXXXXXXXXXXXXXXXXXX, pagination: None }", debug);

        let mut gl = gl.port(81);
        let debug = format!("{:?}", gl);
        assert_eq!("GitLab { scheme: http, domain: gitlab.com, port: 81, private_token: XXXXXXXXXXXXXXXXXXXX, pagination: None }", debug);

        gl.set_pagination(Pagination {page: 2, per_page: 5});
        let debug = format!("{:?}", gl);
        assert_eq!("GitLab { scheme: http, domain: gitlab.com, port: 81, private_token: XXXXXXXXXXXXXXXXXXXX, pagination: Some(Pagination { page: 2, per_page: 5 }) }", debug);
    }

    #[test]
    fn new_valid() {
        let gl = GitLab::new("gitlab.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_ok(&gl);

        let gl = GitLab::new_insecure("gitlab.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_ok(&gl);

        let gl = GitLab::new("localhost", "XXXXXXXXXXXXXXXXXXXX");
        verify_ok(&gl);

        let gl = GitLab::new_insecure("localhost", "XXXXXXXXXXXXXXXXXXXX");
        verify_ok(&gl);
    }

    #[test]
    fn new_invalid_url_1() {
        let gl = GitLab::new("", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);

        let gl = GitLab::new_insecure("", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);
    }

    #[test]
    fn new_invalid_url_2() {
        let gl = GitLab::new("gitla/b.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);

        let gl = GitLab::new_insecure("gitla/b.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);
    }

    #[test]
    fn new_invalid_url_3() {
        let gl = GitLab::new("/gitlab.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);

        let gl = GitLab::new_insecure("/gitlab.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);
    }

    #[test]
    fn new_invalid_url_4() {
        let gl = GitLab::new("http:/gitlab.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);

        let gl = GitLab::new_insecure("http:/gitlab.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);
    }

    #[test]
    fn new_invalid_url_5() {
        let gl = GitLab::new("http:///gitlab.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);

        let gl = GitLab::new_insecure("http:///gitlab.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);
    }

    #[test]
    fn new_invalid_url_6() {
        let gl = GitLab::new(".gitlab", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);

        let gl = GitLab::new_insecure(".gitlab", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);
    }

    #[test]
    fn new_invalid_url_7() {
        let gl = GitLab::new(".gitlab.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);

        let gl = GitLab::new_insecure(".gitlab.com", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);
    }

    #[test]
    fn new_invalid_url_8() {
        let gl = GitLab::new("gitlab.", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);

        let gl = GitLab::new_insecure("gitlab.", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);
    }

    #[test]
    fn new_invalid_url_10() {
        let gl = GitLab::new("gitlab.com.", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);

        let gl = GitLab::new_insecure("gitlab.com.", "XXXXXXXXXXXXXXXXXXXX");
        verify_err(&gl);
    }

    #[test]
    fn new_invalid_token() {
        let gl = GitLab::new("gitlab.com", "");
        assert!(gl.is_err());

        let gl = GitLab::new("gitlab.com", "X");
        assert!(gl.is_err());

        let gl = GitLab::new("gitlab.com", "XXXXXXXXXXXXXXXXXXX");
        assert!(gl.is_err());

        let gl = GitLab::new("gitlab.com", "XXXXXXXXXXXXXXXXXXXXX");
        assert!(gl.is_err());
    }
}
<|endoftext|>


<commit_before>#![feature(test)]
#![cfg_attr(feature = "i128_support", feature(i128_type, i128))]

extern crate test;
extern crate rand;

const RAND_BENCH_N: u64 = 1000;

use std::mem::size_of;
use test::{black_box, Bencher};

use rand::{Rng, NewRng, XorShiftRng};
use rand::distributions::*;

macro_rules! distr_int {
    ($fnn:ident, $ty:ty, $distr:expr) => {
        #[bench]
        fn $fnn(b: &mut Bencher) {
            let mut rng = XorShiftRng::new();
            let distr = $distr;

            b.iter(|| {
                let mut accum = 0 as $ty;
                for _ in 0..::RAND_BENCH_N {
                    let x: $ty = distr.sample(&mut rng);
                    accum = accum.wrapping_add(x);
                }
                black_box(accum);
            });
            b.bytes = size_of::<$ty>() as u64 * ::RAND_BENCH_N;
        }
    }
}

macro_rules! distr_float {
    ($fnn:ident, $ty:ty, $distr:expr) => {
        #[bench]
        fn $fnn(b: &mut Bencher) {
            let mut rng = XorShiftRng::new();
            let distr = $distr;

            b.iter(|| {
                let mut accum = 0.0;
                for _ in 0..::RAND_BENCH_N {
                    let x: $ty = distr.sample(&mut rng);
                    accum = accum + x;
                }
                black_box(accum);
            });
            b.bytes = size_of::<$ty>() as u64 * ::RAND_BENCH_N;
        }
    }
}

macro_rules! distr {
    ($fnn:ident, $ty:ty, $distr:expr) => {
        #[bench]
        fn $fnn(b: &mut Bencher) {
            let mut rng = XorShiftRng::new();
            let distr = $distr;

            b.iter(|| {
                for _ in 0..::RAND_BENCH_N {
                    let x: $ty = distr.sample(&mut rng);
                    black_box(x);
                }
            });
            b.bytes = size_of::<$ty>() as u64 * ::RAND_BENCH_N;
        }
    }
}

// range
distr_int!(distr_range_i8, i8, Range::new(20i8, 100));
distr_int!(distr_range_i16, i16, Range::new(-500i16, 2000));
distr_int!(distr_range_i32, i32, Range::new(-200_000_000i32, 800_000_000));
distr_int!(distr_range_i64, i64, Range::new(3i64, 12345678901234));
#[cfg(feature = "i128_support")]
distr_int!(distr_range_i128, i128, Range::new(-12345678901234i128, 12345678901234567890));

distr_float!(distr_range_f32, f32, Range::new(2.26f32, 2.319));
distr_float!(distr_range_f64, f64, Range::new(2.26f64, 2.319));

// uniform
distr_int!(distr_uniform_i8, i8, Uniform);
distr_int!(distr_uniform_i16, i16, Uniform);
distr_int!(distr_uniform_i32, i32, Uniform);
distr_int!(distr_uniform_i64, i64, Uniform);
#[cfg(feature = "i128_support")]
distr_int!(distr_uniform_i128, i128, Uniform);

distr!(distr_uniform_bool, bool, Uniform);
distr!(distr_uniform_alphanumeric, char, Alphanumeric);
distr!(distr_uniform_codepoint, char, Uniform);

distr_float!(distr_uniform_f32, f32, Uniform);
distr_float!(distr_uniform_f64, f64, Uniform);

// distributions
distr_float!(distr_exp, f64, Exp::new(2.71828 * 3.14159));
distr_float!(distr_normal, f64, Normal::new(-2.71828, 3.14159));
distr_float!(distr_log_normal, f64, LogNormal::new(-2.71828, 3.14159));
distr_float!(distr_gamma_large_shape, f64, Gamma::new(10., 1.0));
distr_float!(distr_gamma_small_shape, f64, Gamma::new(0.1, 1.0));
distr_int!(distr_binomial, u64, Binomial::new(20, 0.7));
distr_int!(distr_poisson, u64, Poisson::new(4.0));

// construct and sample from a range
macro_rules! gen_range_int {
    ($fnn:ident, $ty:ty, $low:expr, $high:expr, $incr:expr) => {
        #[bench]
        fn $fnn(b: &mut Bencher) {
            let mut rng = XorShiftRng::new();

            b.iter(|| {
                let mut high = $high;
                let mut accum: $ty = 0;
                for _ in 0..::RAND_BENCH_N {
                    accum = accum.wrapping_add(rng.gen_range($low, high));
                    high += $incr;  // force recalculation of range each time
                }
                black_box(accum);
            });
            b.bytes = size_of::<$ty>() as u64 * ::RAND_BENCH_N;
        }
    }
}

gen_range_int!(gen_range_i8, i8, 20i8, 100, 0);
gen_range_int!(gen_range_i16, i16, -500i16, 2000, 1);
gen_range_int!(gen_range_i32, i32, -200_000_000i32, 800_000_000, 3);
gen_range_int!(gen_range_i64, i64, 3i64, 12345678901234, 3);
#[cfg(feature = "i128_support")]
gen_range_int!(gen_range_i128, i128, -12345678901234i128, 12345678901234567890, 3);
<commit_msg>Use mask in gen_range_i benchmarks<commit_after>#![feature(test)]
#![cfg_attr(feature = "i128_support", feature(i128_type, i128))]

extern crate test;
extern crate rand;

const RAND_BENCH_N: u64 = 1000;

use std::mem::size_of;
use test::{black_box, Bencher};

use rand::{Rng, NewRng, XorShiftRng};
use rand::distributions::*;

macro_rules! distr_int {
    ($fnn:ident, $ty:ty, $distr:expr) => {
        #[bench]
        fn $fnn(b: &mut Bencher) {
            let mut rng = XorShiftRng::new();
            let distr = $distr;

            b.iter(|| {
                let mut accum = 0 as $ty;
                for _ in 0..::RAND_BENCH_N {
                    let x: $ty = distr.sample(&mut rng);
                    accum = accum.wrapping_add(x);
                }
                black_box(accum);
            });
            b.bytes = size_of::<$ty>() as u64 * ::RAND_BENCH_N;
        }
    }
}

macro_rules! distr_float {
    ($fnn:ident, $ty:ty, $distr:expr) => {
        #[bench]
        fn $fnn(b: &mut Bencher) {
            let mut rng = XorShiftRng::new();
            let distr = $distr;

            b.iter(|| {
                let mut accum = 0.0;
                for _ in 0..::RAND_BENCH_N {
                    let x: $ty = distr.sample(&mut rng);
                    accum = accum + x;
                }
                black_box(accum);
            });
            b.bytes = size_of::<$ty>() as u64 * ::RAND_BENCH_N;
        }
    }
}

macro_rules! distr {
    ($fnn:ident, $ty:ty, $distr:expr) => {
        #[bench]
        fn $fnn(b: &mut Bencher) {
            let mut rng = XorShiftRng::new();
            let distr = $distr;

            b.iter(|| {
                for _ in 0..::RAND_BENCH_N {
                    let x: $ty = distr.sample(&mut rng);
                    black_box(x);
                }
            });
            b.bytes = size_of::<$ty>() as u64 * ::RAND_BENCH_N;
        }
    }
}

// range
distr_int!(distr_range_i8, i8, Range::new(20i8, 100));
distr_int!(distr_range_i16, i16, Range::new(-500i16, 2000));
distr_int!(distr_range_i32, i32, Range::new(-200_000_000i32, 800_000_000));
distr_int!(distr_range_i64, i64, Range::new(3i64, 12345678901234));
#[cfg(feature = "i128_support")]
distr_int!(distr_range_i128, i128, Range::new(-12345678901234i128, 12345678901234567890));

distr_float!(distr_range_f32, f32, Range::new(2.26f32, 2.319));
distr_float!(distr_range_f64, f64, Range::new(2.26f64, 2.319));

// uniform
distr_int!(distr_uniform_i8, i8, Uniform);
distr_int!(distr_uniform_i16, i16, Uniform);
distr_int!(distr_uniform_i32, i32, Uniform);
distr_int!(distr_uniform_i64, i64, Uniform);
#[cfg(feature = "i128_support")]
distr_int!(distr_uniform_i128, i128, Uniform);

distr!(distr_uniform_bool, bool, Uniform);
distr!(distr_uniform_alphanumeric, char, Alphanumeric);
distr!(distr_uniform_codepoint, char, Uniform);

distr_float!(distr_uniform_f32, f32, Uniform);
distr_float!(distr_uniform_f64, f64, Uniform);

// distributions
distr_float!(distr_exp, f64, Exp::new(2.71828 * 3.14159));
distr_float!(distr_normal, f64, Normal::new(-2.71828, 3.14159));
distr_float!(distr_log_normal, f64, LogNormal::new(-2.71828, 3.14159));
distr_float!(distr_gamma_large_shape, f64, Gamma::new(10., 1.0));
distr_float!(distr_gamma_small_shape, f64, Gamma::new(0.1, 1.0));
distr_int!(distr_binomial, u64, Binomial::new(20, 0.7));
distr_int!(distr_poisson, u64, Poisson::new(4.0));

// construct and sample from a range
macro_rules! gen_range_int {
    ($fnn:ident, $ty:ident, $low:expr, $high:expr) => {
        #[bench]
        fn $fnn(b: &mut Bencher) {
            let mut rng = XorShiftRng::new();

            b.iter(|| {
                let mut high = $high;
                let mut accum: $ty = 0;
                for _ in 0..::RAND_BENCH_N {
                    accum = accum.wrapping_add(rng.gen_range($low, high));
                    // force recalculation of range each time
                    high = high.wrapping_add(1) & std::$ty::MAX;
                }
                black_box(accum);
            });
            b.bytes = size_of::<$ty>() as u64 * ::RAND_BENCH_N;
        }
    }
}

gen_range_int!(gen_range_i8, i8, -20i8, 100);
gen_range_int!(gen_range_i16, i16, -500i16, 2000);
gen_range_int!(gen_range_i32, i32, -200_000_000i32, 800_000_000);
gen_range_int!(gen_range_i64, i64, 3i64, 12345678901234);
#[cfg(feature = "i128_support")]
gen_range_int!(gen_range_i128, i128, -12345678901234i128, 12345678901234567890);
<|endoftext|>