<commit_before>use std::{io, fmt, thread, time};
use std::io::Read;
use std::marker::PhantomData;
use std::sync::Arc;
use std::sync::atomic::{AtomicPtr, Ordering};

use flate2::read::GzDecoder;
use hyper;
use hyper_tls;
use futures::{future, Future, Stream};
use futures::sync::oneshot;
use openssl;
use serde::{Deserialize, Serialize};
use serde_json;
use tokio_core::reactor;

use auth;

// (max) NOTE please don't use this directly - prefer 'access_hyper_client'
type HyperClient = hyper::Client<hyper_tls::HttpsConnector<hyper::client::HttpConnector>>;
lazy_static!(static ref HYPER_CLIENT: AtomicPtr<HyperClient> = Default::default(););

// (max) NOTE since Handle is only available on the reactor thread, we can
// enforce that HyperClient is only used there by requiring Handle as an arg
fn access_hyper_client(_: &reactor::Handle) -> &HyperClient {
    unsafe { &*HYPER_CLIENT.load(Ordering::SeqCst) }
}

const DNS_WORKER_THREADS: usize = 4;
const KEEP_ALIVE_TIMEOUT: u64 = 600; // 10 minutes

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

#[derive(Debug)]
pub enum Error {
    ApiError(ApiError),
    OpenSslError(openssl::error::ErrorStack),
    GzipError(::std::io::Error),
    HyperError(hyper::Error),
    JsonError(serde_json::Error),
    Unauthorized, // a generic "unauthorized" error
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> {
        match *self {
            Error::ApiError(ref e) => write!(f, "ApiError {:?}", e),
            Error::GzipError(ref e) => write!(f, "GzipError {:?}", e),
            Error::HyperError(ref e) => write!(f, "HyperError {:?}", e),
            Error::JsonError(ref e) => write!(f, "JsonError {:?}", e),
            Error::OpenSslError(ref e) => write!(f, "OpenSslError {:?}", e),
            Error::Unauthorized => write!(f, "Unauthorized"),
        }
    }
}

#[derive(Deserialize, Debug)]
pub struct ApiError {
    #[serde(default)]
    pub error: Option<ErrorDetails>,
    pub error_description: Option<String>,
}

#[derive(Deserialize, Debug)]
pub struct ErrorDetails {
    pub code: Option<usize>,
    pub message: Option<String>,
    pub status: Option<String>,
}

#[derive(Clone)]
pub struct GoogleCloudClient {
    project_id: String,
    remote: reactor::Remote,
    auth: auth::GoogleCloudAuth,
    _core_thread: Arc<thread::JoinHandle<()>>,
}

impl GoogleCloudClient {
    pub fn new(project_id: &str) -> io::Result<Self> {
        let (tx, rx) = oneshot::channel();
        let core_thread = thread::Builder::new()
            .name("google-cloud-client".into())
            .spawn(move || {
                let mut core = reactor::Core::new().unwrap();

                let https = hyper_tls::HttpsConnector::new(DNS_WORKER_THREADS, &core.handle())
                    .unwrap();

                let mut client = hyper::Client::configure()
                    .connector(https)
                    .keep_alive_timeout(Some(time::Duration::from_secs(KEEP_ALIVE_TIMEOUT)))
                    .keep_alive(true)
                    .build(&core.handle());

                HYPER_CLIENT.store(&mut client, Ordering::SeqCst);

                let remote = core.remote();
                tx.send(remote).unwrap();

                core.run(future::empty::<(), ()>()).unwrap()
            })?;

        let remote = rx.wait().unwrap();
        Ok(GoogleCloudClient {
            project_id: project_id.to_string(),
            remote: remote,
            auth: auth::default_credentials(),
            _core_thread: Arc::new(core_thread),
        })
    }
    pub fn hub<S>(&self) -> Hub<S> {
        Hub {
            client: self,
            _service: PhantomData,
        }
    }
}

pub struct Hub<'a, S> {
    client: &'a GoogleCloudClient,
    _service: PhantomData<S>,
}

impl<'a, S> Hub<'a, S> {
    pub fn project_id(&self) -> &str {
        &self.client.project_id
    }
}

impl<'a> Hub<'a, ::svc::tokeninfo::TokenInfoService> {
    pub fn get_firebase_pkey(&self, kid: &str) -> Result<auth::PubKey> {
        self.client.auth.get_firebase_pkey(self, kid)
    }
    pub fn get_google_auth_pkey(&self, kid: &str) -> Result<auth::PubKey> {
        self.client.auth.get_google_auth_pkey(self, kid)
    }
    // NOTE this flow is described in the following google documentation
    //
    // https://developers.google.com/identity/protocols/OAuth2ServiceAccount#authorizingrequests
    pub fn delegate(&self, id_token: &str, scopes: &[String]) -> Result<auth::Token> {
        self.client.auth.delegate(self, id_token, scopes)
    }
}

impl<'a, S> ApiClient for Hub<'a, S> {
    fn token(&self, scopes: &[String]) -> Result<auth::Token> {
        self.client.auth.token(self, scopes)
    }
    fn request<D: 'static + Send>(
        &self,
        mut r: hyper::Request<hyper::Body>,
    ) -> Result<(hyper::Headers, D)>
    where
        for<'de> D: 'static + Send + Deserialize<'de>,
    {
        use hyper::header::{AcceptEncoding, ContentEncoding, Encoding, UserAgent, qitem};
        // https://cloud.google.com/bigquery/docs/api-performance
        r.headers_mut().set(
            AcceptEncoding(vec![qitem(Encoding::Gzip)]),
        );
        r.headers_mut().set(UserAgent::new(
            "Sigma Computing, Inc. (gzip)",
        ));

        trace!("send request: {:?}", r);
        let (tx, rx) = oneshot::channel();

        self.client.remote.spawn(|handle| {
            let work = access_hyper_client(handle).request(r);
            work.and_then(|res| {
                trace!("recv response: {:?}", res);
                let status = res.status();
                let headers = res.headers().clone();

                res.body()
                    .collect()
                    .map(move |chunks| {
                        trace!("fold chunks: {:?}", chunks);

                        let body = chunks.into_iter().fold(vec![], |mut acc, chunk| {
                            acc.extend_from_slice(&*chunk);
                            acc
                        });
                        let body = match headers.get::<ContentEncoding>() {
                            Some(&ContentEncoding(ref encs)) if encs.contains(&Encoding::Gzip) => {
                                let mut unzipped = vec![];
                                GzDecoder::new(body.as_slice())
                                    .map_err(Error::GzipError)?
                                    .read_to_end(&mut unzipped)
                                    .map_err(Error::GzipError)?;
                                unzipped
                            }
                            _ => body,
                        };

                        let as_str = unsafe { ::std::str::from_utf8_unchecked(&body) };
                        trace!("recv oneshot: {}", as_str);

                        if status.is_success() {
                            match serde_json::from_slice(&body) {
                                Ok(res) => Ok((headers, res)),
                                Err(e) => Err(Error::JsonError(e)),
                            }
                        } else {
                            match serde_json::from_slice::<ApiError>(&body) {
                                Ok(e) => Err(Error::ApiError(e)),
                                Err(e) => Err(Error::JsonError(e)),
                            }
                        }
                    })
                    .map(|res| {
                        tx.send(res.map(|(headers, res)| (headers, res)))
                            .unwrap_or(())
                    })
            }).map_err(|_| ())
        });
        rx.wait().unwrap()
    }
}

pub fn encode_query_params<'a, I>(i: I) -> String
where
    I: IntoIterator<Item = (&'a str, String)>,
{
    use url::percent_encoding::{utf8_percent_encode, QUERY_ENCODE_SET};

    let mut query = String::new();
    for (i, (k, v)) in i.into_iter().enumerate() {
        if i != 0 {
            query.push('&');
        }
        query.extend(utf8_percent_encode(k, QUERY_ENCODE_SET));
        query.push('=');
        query.extend(utf8_percent_encode(&v, QUERY_ENCODE_SET));
    }
    query
}

pub trait ApiClient {
    // submits a raw request using hyper
    fn request<D>(&self, hyper::Request<hyper::Body>) -> Result<(hyper::Headers, D)>
    where
        for<'de> D: 'static + Send + Deserialize<'de>;

    // fetches an access token for use in requests
    fn token(&self, &[String]) -> Result<auth::Token>;

    // helper method for making a GET request
    fn get<D>(&self, uri: &hyper::Uri, scopes: &[String]) -> Result<D>
    where
        for<'de> D: 'static + Send + Deserialize<'de>,
    {
        let mut req = hyper::Request::new(hyper::Method::Get, uri.clone());
        req.headers_mut().set(self.token(scopes)?.into_header());

        self.request(req).map(|(_, res)| res)
    }

    // helper method for making a POST request with a JSON body
    fn post<B: Serialize, D>(&self, uri: &hyper::Uri, body: B, scopes: &[String]) -> Result<D>
    where
        for<'de> D: 'static + Send + Deserialize<'de>,
    {
        let mut req = hyper::Request::new(hyper::Method::Post, uri.clone());
        req.headers_mut().set(hyper::header::ContentType::json());
        req.headers_mut().set(self.token(scopes)?.into_header());

        let body = serde_json::to_string(&body).unwrap();
        req.set_body(body);

        self.request(req).map(|(_, res)| res)
    }
}
<commit_msg>quick fix to handle using multiple clients<commit_after>use std::{io, fmt, thread, time};
use std::io::Read;
use std::marker::PhantomData;
use std::sync::atomic::{AtomicPtr, Ordering};

use flate2::read::GzDecoder;
use hyper;
use hyper_tls;
use futures::{future, Future, Stream};
use futures::sync::oneshot;
use openssl;
use serde::{Deserialize, Serialize};
use serde_json;
use tokio_core::reactor;

use auth;

// (max) NOTE please don't use this directly - prefer 'access_hyper_client'
type HyperClient = hyper::Client<hyper_tls::HttpsConnector<hyper::client::HttpConnector>>;
lazy_static!{
    static ref HYPER_CLIENT: AtomicPtr<HyperClient> = Default::default();
    static ref CORE_THREAD__REMOTE: (thread::JoinHandle<()>, reactor::Remote) = init_core_thread();
}

// (max) NOTE since Handle is only available on the reactor thread, we can
// enforce that HyperClient is only used there by requiring Handle as an arg
fn access_hyper_client(_: &reactor::Handle) -> &HyperClient {
    unsafe { &*HYPER_CLIENT.load(Ordering::SeqCst) }
}

const DNS_WORKER_THREADS: usize = 4;
const KEEP_ALIVE_TIMEOUT: u64 = 600; // 10 minutes

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

#[derive(Debug)]
pub enum Error {
    ApiError(ApiError),
    OpenSslError(openssl::error::ErrorStack),
    GzipError(::std::io::Error),
    HyperError(hyper::Error),
    JsonError(serde_json::Error),
    Unauthorized, // a generic "unauthorized" error
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> {
        match *self {
            Error::ApiError(ref e) => write!(f, "ApiError {:?}", e),
            Error::GzipError(ref e) => write!(f, "GzipError {:?}", e),
            Error::HyperError(ref e) => write!(f, "HyperError {:?}", e),
            Error::JsonError(ref e) => write!(f, "JsonError {:?}", e),
            Error::OpenSslError(ref e) => write!(f, "OpenSslError {:?}", e),
            Error::Unauthorized => write!(f, "Unauthorized"),
        }
    }
}

#[derive(Deserialize, Debug)]
pub struct ApiError {
    #[serde(default)]
    pub error: Option<ErrorDetails>,
    pub error_description: Option<String>,
}

#[derive(Deserialize, Debug)]
pub struct ErrorDetails {
    pub code: Option<usize>,
    pub message: Option<String>,
    pub status: Option<String>,
}

#[derive(Clone)]
pub struct GoogleCloudClient {
    project_id: String,
    remote: reactor::Remote,
    auth: auth::GoogleCloudAuth,
}

impl GoogleCloudClient {
    pub fn new(project_id: &str) -> io::Result<Self> {
        Ok(GoogleCloudClient {
            project_id: project_id.to_string(),
            remote: CORE_THREAD__REMOTE.1.clone(),
            auth: auth::default_credentials(),
        })
    }
    pub fn hub<S>(&self) -> Hub<S> {
        Hub {
            client: self,
            _service: PhantomData,
        }
    }
}

pub struct Hub<'a, S> {
    client: &'a GoogleCloudClient,
    _service: PhantomData<S>,
}

impl<'a, S> Hub<'a, S> {
    pub fn project_id(&self) -> &str {
        &self.client.project_id
    }
}

impl<'a> Hub<'a, ::svc::tokeninfo::TokenInfoService> {
    pub fn get_firebase_pkey(&self, kid: &str) -> Result<auth::PubKey> {
        self.client.auth.get_firebase_pkey(self, kid)
    }
    pub fn get_google_auth_pkey(&self, kid: &str) -> Result<auth::PubKey> {
        self.client.auth.get_google_auth_pkey(self, kid)
    }
    // NOTE this flow is described in the following google documentation
    //
    // https://developers.google.com/identity/protocols/OAuth2ServiceAccount#authorizingrequests
    pub fn delegate(&self, id_token: &str, scopes: &[String]) -> Result<auth::Token> {
        self.client.auth.delegate(self, id_token, scopes)
    }
}

impl<'a, S> ApiClient for Hub<'a, S> {
    fn token(&self, scopes: &[String]) -> Result<auth::Token> {
        self.client.auth.token(self, scopes)
    }
    fn request<D: 'static + Send>(
        &self,
        mut r: hyper::Request<hyper::Body>,
    ) -> Result<(hyper::Headers, D)>
    where
        for<'de> D: 'static + Send + Deserialize<'de>,
    {
        use hyper::header::{AcceptEncoding, ContentEncoding, Encoding, UserAgent, qitem};
        // https://cloud.google.com/bigquery/docs/api-performance
        r.headers_mut().set(
            AcceptEncoding(vec![qitem(Encoding::Gzip)]),
        );
        r.headers_mut().set(UserAgent::new(
            "Sigma Computing, Inc. (gzip)",
        ));

        trace!("send request: {:?}", r);
        let (tx, rx) = oneshot::channel();

        self.client.remote.spawn(|handle| {
            let work = access_hyper_client(handle).request(r);
            work.and_then(|res| {
                trace!("recv response: {:?}", res);
                let status = res.status();
                let headers = res.headers().clone();

                res.body()
                    .collect()
                    .map(move |chunks| {
                        trace!("fold chunks: {:?}", chunks);

                        let body = chunks.into_iter().fold(vec![], |mut acc, chunk| {
                            acc.extend_from_slice(&*chunk);
                            acc
                        });
                        let body = match headers.get::<ContentEncoding>() {
                            Some(&ContentEncoding(ref encs)) if encs.contains(&Encoding::Gzip) => {
                                let mut unzipped = vec![];
                                GzDecoder::new(body.as_slice())
                                    .map_err(Error::GzipError)?
                                    .read_to_end(&mut unzipped)
                                    .map_err(Error::GzipError)?;
                                unzipped
                            }
                            _ => body,
                        };

                        let as_str = unsafe { ::std::str::from_utf8_unchecked(&body) };
                        trace!("recv oneshot: {}", as_str);

                        if status.is_success() {
                            match serde_json::from_slice(&body) {
                                Ok(res) => Ok((headers, res)),
                                Err(e) => Err(Error::JsonError(e)),
                            }
                        } else {
                            match serde_json::from_slice::<ApiError>(&body) {
                                Ok(e) => Err(Error::ApiError(e)),
                                Err(e) => Err(Error::JsonError(e)),
                            }
                        }
                    })
                    .map(|res| {
                        tx.send(res.map(|(headers, res)| (headers, res)))
                            .unwrap_or(())
                    })
            }).map_err(|_| ())
        });
        rx.wait().unwrap()
    }
}

pub fn encode_query_params<'a, I>(i: I) -> String
where
    I: IntoIterator<Item = (&'a str, String)>,
{
    use url::percent_encoding::{utf8_percent_encode, QUERY_ENCODE_SET};

    let mut query = String::new();
    for (i, (k, v)) in i.into_iter().enumerate() {
        if i != 0 {
            query.push('&');
        }
        query.extend(utf8_percent_encode(k, QUERY_ENCODE_SET));
        query.push('=');
        query.extend(utf8_percent_encode(&v, QUERY_ENCODE_SET));
    }
    query
}

pub trait ApiClient {
    // submits a raw request using hyper
    fn request<D>(&self, hyper::Request<hyper::Body>) -> Result<(hyper::Headers, D)>
    where
        for<'de> D: 'static + Send + Deserialize<'de>;

    // fetches an access token for use in requests
    fn token(&self, &[String]) -> Result<auth::Token>;

    // helper method for making a GET request
    fn get<D>(&self, uri: &hyper::Uri, scopes: &[String]) -> Result<D>
    where
        for<'de> D: 'static + Send + Deserialize<'de>,
    {
        let mut req = hyper::Request::new(hyper::Method::Get, uri.clone());
        req.headers_mut().set(self.token(scopes)?.into_header());

        self.request(req).map(|(_, res)| res)
    }

    // helper method for making a POST request with a JSON body
    fn post<B: Serialize, D>(&self, uri: &hyper::Uri, body: B, scopes: &[String]) -> Result<D>
    where
        for<'de> D: 'static + Send + Deserialize<'de>,
    {
        let mut req = hyper::Request::new(hyper::Method::Post, uri.clone());
        req.headers_mut().set(hyper::header::ContentType::json());
        req.headers_mut().set(self.token(scopes)?.into_header());

        let body = serde_json::to_string(&body).unwrap();
        req.set_body(body);

        self.request(req).map(|(_, res)| res)
    }
}

/// this will be called when initializing CORE_THREAD__REMOTE
fn init_core_thread() -> (thread::JoinHandle<()>, reactor::Remote) {
    let (tx, rx) = oneshot::channel();
    let core_thread = thread::Builder::new()
        .name("google-cloud-client".into())
        .spawn(move || {
            let mut core = reactor::Core::new().unwrap();

            let https = hyper_tls::HttpsConnector::new(DNS_WORKER_THREADS, &core.handle()).unwrap();

            let mut client = hyper::Client::configure()
                .connector(https)
                .keep_alive_timeout(Some(time::Duration::from_secs(KEEP_ALIVE_TIMEOUT)))
                .keep_alive(true)
                .build(&core.handle());

            HYPER_CLIENT.store(&mut client, Ordering::SeqCst);

            let remote = core.remote();
            tx.send(remote).unwrap();

            core.run(future::empty::<(), ()>()).unwrap()
        })
        .expect("spawning core thread should succeed");

    (core_thread, rx.wait().unwrap())
}
<|endoftext|>


<commit_before>use hyper::client::Client;
use hyper::client::response::Response;
use hyper::error::Error;
use hyper::header::Headers;
use hyper::method::Method;
use hyper::net::HttpsConnector;
use hyper_openssl::OpensslClient;

use SSL_SUPPORT;

/// Returns an Hyper client, which enables (or not) an HTTPS connector
pub fn get_hyper_client() -> Client {
    unsafe {
        if !SSL_SUPPORT {
            return Client::default();
        }
    }
    // Get client enabling SSL
    let ssl = OpensslClient::new().unwrap();
    // Connect the SSL client with HttpsConnector from Hyper
    let connector = HttpsConnector::new(ssl);
    // Return the client
    Client::with_connector(connector)
}

/// Trait that represents some methods to send a specific request
pub trait GetResponse {
    /// Given a specific URL, get the header without the content body (useful to not waste time,
    /// ressources and informations)
    fn get_head_response(&self, url: &str) -> Result<Response, Error>;

    /// Given a specific URL and an header, get the header without the content body (useful to not
    /// waste time, ressources and informations)
    fn get_head_response_using_headers(
        &self,
        url: &str,
        header: Headers,
    ) -> Result<Response, Error>;

    /// Given a specific URL, get the response from the target server
    fn get_http_response(&self, url: &str) -> Result<Response, Error>;

    /// Given a specific URL and an header, get the response from the target server
    fn get_http_response_using_headers(
        &self,
        url: &str,
        header: Headers,
    ) -> Result<Response, Error>;
}

impl GetResponse for Client {
    fn get_head_response(&self, url: &str) -> Result<Response, Error> {
        self.request(Method::Head, url).send()
    }

    fn get_head_response_using_headers(
        &self,
        url: &str,
        custom_header: Headers,
    ) -> Result<Response, Error> {
        self.request(Method::Head, url)
            .headers(custom_header)
            .send()
    }

    fn get_http_response(&self, url: &str) -> Result<Response, Error> {
        self.get_http_response_using_headers(url, Headers::new())
    }

    fn get_http_response_using_headers(
        &self,
        url: &str,
        custom_header: Headers,
    ) -> Result<Response, Error> {
        self.request(Method::Get, url).headers(custom_header).send()
    }
}
<commit_msg>Remove usage of SSL_SUPPORT static variable, and add a new trait + struct to enable HTTPS client<commit_after>use hyper::client::Client;
use hyper::client::response::Response;
use hyper::error::Error;
use hyper::header::Headers;
use hyper::method::Method;
use hyper::net::HttpsConnector;
use hyper_openssl::OpensslClient;

/// Structure to store if SSL is required, and to
/// implement a default HTTP/HTTPS client
pub struct Config {
    pub enable_ssl: bool,
}

impl Config {
    /// Get the HTTP/HTTPS Hyper client
    pub fn get_hyper_client(&self) -> Client {
        if !self.enable_ssl {
            return Client::default();
        }
        Client::default_ssl()
    }
}

/// Trait to instantiate an Hyper client, with SSL support
trait SSLSupport {
    /// Function to return a Client, with SSL support
    fn default_ssl() -> Client;
}

impl SSLSupport for Client {
    fn default_ssl() -> Client {
        let ssl = OpensslClient::new().unwrap();
        let connector = HttpsConnector::new(ssl);
        Client::with_connector(connector)
    }
}

/// Trait that represents some methods to send a specific request
pub trait GetResponse {
    /// Given a specific URL, get the header without the content body (useful to not waste time,
    /// ressources and informations)
    fn get_head_response(&self, url: &str) -> Result<Response, Error>;

    /// Given a specific URL and an header, get the header without the content body (useful to not
    /// waste time, ressources and informations)
    fn get_head_response_using_headers(
        &self,
        url: &str,
        header: Headers,
    ) -> Result<Response, Error>;

    /// Given a specific URL, get the response from the target server
    fn get_http_response(&self, url: &str) -> Result<Response, Error>;

    /// Given a specific URL and an header, get the response from the target server
    fn get_http_response_using_headers(
        &self,
        url: &str,
        header: Headers,
    ) -> Result<Response, Error>;
}

impl GetResponse for Client {
    fn get_head_response(&self, url: &str) -> Result<Response, Error> {
        self.request(Method::Head, url).send()
    }

    fn get_head_response_using_headers(
        &self,
        url: &str,
        custom_header: Headers,
    ) -> Result<Response, Error> {
        self.request(Method::Head, url)
            .headers(custom_header)
            .send()
    }

    fn get_http_response(&self, url: &str) -> Result<Response, Error> {
        self.get_http_response_using_headers(url, Headers::new())
    }

    fn get_http_response_using_headers(
        &self,
        url: &str,
        custom_header: Headers,
    ) -> Result<Response, Error> {
        self.request(Method::Get, url).headers(custom_header).send()
    }
}
<|endoftext|>


<commit_before>use std::process;
use std::io;
use std::io::Write;

extern crate amqp;
use amqp::{Session, Options, Channel};
use amqp::protocol::basic::{Deliver, BasicProperties};
use amqp::Basic;
use amqp::{Table, TableEntry};

macro_rules! exitln(
    ($($arg:tt)*) => { {
        let r = writeln!(&mut ::std::io::stderr(), $($arg)*);
        r.expect("failed printing to stderr");
        process::exit(1);
    } }
);

pub struct Sendable {
    pub exchange: String,
    pub routing_key: String,
    pub content_type: String,
    pub headers: Vec<String>,
    pub file_name: String,
    pub reader: Box<io::Read>,
}

fn _open(o:Options) -> (Session, Channel) {
    let mut session = Session::new(o)
        .unwrap_or_else(|e| exitln!("Error: {:?}", e));
    let channel = session.open_channel(1)
        .unwrap_or_else(|e| exitln!("Error: {:?}", e));
    (session, channel)
}

pub fn open_send(o:Options, s:Sendable) {
    let (mut session, mut channel) = _open(o);
    let mut headers = s.headers.iter().fold(Table::new(), |mut h, st| {
        let idx = st.find(':').expect("Header must have a :");
        let (name, value) = st.split_at(idx);
        let key = name.trim();
        let val = (&value[1..]).trim();
        h.insert(String::from(key), TableEntry::LongString(String::from(val)));
        h
    });
    if s.file_name != "-" && !headers.contains_key("fileName") {
        headers.insert("fileName".to_owned(), TableEntry::LongString(String::from(s.file_name)));
    }
    let props = BasicProperties {
        content_type: Some(s.content_type),
        headers: Some(headers),
        ..Default::default()
    };
    let mut buffer = Box::new(vec![]);
    let mut reader = s.reader;
    reader.read_to_end(&mut buffer)
        .unwrap_or_else(|e| exitln!("Error: {:?}", e));
    channel.basic_publish(s.exchange, s.routing_key, false, false, props, *buffer)
        .unwrap_or_else(|e| exitln!("Error: {:?}", e));
    channel.close(200, "Bye")
        .unwrap_or_else(|e| exitln!("Error: {:?}", e));
    session.close(200, "Good Bye");
}

pub type ReceiverCallback = fn(deliver:Deliver,
                               props:BasicProperties,
                               body:Vec<u8>);

pub struct Receiver {
    pub exchange:String,
    pub routing_key:String,
    pub callback:Box<FnMut(Deliver, BasicProperties, Vec<u8>) + Send>,
}

impl amqp::Consumer for Receiver {
    fn handle_delivery(&mut self, channel:&mut Channel, deliver:Deliver,
                       headers:BasicProperties, body:Vec<u8>){

        // ack it, not that we're in ack mode...
        channel.basic_ack(deliver.delivery_tag, false).unwrap();

        // and deliver to callback
        (self.callback)(deliver, headers, body)
    }
}

pub fn open_receive(o:Options, r:Receiver) {

    // open session/channel
    let (_, mut channel) = _open(o);

    // declare an exclusive anonymous queue that auto deletes
    // when the process exits.
    // queue, passive, durable, exclusive, auto_delete, nowait,  arguments
    let queue_declare =
        channel.queue_declare("", false, false, true,
                              true, false, Table::new())
        .unwrap_or_else(|e| exitln!("Error: {:?}", e));

    // name is auto generated
    let queue_name = queue_declare.queue;

    // bind queue to the exchange, which already must
    // be declared.
    channel.queue_bind(queue_name.clone(), r.exchange.clone(), r.routing_key.clone(),
                       false, Table::new())
        .unwrap_or_else(|e| exitln!("Error: {:?}", e));

    // why oh why?
    let consumer_tag = String::from("");

    // start consuming the queue.
    channel.basic_consume(r, queue_name, consumer_tag, false,
                          false, false, false, Table::new())
        .unwrap_or_else(|e| exitln!("Error: {:?}", e));

    // and go!
    channel.start_consuming();

    //    channel.close(200, "Bye")
    //        .unwrap_or_else(|e| exitln!("Error: {:?}", e));
    //    session.close(200, "Good Bye");

}
<commit_msg>ack after delivery<commit_after>use std::process;
use std::io;
use std::io::Write;

extern crate amqp;
use amqp::{Session, Options, Channel};
use amqp::protocol::basic::{Deliver, BasicProperties};
use amqp::Basic;
use amqp::{Table, TableEntry};

macro_rules! exitln(
    ($($arg:tt)*) => { {
        let r = writeln!(&mut ::std::io::stderr(), $($arg)*);
        r.expect("failed printing to stderr");
        process::exit(1);
    } }
);

pub struct Sendable {
    pub exchange: String,
    pub routing_key: String,
    pub content_type: String,
    pub headers: Vec<String>,
    pub file_name: String,
    pub reader: Box<io::Read>,
}

fn _open(o:Options) -> (Session, Channel) {
    let mut session = Session::new(o)
        .unwrap_or_else(|e| exitln!("Error: {:?}", e));
    let channel = session.open_channel(1)
        .unwrap_or_else(|e| exitln!("Error: {:?}", e));
    (session, channel)
}

pub fn open_send(o:Options, s:Sendable) {
    let (mut session, mut channel) = _open(o);
    let mut headers = s.headers.iter().fold(Table::new(), |mut h, st| {
        let idx = st.find(':').expect("Header must have a :");
        let (name, value) = st.split_at(idx);
        let key = name.trim();
        let val = (&value[1..]).trim();
        h.insert(String::from(key), TableEntry::LongString(String::from(val)));
        h
    });
    if s.file_name != "-" && !headers.contains_key("fileName") {
        headers.insert("fileName".to_owned(), TableEntry::LongString(String::from(s.file_name)));
    }
    let props = BasicProperties {
        content_type: Some(s.content_type),
        headers: Some(headers),
        ..Default::default()
    };
    let mut buffer = Box::new(vec![]);
    let mut reader = s.reader;
    reader.read_to_end(&mut buffer)
        .unwrap_or_else(|e| exitln!("Error: {:?}", e));
    channel.basic_publish(s.exchange, s.routing_key, false, false, props, *buffer)
        .unwrap_or_else(|e| exitln!("Error: {:?}", e));
    channel.close(200, "Bye")
        .unwrap_or_else(|e| exitln!("Error: {:?}", e));
    session.close(200, "Good Bye");
}

pub type ReceiverCallback = fn(deliver:Deliver,
                               props:BasicProperties,
                               body:Vec<u8>);

pub struct Receiver {
    pub exchange:String,
    pub routing_key:String,
    pub callback:Box<FnMut(Deliver, BasicProperties, Vec<u8>) + Send>,
}

impl amqp::Consumer for Receiver {
    fn handle_delivery(&mut self, channel:&mut Channel, deliver:Deliver,
                       headers:BasicProperties, body:Vec<u8>){

        let delivery_tag = deliver.delivery_tag.clone();

        // and deliver to callback
        (self.callback)(deliver, headers, body);

        // ack it. not that we're in ack mode...
        channel.basic_ack(delivery_tag, false).unwrap();

    }
}

pub fn open_receive(o:Options, r:Receiver) {

    // open session/channel
    let (_, mut channel) = _open(o);

    // declare an exclusive anonymous queue that auto deletes
    // when the process exits.
    // queue, passive, durable, exclusive, auto_delete, nowait,  arguments
    let queue_declare =
        channel.queue_declare("", false, false, true,
                              true, false, Table::new())
        .unwrap_or_else(|e| exitln!("Error: {:?}", e));

    // name is auto generated
    let queue_name = queue_declare.queue;

    // bind queue to the exchange, which already must
    // be declared.
    channel.queue_bind(queue_name.clone(), r.exchange.clone(), r.routing_key.clone(),
                       false, Table::new())
        .unwrap_or_else(|e| exitln!("Error: {:?}", e));

    // why oh why?
    let consumer_tag = String::from("");

    // start consuming the queue.
    channel.basic_consume(r, queue_name, consumer_tag, false,
                          false, false, false, Table::new())
        .unwrap_or_else(|e| exitln!("Error: {:?}", e));

    // and go!
    channel.start_consuming();

    //    channel.close(200, "Bye")
    //        .unwrap_or_else(|e| exitln!("Error: {:?}", e));
    //    session.close(200, "Good Bye");

}
<|endoftext|>


<commit_before>use std::sync::mpsc;
use std::sync::Arc;
use std::thread;
use std::io;
use std::net::SocketAddr;
use std::net::ToSocketAddrs;

use bytes::Bytes;

use futures::future;
use futures::future::Future;
use futures::stream::Stream;
use futures::sync::mpsc::unbounded;
use futures::sync::mpsc::UnboundedSender;
use futures::sync::mpsc::UnboundedReceiver;
use futures::sync::oneshot;

use tokio_core::reactor;

use tls_api::TlsConnector;
use tls_api::TlsConnectorBuilder;
use tls_api_stub;

use futures_misc::*;

use error;
use error::Error;
use result::Result;

use solicit::header::*;
use solicit::HttpScheme;
use solicit::StreamId;

use solicit_async::*;

use client_conn::*;
use client_conf::*;
use common::*;
use stream_part::*;
use service::Service;

pub use client_tls::ClientTlsOption;

// Data sent from event loop to Http2Client
struct LoopToClient {
    // used only once to send shutdown signal
    shutdown: ShutdownSignal,
    _loop_handle: reactor::Remote,
    controller_tx: UnboundedSender<ControllerCommand>,
}

pub struct Client {
    loop_to_client: LoopToClient,
    thread_join_handle: Option<thread::JoinHandle<()>>,
    http_scheme: HttpScheme,
}

impl Client {

    pub fn new_plain(host: &str, port: u16, conf: ClientConf) -> Result<Client> {
        // TODO: sync
        // TODO: try connect to all addrs
        let socket_addr = (host, port).to_socket_addrs()?.next().expect("resolve host/port");

        let tls_enabled: ClientTlsOption<tls_api_stub::TlsConnector> = ClientTlsOption::Plain;

        Client::new_expl(&socket_addr, tls_enabled, conf)
    }

    pub fn new_tls<C : TlsConnector>(host: &str, port: u16, conf: ClientConf) -> Result<Client> {
        // TODO: sync
        // TODO: try connect to all addrs
        let socket_addr = (host, port).to_socket_addrs()?.next().expect("resolve host/port");

        let tls_enabled = {
            let mut tls_connector = C::builder()?;

            if C::supports_alpn() {
                // TODO: check negotiated protocol after connect
                tls_connector.set_alpn_protocols(&[b"h2"])?;
            }

            let tls_connector = tls_connector.build()?;

            let tls_connector = Arc::new(tls_connector);
            ClientTlsOption::Tls(host.to_owned(), tls_connector)
        };

        Client::new_expl(&socket_addr, tls_enabled, conf)
    }

    pub fn new_expl<C : TlsConnector>(addr: &SocketAddr, tls: ClientTlsOption<C>, conf: ClientConf) -> Result<Client> {
        // We need some data back from event loop.
        // This channel is used to exchange that data
        let (get_from_loop_tx, get_from_loop_rx) = mpsc::channel();

        let addr = addr.clone();
        let http_scheme = tls.http_scheme();

        // Start event loop.
        let join_handle = thread::Builder::new()
            .name(conf.thread_name.clone().unwrap_or_else(|| "http2-client-loop".to_owned()).to_string())
            .spawn(move || {
                run_client_event_loop(addr, tls, conf, get_from_loop_tx);
            })
            .expect("spawn");

        // Get back call channel and shutdown channel.
        let loop_to_client = get_from_loop_rx.recv()
            .map_err(|_| Error::IoError(io::Error::new(io::ErrorKind::Other, "get response from loop")))?;

        Ok(Client {
            loop_to_client: loop_to_client,
            thread_join_handle: Some(join_handle),
            http_scheme: http_scheme,
        })
    }

    pub fn start_request_simple(
        &self,
        headers: Headers,
        body: Bytes)
            -> Response
    {
        self.start_request(
            headers,
            HttpPartStream::once_bytes(body))
    }

    pub fn start_get(
        &self,
        path: &str,
        authority: &str)
            -> Response
    {
        let headers = Headers(vec![
            Header::new(":method", "GET"),
            Header::new(":path", path.to_owned()),
            Header::new(":authority", authority.to_owned()),
            Header::new(":scheme", self.http_scheme.as_bytes()),
        ]);
        self.start_request_simple(headers, Bytes::new())
    }

    pub fn start_post(
        &self,
        path: &str,
        authority: &str,
        body: Bytes)
            -> Response
    {
        let headers = Headers(vec![
            Header::new(":method", "POST"),
            Header::new(":path", path.to_owned()),
            Header::new(":authority", authority.to_owned()),
            Header::new(":scheme", self.http_scheme.as_bytes()),
        ]);
        self.start_request_simple(headers, body)
    }

    pub fn dump_state(&self) -> HttpFutureSend<ConnectionStateSnapshot> {
        let (tx, rx) = oneshot::channel();
        // ignore error
        drop(self.loop_to_client.controller_tx.send(ControllerCommand::DumpState(tx)));
        Box::new(rx.map_err(|_| error::Error::Other("conn died")))
    }

    pub fn wait_for_connect(&self) -> HttpFutureSend<()> {
        let (tx, rx) = oneshot::channel();
        // ignore error
        drop(self.loop_to_client.controller_tx.send(ControllerCommand::WaitForConnect(tx)));
        Box::new(rx.map_err(|_| error::Error::Other("conn died")).and_then(|r| r))
    }
}

impl Service for Client {
    // TODO: copy-paste with ClientConnection::start_request
    fn start_request(
        &self,
        headers: Headers,
        body: HttpPartStream)
            -> Response
    {
        let (resp_tx, resp_rx) = oneshot::channel();

        let start = StartRequestMessage {
            headers: headers,
            body: body,
            resp_tx: resp_tx,
        };

        if let Err(_) = self.loop_to_client.controller_tx.send(ControllerCommand::StartRequest(start)) {
            return Response::err(error::Error::Other("client controller died"));
        }

        let resp_rx = resp_rx.map_err(|oneshot::Canceled| error::Error::Other("client likely died"));

        let resp_rx = resp_rx.map(|r| r.into_stream_flag());

        let resp_rx = resp_rx.flatten_stream();

        Response::from_stream(resp_rx)
    }}

enum ControllerCommand {
    GoAway,
    StartRequest(StartRequestMessage),
    WaitForConnect(oneshot::Sender<Result<()>>),
    DumpState(oneshot::Sender<ConnectionStateSnapshot>),
}

struct ControllerState<C : TlsConnector> {
    handle: reactor::Handle,
    socket_addr: SocketAddr,
    tls: ClientTlsOption<C>,
    conf: ClientConf,
    // current connection
    conn: Arc<ClientConnection>,
    tx: UnboundedSender<ControllerCommand>,
}

impl<C : TlsConnector> ControllerState<C> {
    fn init_conn(&mut self) {
        let (conn, future) = ClientConnection::new(
            self.handle.clone(),
            &self.socket_addr,
            self.tls.clone(),
            self.conf.clone(),
            CallbacksImpl {
                tx: self.tx.clone(),
            });

        self.handle.spawn(future.map_err(|e| { warn!("client error: {:?}", e); () }));

        self.conn = Arc::new(conn);
    }

    fn iter(mut self, cmd: ControllerCommand) -> ControllerState<C> {
        match cmd {
            ControllerCommand::GoAway => {
                self.init_conn();
            },
            ControllerCommand::StartRequest(start) => {
                if let Err(start) = self.conn.start_request_with_resp_sender(start) {
                    self.init_conn();
                    if let Err(start) = self.conn.start_request_with_resp_sender(start) {
                        let err = error::Error::Other("client died and reconnect failed");
                        // ignore error
                        if let Err(_) = start.resp_tx.send(Response::err(err)) {
                            debug!("called likely died");
                        }
                    }
                }
            }
            ControllerCommand::WaitForConnect(tx) => {
                if let Err(tx) = self.conn.wait_for_connect_with_resp_sender(tx) {
                    self.init_conn();
                    if let Err(tx) = self.conn.wait_for_connect_with_resp_sender(tx) {
                        let err = error::Error::Other("client died and reconnect failed");
                        // ignore error
                        drop(tx.send(Err(err)));
                    }
                }
            }
            ControllerCommand::DumpState(tx) => {
                self.conn.dump_state_with_resp_sender(tx);
            }
        }
        self
    }

    fn run(self, rx: UnboundedReceiver<ControllerCommand>)
        -> HttpFuture<()>
    {
        let rx = rx.map_err(|_| error::Error::Other("channel died"));
        let r = rx.fold(self, |state, cmd| {
            Ok::<_, error::Error>(state.iter(cmd))
        });
        let r = r.map(|_| ());
        Box::new(r)
    }
}

struct CallbacksImpl {
    tx: UnboundedSender<ControllerCommand>,
}

impl ClientConnectionCallbacks for CallbacksImpl {
    fn goaway(&self, _stream_id: StreamId, _error_code: u32) {
        drop(self.tx.send(ControllerCommand::GoAway));
    }
}

// Event loop entry point
fn run_client_event_loop<C : TlsConnector>(
    socket_addr: SocketAddr,
    tls: ClientTlsOption<C>,
    conf: ClientConf,
    send_to_back: mpsc::Sender<LoopToClient>)
{
    // Create an event loop.
    let mut lp: reactor::Core = reactor::Core::new().expect("Core::new");

    // Create a channel to receive shutdown signal.
    let (shutdown_signal, shutdown_future) = shutdown_signal();

    let (controller_tx, controller_rx) = unbounded();

    let (http_conn, conn_future) =
        ClientConnection::new(lp.handle(), &socket_addr, tls.clone(), conf.clone(), CallbacksImpl {
            tx: controller_tx.clone(),
        });

    lp.handle().spawn(conn_future.map_err(|e| { warn!("client error: {:?}", e); () }));

    let init = ControllerState {
        handle: lp.handle(),
        socket_addr: socket_addr.clone(),
        tls: tls,
        conf: conf,
        conn: Arc::new(http_conn),
        tx: controller_tx.clone(),
    };

    let controller_future = init.run(controller_rx);

    // Send channels back to Http2Client
    send_to_back
        .send(LoopToClient {
            shutdown: shutdown_signal,
            _loop_handle: lp.remote(),
            controller_tx: controller_tx,
        })
        .expect("send back");

    let shutdown_future = shutdown_future
        .then(move |_| {
            // Must complete with error,
            // so `join` with this future cancels another future.
            future::failed::<(), _>(Error::Shutdown)
        });

    // Wait for either completion of connection (i. e. error)
    // or shutdown signal.
    let done = controller_future.join(shutdown_future);

    match lp.run(done) {
        Ok(_) => {}
        Err(Error::Shutdown) => {}
        Err(e) => {
            error!("Core::run failed: {:?}", e);
        }
    }
}

// We shutdown the client in the destructor.
impl Drop for Client {
    fn drop(&mut self) {
        self.loop_to_client.shutdown.shutdown();

        // do not ignore errors because we own event loop thread
        self.thread_join_handle.take().expect("handle.take")
            .join().expect("join thread");
    }
}
<commit_msg>Code cleanup<commit_after>use std::sync::mpsc;
use std::sync::Arc;
use std::thread;
use std::io;
use std::net::SocketAddr;
use std::net::ToSocketAddrs;

use bytes::Bytes;

use futures::future;
use futures::future::Future;
use futures::stream::Stream;
use futures::sync::mpsc::unbounded;
use futures::sync::mpsc::UnboundedSender;
use futures::sync::mpsc::UnboundedReceiver;
use futures::sync::oneshot;

use tokio_core::reactor;

use tls_api::TlsConnector;
use tls_api::TlsConnectorBuilder;
use tls_api_stub;

use futures_misc::*;

use error;
use error::Error;
use result::Result;

use solicit::header::*;
use solicit::HttpScheme;
use solicit::StreamId;

use solicit_async::*;

use client_conn::*;
use client_conf::*;
use common::*;
use stream_part::*;
use service::Service;

pub use client_tls::ClientTlsOption;

// Data sent from event loop to Http2Client
struct LoopToClient {
    controller_tx: UnboundedSender<ControllerCommand>,
}

pub struct Client {
    loop_to_client: LoopToClient,
    thread_join_handle: Option<thread::JoinHandle<()>>,
    http_scheme: HttpScheme,
    // used only once to send shutdown signal
    shutdown: ShutdownSignal,
}

impl Client {

    pub fn new_plain(host: &str, port: u16, conf: ClientConf) -> Result<Client> {
        // TODO: sync
        // TODO: try connect to all addrs
        let socket_addr = (host, port).to_socket_addrs()?.next().expect("resolve host/port");

        let tls_enabled: ClientTlsOption<tls_api_stub::TlsConnector> = ClientTlsOption::Plain;

        Client::new_expl(&socket_addr, tls_enabled, conf)
    }

    pub fn new_tls<C : TlsConnector>(host: &str, port: u16, conf: ClientConf) -> Result<Client> {
        // TODO: sync
        // TODO: try connect to all addrs
        let socket_addr = (host, port).to_socket_addrs()?.next().expect("resolve host/port");

        let tls_enabled = {
            let mut tls_connector = C::builder()?;

            if C::supports_alpn() {
                // TODO: check negotiated protocol after connect
                tls_connector.set_alpn_protocols(&[b"h2"])?;
            }

            let tls_connector = tls_connector.build()?;

            let tls_connector = Arc::new(tls_connector);
            ClientTlsOption::Tls(host.to_owned(), tls_connector)
        };

        Client::new_expl(&socket_addr, tls_enabled, conf)
    }

    pub fn new_expl<C : TlsConnector>(addr: &SocketAddr, tls: ClientTlsOption<C>, conf: ClientConf) -> Result<Client> {
        // We need some data back from event loop.
        // This channel is used to exchange that data
        let (get_from_loop_tx, get_from_loop_rx) = mpsc::channel();

        let addr = addr.clone();
        let http_scheme = tls.http_scheme();

        // Create a channel to receive shutdown signal.
        let (shutdown_signal, shutdown_future) = shutdown_signal();

        // Start event loop.
        let join_handle = thread::Builder::new()
            .name(conf.thread_name.clone().unwrap_or_else(|| "http2-client-loop".to_owned()).to_string())
            .spawn(move || {
                run_client_event_loop(shutdown_future, addr, tls, conf, get_from_loop_tx);
            })
            .expect("spawn");

        // Get back call channel and shutdown channel.
        let loop_to_client = get_from_loop_rx.recv()
            .map_err(|_| Error::IoError(io::Error::new(io::ErrorKind::Other, "get response from loop")))?;

        Ok(Client {
            loop_to_client: loop_to_client,
            thread_join_handle: Some(join_handle),
            http_scheme: http_scheme,
            shutdown: shutdown_signal,
        })
    }

    pub fn start_request_simple(
        &self,
        headers: Headers,
        body: Bytes)
            -> Response
    {
        self.start_request(
            headers,
            HttpPartStream::once_bytes(body))
    }

    pub fn start_get(
        &self,
        path: &str,
        authority: &str)
            -> Response
    {
        let headers = Headers(vec![
            Header::new(":method", "GET"),
            Header::new(":path", path.to_owned()),
            Header::new(":authority", authority.to_owned()),
            Header::new(":scheme", self.http_scheme.as_bytes()),
        ]);
        self.start_request_simple(headers, Bytes::new())
    }

    pub fn start_post(
        &self,
        path: &str,
        authority: &str,
        body: Bytes)
            -> Response
    {
        let headers = Headers(vec![
            Header::new(":method", "POST"),
            Header::new(":path", path.to_owned()),
            Header::new(":authority", authority.to_owned()),
            Header::new(":scheme", self.http_scheme.as_bytes()),
        ]);
        self.start_request_simple(headers, body)
    }

    pub fn dump_state(&self) -> HttpFutureSend<ConnectionStateSnapshot> {
        let (tx, rx) = oneshot::channel();
        // ignore error
        drop(self.loop_to_client.controller_tx.send(ControllerCommand::DumpState(tx)));
        Box::new(rx.map_err(|_| error::Error::Other("conn died")))
    }

    pub fn wait_for_connect(&self) -> HttpFutureSend<()> {
        let (tx, rx) = oneshot::channel();
        // ignore error
        drop(self.loop_to_client.controller_tx.send(ControllerCommand::WaitForConnect(tx)));
        Box::new(rx.map_err(|_| error::Error::Other("conn died")).and_then(|r| r))
    }
}

impl Service for Client {
    // TODO: copy-paste with ClientConnection::start_request
    fn start_request(
        &self,
        headers: Headers,
        body: HttpPartStream)
            -> Response
    {
        let (resp_tx, resp_rx) = oneshot::channel();

        let start = StartRequestMessage {
            headers: headers,
            body: body,
            resp_tx: resp_tx,
        };

        if let Err(_) = self.loop_to_client.controller_tx.send(ControllerCommand::StartRequest(start)) {
            return Response::err(error::Error::Other("client controller died"));
        }

        let resp_rx = resp_rx.map_err(|oneshot::Canceled| error::Error::Other("client likely died"));

        let resp_rx = resp_rx.map(|r| r.into_stream_flag());

        let resp_rx = resp_rx.flatten_stream();

        Response::from_stream(resp_rx)
    }}

enum ControllerCommand {
    GoAway,
    StartRequest(StartRequestMessage),
    WaitForConnect(oneshot::Sender<Result<()>>),
    DumpState(oneshot::Sender<ConnectionStateSnapshot>),
}

struct ControllerState<C : TlsConnector> {
    handle: reactor::Handle,
    socket_addr: SocketAddr,
    tls: ClientTlsOption<C>,
    conf: ClientConf,
    // current connection
    conn: Arc<ClientConnection>,
    tx: UnboundedSender<ControllerCommand>,
}

impl<C : TlsConnector> ControllerState<C> {
    fn init_conn(&mut self) {
        let (conn, future) = ClientConnection::new(
            self.handle.clone(),
            &self.socket_addr,
            self.tls.clone(),
            self.conf.clone(),
            CallbacksImpl {
                tx: self.tx.clone(),
            });

        self.handle.spawn(future.map_err(|e| { warn!("client error: {:?}", e); () }));

        self.conn = Arc::new(conn);
    }

    fn iter(mut self, cmd: ControllerCommand) -> ControllerState<C> {
        match cmd {
            ControllerCommand::GoAway => {
                self.init_conn();
            },
            ControllerCommand::StartRequest(start) => {
                if let Err(start) = self.conn.start_request_with_resp_sender(start) {
                    self.init_conn();
                    if let Err(start) = self.conn.start_request_with_resp_sender(start) {
                        let err = error::Error::Other("client died and reconnect failed");
                        // ignore error
                        if let Err(_) = start.resp_tx.send(Response::err(err)) {
                            debug!("called likely died");
                        }
                    }
                }
            }
            ControllerCommand::WaitForConnect(tx) => {
                if let Err(tx) = self.conn.wait_for_connect_with_resp_sender(tx) {
                    self.init_conn();
                    if let Err(tx) = self.conn.wait_for_connect_with_resp_sender(tx) {
                        let err = error::Error::Other("client died and reconnect failed");
                        // ignore error
                        drop(tx.send(Err(err)));
                    }
                }
            }
            ControllerCommand::DumpState(tx) => {
                self.conn.dump_state_with_resp_sender(tx);
            }
        }
        self
    }

    fn run(self, rx: UnboundedReceiver<ControllerCommand>)
        -> HttpFuture<()>
    {
        let rx = rx.map_err(|_| error::Error::Other("channel died"));
        let r = rx.fold(self, |state, cmd| {
            Ok::<_, error::Error>(state.iter(cmd))
        });
        let r = r.map(|_| ());
        Box::new(r)
    }
}

struct CallbacksImpl {
    tx: UnboundedSender<ControllerCommand>,
}

impl ClientConnectionCallbacks for CallbacksImpl {
    fn goaway(&self, _stream_id: StreamId, _error_code: u32) {
        drop(self.tx.send(ControllerCommand::GoAway));
    }
}

// Event loop entry point
fn run_client_event_loop<C : TlsConnector>(
    shutdown_future: ShutdownFuture,
    socket_addr: SocketAddr,
    tls: ClientTlsOption<C>,
    conf: ClientConf,
    send_to_back: mpsc::Sender<LoopToClient>)
{
    // Create an event loop.
    let mut lp: reactor::Core = reactor::Core::new().expect("Core::new");

    let (controller_tx, controller_rx) = unbounded();

    let (http_conn, conn_future) =
        ClientConnection::new(lp.handle(), &socket_addr, tls.clone(), conf.clone(), CallbacksImpl {
            tx: controller_tx.clone(),
        });

    lp.handle().spawn(conn_future.map_err(|e| { warn!("client error: {:?}", e); () }));

    let init = ControllerState {
        handle: lp.handle(),
        socket_addr: socket_addr.clone(),
        tls: tls,
        conf: conf,
        conn: Arc::new(http_conn),
        tx: controller_tx.clone(),
    };

    let controller_future = init.run(controller_rx);

    // Send channels back to Http2Client
    send_to_back
        .send(LoopToClient {
            controller_tx: controller_tx,
        })
        .expect("send back");

    let shutdown_future = shutdown_future
        .then(move |_| {
            // Must complete with error,
            // so `join` with this future cancels another future.
            future::failed::<(), _>(Error::Shutdown)
        });

    // Wait for either completion of connection (i. e. error)
    // or shutdown signal.
    let done = controller_future.join(shutdown_future);

    match lp.run(done) {
        Ok(_) => {}
        Err(Error::Shutdown) => {}
        Err(e) => {
            error!("Core::run failed: {:?}", e);
        }
    }
}

// We shutdown the client in the destructor.
impl Drop for Client {
    fn drop(&mut self) {
        self.shutdown.shutdown();

        // do not ignore errors because we own event loop thread
        self.thread_join_handle.take().expect("handle.take")
            .join().expect("join thread");
    }
}
<|endoftext|>


<commit_before>#![warn(bad_style,
        unused, unused_extern_crates, unused_import_braces,
        unused_qualifications, unused_results)]

extern crate getopts;
extern crate num;
extern crate reqwest;
extern crate rustc_serialize;
extern crate term;
extern crate time;

use getopts::Options;
use num::Integer;
use rustc_serialize::{Encodable, json};
use std::{env, fmt, io, process};
use std::borrow::Cow;
use std::error::Error;
use std::fs::{self, File};
use std::io::prelude::*;
use std::path::PathBuf;
use term::color;
use term::color::Color;

type OutputPair<'a> = (Option<Color>, Cow<'a, str>);

const NSEC_PER_SEC: u64 = 1000000000;
const NSEC_WARN_LIMIT: u64 = 1 * NSEC_PER_SEC;
const NSEC_NG_LIMIT: u64 = 10 * NSEC_PER_SEC;

const COLOR_OK: Color = color::GREEN;
const COLOR_NG: Color = color::RED;
const COLOR_WARN: Color = color::YELLOW;

#[derive(Debug)]
pub enum SolverError {
    Io(io::Error),
    Reqwest(reqwest::Error),
    InvalidHttpStatus(reqwest::StatusCode, Vec<u8>),
}

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

impl Error for SolverError {
    fn description(&self) -> &str {
        match *self {
            SolverError::Io(ref err) => err.description(),
            SolverError::Reqwest(ref err) => err.description(),
            SolverError::InvalidHttpStatus(ref status, _) => {
                status.canonical_reason().unwrap_or("unknown")
            }
        }
    }

    fn cause(&self) -> Option<&Error> {
        match *self {
            SolverError::Io(ref err) => Some(err),
            SolverError::Reqwest(ref err) => Some(err),
            SolverError::InvalidHttpStatus(..) => None,
        }
    }
}

impl fmt::Display for SolverError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            SolverError::Io(ref err) => write!(f, "{}", err),
            SolverError::Reqwest(ref err) => write!(f, "{}", err),
            SolverError::InvalidHttpStatus(ref status, ref body) => {
                write!(f, "{}:{}", status, String::from_utf8_lossy(body))
            }
        }
    }
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
pub struct SolverResult<T> {
    pub time: u64,
    pub answer: T,
    pub is_ok: bool,
}

impl<T: Encodable> SolverResult<T> {
    pub fn print_json<W: Write>(&self, out: &mut W) -> io::Result<()> {
        writeln!(out, "{}", json::encode(self).unwrap())
    }
}

fn print_items(items: &[OutputPair]) {
    match term::stdout() {
        None => {
            let mut out = io::stdout();
            for &(_, ref s) in items {
                let _ = write!(&mut out, "{}", s);
            }
            let _ = out.flush();
        }
        Some(mut t) => {
            for &(c, ref s) in items {
                match c {
                    Some(c) => {
                        let _ = t.fg(c);
                        let _ = write!(&mut t, "{}", s);
                        let _ = t.reset();
                    }
                    None => {
                        let _ = write!(&mut t, "{}", s);
                    }
                }
            }
            let _ = t.flush();
        }
    }
}

impl<T: fmt::Display> SolverResult<T> {
    pub fn print_pretty(&self, name: &str, enable_time_color: bool) -> io::Result<()> {
        let mut items = vec![];
        if self.is_ok {
            items.push(normal(format!("{} ", name)));
        } else {
            items.push(normal(format!("{} ", name)));
        }

        items.push(normal("["));
        if self.is_ok {
            items.push(ok("OK"));
        } else {
            items.push(ng("NG"));
        }
        items.push(normal("] "));

        let (sec, nsec) = self.time.div_rem(&NSEC_PER_SEC);
        let time_str = format!("{:3}.{:09} ", sec, nsec);
        if !enable_time_color || self.time < NSEC_WARN_LIMIT {
            items.push(normal(time_str));
        } else if self.time < NSEC_NG_LIMIT {
            items.push(warn(time_str));
        } else {
            items.push(ng(time_str));
        }

        items.push(normal(format!("{} ", self.answer)));

        items.push(normal("\n"));
        print_items(&items);

        fn normal<'a, T: Into<Cow<'a, str>>>(s: T) -> OutputPair<'a> {
            (None, s.into())
        }
        fn ok<'a, T: Into<Cow<'a, str>>>(s: T) -> OutputPair<'a> {
            (Some(COLOR_OK), s.into())
        }
        fn warn<'a, T: Into<Cow<'a, str>>>(s: T) -> OutputPair<'a> {
            (Some(COLOR_WARN), s.into())
        }
        fn ng<'a, T: Into<Cow<'a, str>>>(s: T) -> OutputPair<'a> {
            (Some(COLOR_NG), s.into())
        }

        Ok(())
    }
}

enum SolverFn<'a> {
    FnOnly(fn() -> String),
    FnWithFile(&'a str, fn(File) -> io::Result<String>),
}

pub struct Solver<'a> {
    answer: &'a str,
    solver: SolverFn<'a>,
}

impl<'a> Solver<'a> {
    pub fn new(answer: &'a str, solver: fn() -> String) -> Solver<'a> {
        Solver {
            answer: answer,
            solver: SolverFn::FnOnly(solver),
        }
    }

    pub fn new_with_file(answer: &'a str,
                         file_name: &'a str,
                         solver: fn(File) -> io::Result<String>)
                         -> Solver<'a> {
        Solver {
            answer: answer,
            solver: SolverFn::FnWithFile(file_name, solver),
        }
    }

    pub fn run(self) {
        let args = env::args().collect::<Vec<_>>();
        let program = &args[0];

        let mut opts = Options::new();
        let _ = opts.optflag("", "json", "Output JSON format");
        let _ = opts.optflag("h", "help", "Display this message");

        let matches = match opts.parse(&args[1..]) {
            Ok(m) => m,
            Err(f) => {
                let _ = writeln!(&mut io::stderr(), "{}: {}", program, f);
                process::exit(255);
            }
        };

        if matches.opt_present("h") {
            let short = opts.short_usage(&program);
            println!("{}", opts.usage(&short));
            return;
        }

        match self.solve() {
            Err(err) => {
                let _ = writeln!(&mut io::stderr(), "{}: {}", program, err);
                process::exit(255);
            }
            Ok(result) => {
                if matches.opt_present("json") {
                    let _ = result.print_json(&mut io::stdout());
                } else {
                    let _ = result.print_pretty(&program, true);
                }
                if !result.is_ok {
                    process::exit(1);
                }
            }
        }
    }

    pub fn solve(&self) -> Result<SolverResult<String>, SolverError> {
        let (time, answer) = match self.solver {
            SolverFn::FnOnly(fun) => bench(move || fun()),
            SolverFn::FnWithFile(file_name, fun) => {
                let file = try!(setup_file(file_name));
                let (time, answer) = bench(move || fun(file));
                (time, try!(answer))
            }
        };

        let result = SolverResult {
            is_ok: answer == self.answer,
            time: time,
            answer: answer,
        };
        Ok(result)
    }
}

fn bench<T, F: FnOnce() -> T>(f: F) -> (u64, T) {
    let start_time = time::precise_time_ns();
    let result = f();
    let end_time = time::precise_time_ns();
    let nsec = end_time - start_time;
    (nsec, result)
}

fn setup_file(file_name: &str) -> Result<File, SolverError> {
    let mut path = PathBuf::from("./.cache");
    path.push(file_name);
    if !path.is_file() {
        try!(fs::create_dir_all(&path.parent().unwrap()));
        let mut file = try!(File::create(&path));
        let content = try!(download(file_name));
        try!(file.write_all(&content));
    }

    let file = try!(File::open(&path));
    Ok(file)
}

const BASE_URL: &'static str = "https://projecteuler.net/project/resources/";
fn download(file_name: &str) -> Result<Vec<u8>, SolverError> {
    let url = format!("{}{}", BASE_URL, file_name);

    let mut retry = 0;
    loop {
        let mut resp = try!(reqwest::get(&url));
        let mut body = vec![];
        let _ = try!(resp.read_to_end(&mut body));

        if !resp.status().is_success() {
            let err = SolverError::InvalidHttpStatus(*resp.status(), body.clone());
            let program = env::args().next().unwrap();
            let _ = writeln!(&mut io::stderr(), "{}: {}", program, err);
            retry += 1;
            if retry >= 3 {
                return Err(err);
            }
        }

        return Ok(body);
    }
}

#[macro_export]
macro_rules! problem {
    ($answer:expr, $solver:expr) => (
        #[cfg(not(test))]
        fn main() {
            $crate::Solver::new($answer, $solver).run();
        }

        #[test]
        fn test_solve() {
            assert!($crate::Solver::new($answer, $solver).solve().unwrap().is_ok);
        }
    );
    ($answer:expr, $file:expr, $solver:expr) => (
        #[cfg(not(test))]
        fn main() {
            $crate::Solver::new_with_file($answer, $file, $solver).run();
        }

        #[test]
        fn test_solve() {
            assert!($crate::Solver::new_with_file($answer, $file, $solver).solve().unwrap().is_ok);
        }
     );
}
<commit_msg>Download from HTTP URL<commit_after>#![warn(bad_style,
        unused, unused_extern_crates, unused_import_braces,
        unused_qualifications, unused_results)]

extern crate getopts;
extern crate num;
extern crate reqwest;
extern crate rustc_serialize;
extern crate term;
extern crate time;

use getopts::Options;
use num::Integer;
use rustc_serialize::{Encodable, json};
use std::{env, fmt, io, process};
use std::borrow::Cow;
use std::error::Error;
use std::fs::{self, File};
use std::io::prelude::*;
use std::path::PathBuf;
use term::color;
use term::color::Color;

type OutputPair<'a> = (Option<Color>, Cow<'a, str>);

const NSEC_PER_SEC: u64 = 1000000000;
const NSEC_WARN_LIMIT: u64 = 1 * NSEC_PER_SEC;
const NSEC_NG_LIMIT: u64 = 10 * NSEC_PER_SEC;

const COLOR_OK: Color = color::GREEN;
const COLOR_NG: Color = color::RED;
const COLOR_WARN: Color = color::YELLOW;

#[derive(Debug)]
pub enum SolverError {
    Io(io::Error),
    Reqwest(reqwest::Error),
    InvalidHttpStatus(reqwest::StatusCode, Vec<u8>),
}

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

impl Error for SolverError {
    fn description(&self) -> &str {
        match *self {
            SolverError::Io(ref err) => err.description(),
            SolverError::Reqwest(ref err) => err.description(),
            SolverError::InvalidHttpStatus(ref status, _) => {
                status.canonical_reason().unwrap_or("unknown")
            }
        }
    }

    fn cause(&self) -> Option<&Error> {
        match *self {
            SolverError::Io(ref err) => Some(err),
            SolverError::Reqwest(ref err) => Some(err),
            SolverError::InvalidHttpStatus(..) => None,
        }
    }
}

impl fmt::Display for SolverError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            SolverError::Io(ref err) => write!(f, "{}", err),
            SolverError::Reqwest(ref err) => write!(f, "{}", err),
            SolverError::InvalidHttpStatus(ref status, ref body) => {
                write!(f, "{}:{}", status, String::from_utf8_lossy(body))
            }
        }
    }
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
pub struct SolverResult<T> {
    pub time: u64,
    pub answer: T,
    pub is_ok: bool,
}

impl<T: Encodable> SolverResult<T> {
    pub fn print_json<W: Write>(&self, out: &mut W) -> io::Result<()> {
        writeln!(out, "{}", json::encode(self).unwrap())
    }
}

fn print_items(items: &[OutputPair]) {
    match term::stdout() {
        None => {
            let mut out = io::stdout();
            for &(_, ref s) in items {
                let _ = write!(&mut out, "{}", s);
            }
            let _ = out.flush();
        }
        Some(mut t) => {
            for &(c, ref s) in items {
                match c {
                    Some(c) => {
                        let _ = t.fg(c);
                        let _ = write!(&mut t, "{}", s);
                        let _ = t.reset();
                    }
                    None => {
                        let _ = write!(&mut t, "{}", s);
                    }
                }
            }
            let _ = t.flush();
        }
    }
}

impl<T: fmt::Display> SolverResult<T> {
    pub fn print_pretty(&self, name: &str, enable_time_color: bool) -> io::Result<()> {
        let mut items = vec![];
        if self.is_ok {
            items.push(normal(format!("{} ", name)));
        } else {
            items.push(normal(format!("{} ", name)));
        }

        items.push(normal("["));
        if self.is_ok {
            items.push(ok("OK"));
        } else {
            items.push(ng("NG"));
        }
        items.push(normal("] "));

        let (sec, nsec) = self.time.div_rem(&NSEC_PER_SEC);
        let time_str = format!("{:3}.{:09} ", sec, nsec);
        if !enable_time_color || self.time < NSEC_WARN_LIMIT {
            items.push(normal(time_str));
        } else if self.time < NSEC_NG_LIMIT {
            items.push(warn(time_str));
        } else {
            items.push(ng(time_str));
        }

        items.push(normal(format!("{} ", self.answer)));

        items.push(normal("\n"));
        print_items(&items);

        fn normal<'a, T: Into<Cow<'a, str>>>(s: T) -> OutputPair<'a> {
            (None, s.into())
        }
        fn ok<'a, T: Into<Cow<'a, str>>>(s: T) -> OutputPair<'a> {
            (Some(COLOR_OK), s.into())
        }
        fn warn<'a, T: Into<Cow<'a, str>>>(s: T) -> OutputPair<'a> {
            (Some(COLOR_WARN), s.into())
        }
        fn ng<'a, T: Into<Cow<'a, str>>>(s: T) -> OutputPair<'a> {
            (Some(COLOR_NG), s.into())
        }

        Ok(())
    }
}

enum SolverFn<'a> {
    FnOnly(fn() -> String),
    FnWithFile(&'a str, fn(File) -> io::Result<String>),
}

pub struct Solver<'a> {
    answer: &'a str,
    solver: SolverFn<'a>,
}

impl<'a> Solver<'a> {
    pub fn new(answer: &'a str, solver: fn() -> String) -> Solver<'a> {
        Solver {
            answer: answer,
            solver: SolverFn::FnOnly(solver),
        }
    }

    pub fn new_with_file(answer: &'a str,
                         file_name: &'a str,
                         solver: fn(File) -> io::Result<String>)
                         -> Solver<'a> {
        Solver {
            answer: answer,
            solver: SolverFn::FnWithFile(file_name, solver),
        }
    }

    pub fn run(self) {
        let args = env::args().collect::<Vec<_>>();
        let program = &args[0];

        let mut opts = Options::new();
        let _ = opts.optflag("", "json", "Output JSON format");
        let _ = opts.optflag("h", "help", "Display this message");

        let matches = match opts.parse(&args[1..]) {
            Ok(m) => m,
            Err(f) => {
                let _ = writeln!(&mut io::stderr(), "{}: {}", program, f);
                process::exit(255);
            }
        };

        if matches.opt_present("h") {
            let short = opts.short_usage(&program);
            println!("{}", opts.usage(&short));
            return;
        }

        match self.solve() {
            Err(err) => {
                let _ = writeln!(&mut io::stderr(), "{}: {}", program, err);
                process::exit(255);
            }
            Ok(result) => {
                if matches.opt_present("json") {
                    let _ = result.print_json(&mut io::stdout());
                } else {
                    let _ = result.print_pretty(&program, true);
                }
                if !result.is_ok {
                    process::exit(1);
                }
            }
        }
    }

    pub fn solve(&self) -> Result<SolverResult<String>, SolverError> {
        let (time, answer) = match self.solver {
            SolverFn::FnOnly(fun) => bench(move || fun()),
            SolverFn::FnWithFile(file_name, fun) => {
                let file = try!(setup_file(file_name));
                let (time, answer) = bench(move || fun(file));
                (time, try!(answer))
            }
        };

        let result = SolverResult {
            is_ok: answer == self.answer,
            time: time,
            answer: answer,
        };
        Ok(result)
    }
}

fn bench<T, F: FnOnce() -> T>(f: F) -> (u64, T) {
    let start_time = time::precise_time_ns();
    let result = f();
    let end_time = time::precise_time_ns();
    let nsec = end_time - start_time;
    (nsec, result)
}

fn setup_file(file_name: &str) -> Result<File, SolverError> {
    let mut path = PathBuf::from("./.cache");
    path.push(file_name);
    if !path.is_file() {
        try!(fs::create_dir_all(&path.parent().unwrap()));
        let mut file = try!(File::create(&path));
        let content = try!(download(file_name));
        try!(file.write_all(&content));
    }

    let file = try!(File::open(&path));
    Ok(file)
}

const BASE_URL: &'static str = "http://projecteuler.net/project/resources/";
fn download(file_name: &str) -> Result<Vec<u8>, SolverError> {
    let url = format!("{}{}", BASE_URL, file_name);

    let mut retry = 0;
    loop {
        let mut resp = try!(reqwest::get(&url));
        let mut body = vec![];
        let _ = try!(resp.read_to_end(&mut body));

        if !resp.status().is_success() {
            let err = SolverError::InvalidHttpStatus(*resp.status(), body.clone());
            let program = env::args().next().unwrap();
            let _ = writeln!(&mut io::stderr(), "{}: {}", program, err);
            retry += 1;
            if retry >= 3 {
                return Err(err);
            }
        }

        return Ok(body);
    }
}

#[macro_export]
macro_rules! problem {
    ($answer:expr, $solver:expr) => (
        #[cfg(not(test))]
        fn main() {
            $crate::Solver::new($answer, $solver).run();
        }

        #[test]
        fn test_solve() {
            assert!($crate::Solver::new($answer, $solver).solve().unwrap().is_ok);
        }
    );
    ($answer:expr, $file:expr, $solver:expr) => (
        #[cfg(not(test))]
        fn main() {
            $crate::Solver::new_with_file($answer, $file, $solver).run();
        }

        #[test]
        fn test_solve() {
            assert!($crate::Solver::new_with_file($answer, $file, $solver).solve().unwrap().is_ok);
        }
     );
}
<|endoftext|>


<commit_before>use csv;
use error::BResult;
use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
use std::cmp::min;
use fintime::Date;

#[derive(Debug, RustcEncodable, PartialEq)]
pub enum TransactionType {
    Credit,
    Debit,
}

impl Decodable for TransactionType {
    fn decode<D: Decoder>(d: &mut D) -> Result<Self, D::Error> {
        match try!(d.read_str()).as_ref() {
            "debit" => Ok(TransactionType::Debit),
            "credit" => Ok(TransactionType::Credit),
            s => Err(d.error(&format!("'{}' is not one of debit or credit", s))),
        }
    }
}

pub trait Genericize {
    fn genericize(self) -> Transaction;
}

#[derive(Debug, RustcEncodable)]
pub enum Person {
    Barry,
    Zach,
}

#[derive(Debug, RustcEncodable)]
pub struct Transaction {
    pub date: Date,
    pub person: Person,
    pub description: String,
    pub original_description: String,
    pub amount: f64,
    pub transaction_type: TransactionType,
    pub category: String,
    pub original_category: String,
    pub account_name: String,
    pub labels: String,
    pub notes: String,
}

pub struct Transactions {
    pub transactions: Vec<Transaction>,
}

impl Transactions {
    pub fn new() -> Transactions {
        Transactions { transactions: Vec::new() }
    }

    pub fn load_records<ExportType>(&mut self, filename: &str) -> BResult<i32>
        where ExportType: Genericize + Decodable
    {
        let mut count = 0;
        for record in try!(csv::Reader::from_file(filename)).decode() {
            let record: ExportType = try!(record);
            self.transactions.push(record.genericize());
            count += 1;
        }
        Ok(count)
    }

    pub fn collate(&mut self) {
        self.transactions.sort_by(|a, b| a.date.cmp(&b.date));

        let mut to_delete = Vec::new();
        for (i, t) in self.transactions.iter().enumerate() {
            for j in i..min(self.transactions.len(), i + 50) {
                let ref tn = self.transactions[j];
                if tn.amount == t.amount && tn.transaction_type != t.transaction_type {
                    to_delete.push(i);
                    to_delete.push(j);
                }
            }
        }

        to_delete.sort();
        to_delete.reverse();

        for i in to_delete {
            self.transactions.remove(i);
        }
    }

    pub fn iter(&self) -> ::std::slice::Iter<Transaction> {
        self.transactions.iter()
    }

    pub fn date_of_last_transaction(&self) -> Option<Date> {
        self.transactions.last().map(|t| t.date)
    }
}
<commit_msg>Don't double delete transactions<commit_after>use csv;
use error::BResult;
use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
use std::cmp::min;
use fintime::Date;
use std::collections::HashSet;

#[derive(Debug, RustcEncodable, PartialEq)]
pub enum TransactionType {
    Credit,
    Debit,
}

impl Decodable for TransactionType {
    fn decode<D: Decoder>(d: &mut D) -> Result<Self, D::Error> {
        match try!(d.read_str()).as_ref() {
            "debit" => Ok(TransactionType::Debit),
            "credit" => Ok(TransactionType::Credit),
            s => Err(d.error(&format!("'{}' is not one of debit or credit", s))),
        }
    }
}

pub trait Genericize {
    fn genericize(self) -> Transaction;
}

#[derive(Debug, RustcEncodable)]
pub enum Person {
    Barry,
    Zach,
}

#[derive(Debug, RustcEncodable)]
pub struct Transaction {
    pub date: Date,
    pub person: Person,
    pub description: String,
    pub original_description: String,
    pub amount: f64,
    pub transaction_type: TransactionType,
    pub category: String,
    pub original_category: String,
    pub account_name: String,
    pub labels: String,
    pub notes: String,
}

pub struct Transactions {
    pub transactions: Vec<Transaction>,
}

impl Transactions {
    pub fn new() -> Transactions {
        Transactions { transactions: Vec::new() }
    }

    pub fn load_records<ExportType>(&mut self, filename: &str) -> BResult<i32>
        where ExportType: Genericize + Decodable
    {
        let mut count = 0;
        for record in try!(csv::Reader::from_file(filename)).decode() {
            let record: ExportType = try!(record);
            self.transactions.push(record.genericize());
            count += 1;
        }
        Ok(count)
    }

    pub fn collate(&mut self) {
        self.transactions.sort_by(|a, b| a.date.cmp(&b.date));

        let mut to_delete = HashSet::new();
        for (i, t) in self.transactions.iter().enumerate() {
            for j in i..min(self.transactions.len(), i + 50) {
                let ref tn = self.transactions[j];
                if tn.amount == t.amount && tn.transaction_type != t.transaction_type &&
                   !to_delete.contains(&i) && !to_delete.contains(&j) {
                    to_delete.insert(i);
                    to_delete.insert(j);
                }
            }
        }

        let mut to_delete: Vec<_> = to_delete.into_iter().collect();
        to_delete.sort();
        to_delete.reverse();

        for i in to_delete {
            self.transactions.remove(i);
        }
    }

    pub fn iter(&self) -> ::std::slice::Iter<Transaction> {
        self.transactions.iter()
    }

    pub fn date_of_last_transaction(&self) -> Option<Date> {
        self.transactions.last().map(|t| t.date)
    }
}
<|endoftext|>


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

// ignore-windows failing on win32 bot
// ignore-freebsd: output doesn't match
// ignore-tidy-linelength
// ignore-lldb
// ignore-android: FIXME(#10381)
// compile-flags:-g

// This test uses some GDB Python API features (e.g. accessing anonymous fields)
// which are only available in newer GDB version. The following directive will
// case the test runner to ignore this test if an older GDB version is used:
// min-gdb-version 7.7

// gdb-command: run

// gdb-command: print regular_struct
// gdb-check:$1 = RegularStruct = {the_first_field = 101, the_second_field = 102.5, the_third_field = false, the_fourth_field = "I'm so pretty, oh so pretty..."}

// gdb-command: print tuple
// gdb-check:$2 = {true, 103, "blub"}

// gdb-command: print tuple_struct
// gdb-check:$3 = TupleStruct = {-104.5, 105}

// gdb-command: print empty_struct
// gdb-check:$4 = EmptyStruct

// gdb-command: print c_style_enum1
// gdb-check:$5 = CStyleEnumVar1

// gdb-command: print c_style_enum2
// gdb-check:$6 = CStyleEnumVar2

// gdb-command: print c_style_enum3
// gdb-check:$7 = CStyleEnumVar3

// gdb-command: print mixed_enum_c_style_var
// gdb-check:$8 = MixedEnumCStyleVar

// gdb-command: print mixed_enum_tuple_var
// gdb-check:$9 = MixedEnumTupleVar = {106, 107, false}

// gdb-command: print mixed_enum_struct_var
// gdb-check:$10 = MixedEnumStructVar = {field1 = 108.5, field2 = 109}

// gdb-command: print some
// gdb-check:$11 = Some = {110}

// gdb-command: print none
// gdb-check:$12 = None

// gdb-command: print some_fat
// gdb-check:$13 = Some = {"abc"}

// gdb-command: print none_fat
// gdb-check:$14 = None

// gdb-command: print nested_variant1
// gdb-check:$15 = NestedVariant1 = {NestedStruct = {regular_struct = RegularStruct = {the_first_field = 111, the_second_field = 112.5, the_third_field = true, the_fourth_field = "NestedStructString1"}, tuple_struct = TupleStruct = {113.5, 114}, empty_struct = EmptyStruct, c_style_enum = CStyleEnumVar2, mixed_enum = MixedEnumTupleVar = {115, 116, false}}}

// gdb-command: print nested_variant2
// gdb-check:$16 = NestedVariant2 = {abc = NestedStruct = {regular_struct = RegularStruct = {the_first_field = 117, the_second_field = 118.5, the_third_field = false, the_fourth_field = "NestedStructString10"}, tuple_struct = TupleStruct = {119.5, 120}, empty_struct = EmptyStruct, c_style_enum = CStyleEnumVar3, mixed_enum = MixedEnumStructVar = {field1 = 121.5, field2 = -122}}}

// gdb-command: print none_check1
// gdb-check:$17 = None

// gdb-command: print none_check2
// gdb-check:$18 = None

#![allow(dead_code, unused_variables)]

use self::CStyleEnum::{CStyleEnumVar1, CStyleEnumVar2, CStyleEnumVar3};
use self::MixedEnum::{MixedEnumCStyleVar, MixedEnumTupleVar, MixedEnumStructVar};
use self::NestedEnum::{NestedVariant1, NestedVariant2};

struct RegularStruct {
    the_first_field: isize,
    the_second_field: f64,
    the_third_field: bool,
    the_fourth_field: &'static str,
}

struct TupleStruct(f64, i16);

struct EmptyStruct;

enum CStyleEnum {
    CStyleEnumVar1,
    CStyleEnumVar2,
    CStyleEnumVar3,
}

enum MixedEnum {
    MixedEnumCStyleVar,
    MixedEnumTupleVar(u32, u16, bool),
    MixedEnumStructVar { field1: f64, field2: i32 }
}

struct NestedStruct {
    regular_struct: RegularStruct,
    tuple_struct: TupleStruct,
    empty_struct: EmptyStruct,
    c_style_enum: CStyleEnum,
    mixed_enum: MixedEnum,
}

enum NestedEnum {
    NestedVariant1(NestedStruct),
    NestedVariant2 { abc: NestedStruct }
}

fn main() {

    let regular_struct = RegularStruct {
        the_first_field: 101,
        the_second_field: 102.5,
        the_third_field: false,
        the_fourth_field: "I'm so pretty, oh so pretty..."
    };

    let tuple = ( true, 103u32, "blub" );

    let tuple_struct = TupleStruct(-104.5, 105);

    let empty_struct = EmptyStruct;

    let c_style_enum1 = CStyleEnumVar1;
    let c_style_enum2 = CStyleEnumVar2;
    let c_style_enum3 = CStyleEnumVar3;

    let mixed_enum_c_style_var = MixedEnumCStyleVar;
    let mixed_enum_tuple_var = MixedEnumTupleVar(106, 107, false);
    let mixed_enum_struct_var = MixedEnumStructVar { field1: 108.5, field2: 109 };

    let some = Some(110_usize);
    let none: Option<isize> = None;
    let some_fat = Some("abc");
    let none_fat: Option<&'static str> = None;

    let nested_variant1 = NestedVariant1(
        NestedStruct {
            regular_struct: RegularStruct {
                the_first_field: 111,
                the_second_field: 112.5,
                the_third_field: true,
                the_fourth_field: "NestedStructString1",
            },
            tuple_struct: TupleStruct(113.5, 114),
            empty_struct: EmptyStruct,
            c_style_enum: CStyleEnumVar2,
            mixed_enum: MixedEnumTupleVar(115, 116, false)
        }
    );

    let nested_variant2 = NestedVariant2 {
        abc: NestedStruct {
            regular_struct: RegularStruct {
                the_first_field: 117,
                the_second_field: 118.5,
                the_third_field: false,
                the_fourth_field: "NestedStructString10",
            },
            tuple_struct: TupleStruct(119.5, 120),
            empty_struct: EmptyStruct,
            c_style_enum: CStyleEnumVar3,
            mixed_enum: MixedEnumStructVar {
                field1: 121.5,
                field2: -122
            }
        }
    };

    let none_check1: Option<(usize, Vec<usize>)> = None;
    let none_check2: Option<String> = None;

    zzz(); // #break
}

fn zzz() { () }
<commit_msg>Auto merge of #26070 - brson:nightlyfix, r=alexcrichton<commit_after>// Copyright 2013-2014 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.

// ignore-test also broken on nightly linux distcheck. it's just broken!
// ignore-windows failing on win32 bot
// ignore-freebsd: output doesn't match
// ignore-tidy-linelength
// ignore-lldb
// ignore-android: FIXME(#10381)
// compile-flags:-g

// This test uses some GDB Python API features (e.g. accessing anonymous fields)
// which are only available in newer GDB version. The following directive will
// case the test runner to ignore this test if an older GDB version is used:
// min-gdb-version 7.7

// gdb-command: run

// gdb-command: print regular_struct
// gdb-check:$1 = RegularStruct = {the_first_field = 101, the_second_field = 102.5, the_third_field = false, the_fourth_field = "I'm so pretty, oh so pretty..."}

// gdb-command: print tuple
// gdb-check:$2 = {true, 103, "blub"}

// gdb-command: print tuple_struct
// gdb-check:$3 = TupleStruct = {-104.5, 105}

// gdb-command: print empty_struct
// gdb-check:$4 = EmptyStruct

// gdb-command: print c_style_enum1
// gdb-check:$5 = CStyleEnumVar1

// gdb-command: print c_style_enum2
// gdb-check:$6 = CStyleEnumVar2

// gdb-command: print c_style_enum3
// gdb-check:$7 = CStyleEnumVar3

// gdb-command: print mixed_enum_c_style_var
// gdb-check:$8 = MixedEnumCStyleVar

// gdb-command: print mixed_enum_tuple_var
// gdb-check:$9 = MixedEnumTupleVar = {106, 107, false}

// gdb-command: print mixed_enum_struct_var
// gdb-check:$10 = MixedEnumStructVar = {field1 = 108.5, field2 = 109}

// gdb-command: print some
// gdb-check:$11 = Some = {110}

// gdb-command: print none
// gdb-check:$12 = None

// gdb-command: print some_fat
// gdb-check:$13 = Some = {"abc"}

// gdb-command: print none_fat
// gdb-check:$14 = None

// gdb-command: print nested_variant1
// gdb-check:$15 = NestedVariant1 = {NestedStruct = {regular_struct = RegularStruct = {the_first_field = 111, the_second_field = 112.5, the_third_field = true, the_fourth_field = "NestedStructString1"}, tuple_struct = TupleStruct = {113.5, 114}, empty_struct = EmptyStruct, c_style_enum = CStyleEnumVar2, mixed_enum = MixedEnumTupleVar = {115, 116, false}}}

// gdb-command: print nested_variant2
// gdb-check:$16 = NestedVariant2 = {abc = NestedStruct = {regular_struct = RegularStruct = {the_first_field = 117, the_second_field = 118.5, the_third_field = false, the_fourth_field = "NestedStructString10"}, tuple_struct = TupleStruct = {119.5, 120}, empty_struct = EmptyStruct, c_style_enum = CStyleEnumVar3, mixed_enum = MixedEnumStructVar = {field1 = 121.5, field2 = -122}}}

// gdb-command: print none_check1
// gdb-check:$17 = None

// gdb-command: print none_check2
// gdb-check:$18 = None

#![allow(dead_code, unused_variables)]

use self::CStyleEnum::{CStyleEnumVar1, CStyleEnumVar2, CStyleEnumVar3};
use self::MixedEnum::{MixedEnumCStyleVar, MixedEnumTupleVar, MixedEnumStructVar};
use self::NestedEnum::{NestedVariant1, NestedVariant2};

struct RegularStruct {
    the_first_field: isize,
    the_second_field: f64,
    the_third_field: bool,
    the_fourth_field: &'static str,
}

struct TupleStruct(f64, i16);

struct EmptyStruct;

enum CStyleEnum {
    CStyleEnumVar1,
    CStyleEnumVar2,
    CStyleEnumVar3,
}

enum MixedEnum {
    MixedEnumCStyleVar,
    MixedEnumTupleVar(u32, u16, bool),
    MixedEnumStructVar { field1: f64, field2: i32 }
}

struct NestedStruct {
    regular_struct: RegularStruct,
    tuple_struct: TupleStruct,
    empty_struct: EmptyStruct,
    c_style_enum: CStyleEnum,
    mixed_enum: MixedEnum,
}

enum NestedEnum {
    NestedVariant1(NestedStruct),
    NestedVariant2 { abc: NestedStruct }
}

fn main() {

    let regular_struct = RegularStruct {
        the_first_field: 101,
        the_second_field: 102.5,
        the_third_field: false,
        the_fourth_field: "I'm so pretty, oh so pretty..."
    };

    let tuple = ( true, 103u32, "blub" );

    let tuple_struct = TupleStruct(-104.5, 105);

    let empty_struct = EmptyStruct;

    let c_style_enum1 = CStyleEnumVar1;
    let c_style_enum2 = CStyleEnumVar2;
    let c_style_enum3 = CStyleEnumVar3;

    let mixed_enum_c_style_var = MixedEnumCStyleVar;
    let mixed_enum_tuple_var = MixedEnumTupleVar(106, 107, false);
    let mixed_enum_struct_var = MixedEnumStructVar { field1: 108.5, field2: 109 };

    let some = Some(110_usize);
    let none: Option<isize> = None;
    let some_fat = Some("abc");
    let none_fat: Option<&'static str> = None;

    let nested_variant1 = NestedVariant1(
        NestedStruct {
            regular_struct: RegularStruct {
                the_first_field: 111,
                the_second_field: 112.5,
                the_third_field: true,
                the_fourth_field: "NestedStructString1",
            },
            tuple_struct: TupleStruct(113.5, 114),
            empty_struct: EmptyStruct,
            c_style_enum: CStyleEnumVar2,
            mixed_enum: MixedEnumTupleVar(115, 116, false)
        }
    );

    let nested_variant2 = NestedVariant2 {
        abc: NestedStruct {
            regular_struct: RegularStruct {
                the_first_field: 117,
                the_second_field: 118.5,
                the_third_field: false,
                the_fourth_field: "NestedStructString10",
            },
            tuple_struct: TupleStruct(119.5, 120),
            empty_struct: EmptyStruct,
            c_style_enum: CStyleEnumVar3,
            mixed_enum: MixedEnumStructVar {
                field1: 121.5,
                field2: -122
            }
        }
    };

    let none_check1: Option<(usize, Vec<usize>)> = None;
    let none_check2: Option<String> = None;

    zzz(); // #break
}

fn zzz() { () }
<|endoftext|>