<commit_before>use std::path::Path;
use std::path::PathBuf;
use std::process::Command;
use std::env;

fn main() {
    let target = env::var("TARGET").unwrap();

    // Initialize git submodule
    if !Path::new("libqmlbind/.git").exists() {
        let _ = Command::new("git").args(&["submodule", "update", "--init"])
                                   .status();
    }

    Command::new("qmake").current_dir("libqmlbind").output().unwrap();
    Command::new("make").current_dir("libqmlbind").output().unwrap();
    Command::new("make").arg("staticlib").current_dir("libqmlbind/qmlbind").output().unwrap();

    println!("cargo:rustc-flags=-L libqmlbind/qmlbind");
    println!("cargo:rustc-link-lib=static=qmlbind");
    // println!("cargo:rustc-link-lib=qmlbind");

    let qt_dir = env::var("QT_DIR").map(|p| PathBuf::from(p)).unwrap_or({
        println!("Environnement variable 'QT_DIR' not set!");
        println!("Defaulting to ${{HOME}}/Qt/${{QT_VER}}/${{QT_COMP}} where:");
        let home_dir = env::home_dir().map(|p| PathBuf::from(p)).unwrap();
        let default_qt_ver = "5.7".to_string();
        let default_qt_comp = if target.contains("linux") {
            "gcc_64".to_string()
        } else if target.contains("darwin") {
            "clang_64".to_string()
        } else {
            panic!("Unsuported platform in gallery's build.rs!")
        };
        println!("    QT_VER:  {}", default_qt_ver);
        println!("    QT_COMP: {}", default_qt_comp);
        let qt_dir_default = home_dir.join("Qt")
                                  .join(env::var("QT_VER").unwrap_or(default_qt_ver))
                                  .join(env::var("QT_COMP").unwrap_or(default_qt_comp));
        qt_dir_default
    });
    println!("Using Qt directory: {:?}", qt_dir);
    println!("Use QT_DIR environment variable to ovewrite.");

    let qt_lib_dir = qt_dir.join("lib").to_str().unwrap().to_string();

    let osx_framework = if target.contains("darwin") { "=framework" }
                        else  { "" };
    // On Linux, libraries are name "Qt5Core", not "QtCore" as on OSX
    let linux_qt_lib_ver = if target.contains("linux") { "5" }
                           else  { "" };

    println!("cargo:rustc-link-search{}={}", osx_framework, qt_lib_dir);

    println!("cargo:rustc-link-lib{}=Qt{}Core", osx_framework, linux_qt_lib_ver);
    println!("cargo:rustc-link-lib{}=Qt{}Network", osx_framework, linux_qt_lib_ver);
    println!("cargo:rustc-link-lib{}=Qt{}Gui", osx_framework, linux_qt_lib_ver);
    println!("cargo:rustc-link-lib{}=Qt{}Qml", osx_framework, linux_qt_lib_ver);
    println!("cargo:rustc-link-lib{}=Qt{}Widgets", osx_framework, linux_qt_lib_ver);

    // On Linux, Qt needs icui18n, icuuc and icudata
    if target.contains("linux") {
        println!("cargo:rustc-link-lib=icui18n");
        println!("cargo:rustc-link-lib=icuuc");
        println!("cargo:rustc-link-lib=icudata");
    }

    println!("cargo:rustc-flags=-l stdc++");
}
<commit_msg>libqmlbnd-sys: Use 'rustc-link-search' instead of '-L' in build.rs<commit_after>use std::path::Path;
use std::path::PathBuf;
use std::process::Command;
use std::env;

fn main() {
    let target = env::var("TARGET").unwrap();
    // let out_dir = env::var("OUT_DIR").unwrap();
    let current_dir = env::current_dir().unwrap();

    // println!("target:      {:?}", target);
    // println!("out_dir:     {:?}", out_dir);
    // println!("current_dir: {:?}", current_dir);

    let libqmlbind_dir = current_dir.join("libqmlbind");
    let libqmlbind_build_dir = libqmlbind_dir.join("qmlbind");

    // Initialize git submodule
    if !libqmlbind_dir.join(".git").exists() {
        let _ = Command::new("git").args(&["submodule", "update", "--init"])
                                   .status();
    }

    Command::new("qmake").current_dir(&libqmlbind_dir).output().unwrap();
    Command::new("make").current_dir(&libqmlbind_dir).output().unwrap();
    Command::new("make").arg("staticlib").current_dir(&libqmlbind_build_dir).output().unwrap();

    println!("cargo:rustc-link-search={}", libqmlbind_build_dir.to_str().unwrap());
    println!("cargo:rustc-link-lib=static=qmlbind");

    let qt_dir = env::var("QT_DIR").map(|p| PathBuf::from(p)).unwrap_or({
        println!("Environnement variable 'QT_DIR' not set!");
        println!("Defaulting to ${{HOME}}/Qt/${{QT_VER}}/${{QT_COMP}} where:");
        let home_dir = env::home_dir().map(|p| PathBuf::from(p)).unwrap();
        let default_qt_ver = "5.7".to_string();
        let default_qt_comp = if target.contains("linux") {
            "gcc_64".to_string()
        } else if target.contains("darwin") {
            "clang_64".to_string()
        } else {
            panic!("Unsuported platform in gallery's build.rs!")
        };
        println!("    QT_VER:  {}", default_qt_ver);
        println!("    QT_COMP: {}", default_qt_comp);
        let qt_dir_default = home_dir.join("Qt")
                                  .join(env::var("QT_VER").unwrap_or(default_qt_ver))
                                  .join(env::var("QT_COMP").unwrap_or(default_qt_comp));
        qt_dir_default
    });
    println!("Using Qt directory: {:?}", qt_dir);
    println!("Use QT_DIR environment variable to ovewrite.");

    let qt_lib_dir = qt_dir.join("lib").to_str().unwrap().to_string();

    let osx_framework = if target.contains("darwin") { "=framework" }
                        else  { "" };
    // On Linux, libraries are name "Qt5Core", not "QtCore" as on OSX
    let linux_qt_lib_ver = if target.contains("linux") { "5" }
                           else  { "" };

    println!("cargo:rustc-link-search{}={}", osx_framework, qt_lib_dir);

    println!("cargo:rustc-link-lib{}=Qt{}Core", osx_framework, linux_qt_lib_ver);
    println!("cargo:rustc-link-lib{}=Qt{}Network", osx_framework, linux_qt_lib_ver);
    println!("cargo:rustc-link-lib{}=Qt{}Gui", osx_framework, linux_qt_lib_ver);
    println!("cargo:rustc-link-lib{}=Qt{}Qml", osx_framework, linux_qt_lib_ver);
    println!("cargo:rustc-link-lib{}=Qt{}Widgets", osx_framework, linux_qt_lib_ver);

    // On Linux, Qt needs icui18n, icuuc and icudata
    if target.contains("linux") {
        println!("cargo:rustc-link-lib=icui18n");
        println!("cargo:rustc-link-lib=icuuc");
        println!("cargo:rustc-link-lib=icudata");
    }

    println!("cargo:rustc-flags=-l stdc++");
}
<|endoftext|>


<commit_before>//! Proxy engine
//!
//! This module is intended for processing incoming requests from clients,
//! handling occurred errors during a work, communicating with a message
//! broker and preparing appropriate responses in the certain format.
//!

use std::collections::HashMap;
use std::str::from_utf8;
use std::sync::Arc;
use std::vec::Vec;

use futures::future::{lazy, Future};
use futures::sync::mpsc;
use futures::Stream;
use json::parse as json_parse;
use lapin_futures_rustls::lapin::channel::{
    BasicConsumeOptions, BasicProperties, BasicPublishOptions, QueueBindOptions,
    QueueDeclareOptions, QueueDeleteOptions, QueueUnbindOptions,
};
use lapin_futures_rustls::lapin::types::{AMQPValue, FieldTable};
use tungstenite::Message;
use uuid::Uuid;

use super::super::cli::CliOptions;
use super::super::config::get_config;
use super::super::error::{Result, PathfinderError};
use super::super::rabbitmq::RabbitMQClient;
use engine::middleware::{EmptyMiddleware, JwtTokenMiddleware, Middleware};
use engine::router::{extract_endpoints, ReadOnlyEndpoint, Router};
use engine::options::RpcOptions;
use engine::serializer::{JsonMessage, Serializer};
use engine::utils::{deserialize_message, wrap_an_error};

/// Alias type for msps sender.
pub type MessageSender = Arc<mpsc::UnboundedSender<Message>>;
/// Alias for generic future that can be returned from internal proxy engine
/// handlers and middlewares.
pub type EngineFuture = Box<Future<Item=(), Error=PathfinderError> + Send + Sync + 'static>;

/// Proxy engine for processing messages, handling errors and communicating
/// with a message broker.
pub struct Engine {
    router: Arc<Router>,
    middlewares: Arc<HashMap<String, Arc<Box<Middleware>>>>
}

impl Engine {
    /// Returns a new instance of `Engine`.
    pub fn new(cli: &CliOptions) -> Engine {
        let config = get_config(&cli.config);
        let endpoints = extract_endpoints(config);
        let router = Router::new(endpoints);
        let middlewares_list: Vec<(&str, Box<Middleware>)> = vec![
            ("jwt", Box::new(JwtTokenMiddleware::new())),
            ("empty", Box::new(EmptyMiddleware::new())),
        ];
        let middlewares = middlewares_list
            .into_iter()
            .map(|(key, middleware)| (String::from(key), Arc::new(middleware)))
            .collect();

        Engine {
            router: Arc::new(router),
            middlewares: Arc::new(middlewares),
        }
    }

    pub fn process_request(&self, message: Message, transmitter: MessageSender, rabbitmq_client: Arc<RabbitMQClient>) -> Box<Future<Item=(), Error=()> + Send + Sync + 'static> {
        // 1. Deserialize message into JSON
        let transmitter_for_json_error = transmitter.clone();
        let json_message = match deserialize_message(&message) {
            Ok(json_message) => json_message,
            Err(error) => {
                let formatted_error = format!("{}", error);
                let error_message = wrap_an_error(formatted_error.as_str());
                transmitter_for_json_error.unbounded_send(error_message).unwrap();
                return Box::new(lazy(move || Ok(())));
            }
        };

        // 2. Apply a middleware to each incoming message
        let url = json_message["url"].as_str().unwrap();
        let transmitter_for_match_error = transmitter.clone();
        let endpoint = match self.get_endpoint(url) {
            Ok(endpoint) => endpoint.clone(),
            Err(error) => {
                let formatted_error = format!("{}", error);
                let error_message = wrap_an_error(formatted_error.as_str());
                transmitter_for_match_error.unbounded_send(error_message).unwrap();
                return Box::new(lazy(move || Ok(())));
            }
        };
        let middleware = self.get_middleware(endpoint.clone()).clone();
        let auth_future = middleware.process_request(json_message.clone(), rabbitmq_client.clone());

        // 3. Put request into a queue in RabbitMQ and receive the response
        let rpc_options = RpcOptions::new(
            endpoint.clone(),
            json_message.clone(),
            transmitter.clone(),
            Arc::new(format!("{}", Uuid::new_v4()))
        );
        let transmitter_nested = transmitter.clone();
        let rabbitmq_future = self.handle(rabbitmq_client, rpc_options);

        Box::new(
            auth_future
                .and_then(move |_| rabbitmq_future)
                .map_err(move |error| {
                    let formatted_error = format!("{}", error);
                    let error_message = wrap_an_error(formatted_error.as_str());
                    transmitter_nested.unbounded_send(error_message).unwrap();
                    ()
                })
        )
    }

    /// Returns an endpoint based on specified URL.
    fn get_endpoint(&self, url: &str) -> Result<ReadOnlyEndpoint> {
        let router = self.router.clone();
        router.match_url(&url)
    }

    /// Returns a middleware for processing client credentials.
    fn get_middleware(&self, endpoint: ReadOnlyEndpoint) -> Arc<Box<Middleware>> {
        match endpoint.is_token_required() {
            true => self.middlewares.clone()["jwt"].clone(),
            false => self.middlewares.clone()["empty"].clone()
        }
    }

    /// Main handler for generating a response per each incoming request.
    fn handle(&self, rabbitmq_client: Arc<RabbitMQClient>, options: RpcOptions) -> EngineFuture {
        let message = options.message().clone();
        let endpoint = options.endpoint().clone();
        let request_headers = self.prepare_request_headers(&message, endpoint);
        let rabbitmq_client_local = rabbitmq_client.clone();

        Box::new(
            // 1. Create a channel
            rabbitmq_client_local.get_channel()
            // 2. Declare a response queue
            .and_then(move |channel| {
                let queue_name = options.queue_name().clone();
                let queue_declare_options = QueueDeclareOptions {
                    passive: false,
                    durable: true,
                    exclusive: true,
                    auto_delete: false,
                    ..Default::default()
                };

                channel
                    .queue_declare(&queue_name, queue_declare_options, FieldTable::new())
                    .map(move |queue| (channel, queue, options))
            })
            // 3. Link the response queue the exchange
            .and_then(move |(channel, queue, options)| {
                let queue_name = options.queue_name().clone();
                let endpoint = options.endpoint().clone();
                let routing_key = options.queue_name().clone();

                channel
                    .queue_bind(
                        &queue_name,
                        &endpoint.get_response_exchange(),
                        &routing_key,
                        QueueBindOptions::default(),
                        FieldTable::new()
                    )
                    .map(move |_| (channel, queue, options))
            })
            // 4. Publish message into the microservice queue and make ensure that it's delivered
            .and_then(move |(channel, queue, options)| {
                let publish_message_options = BasicPublishOptions {
                    mandatory: true,
                    immediate: false,
                    ..Default::default()
                };

                let mut message_headers = FieldTable::new();
                for &(ref key, ref value) in request_headers.clone().iter() {
                    let header_name = key.to_string();
                    let header_value = AMQPValue::LongString(value.to_string());
                    message_headers.insert(header_name, header_value);
                }

                let endpoint = options.endpoint().clone();
                let message = options.message().clone();
                let queue_name_response = options.queue_name().clone();
                let event_name = message["event-name"].as_str().unwrap_or("null");
                let basic_properties = BasicProperties::default()
                    .with_content_type("application/json".to_string())    // Content type
                    .with_headers(message_headers)                        // Headers for the message
                    .with_delivery_mode(2)                                // Message must be persistent
                    .with_reply_to(queue_name_response.to_string())       // Response queue
                    .with_correlation_id(event_name.clone().to_string()); // Event name

                channel
                    .basic_publish(
                        &endpoint.get_request_exchange(),
                        &endpoint.get_microservice(),
                        message["content"].dump().as_bytes().to_vec(),
                        publish_message_options,
                        basic_properties
                    )
                    .map(move |_confirmation| (channel, queue, options))
            })
            // 5. Consume a response message from the queue, that was declared on the 2nd step
            .and_then(move |(channel, queue, options)| {
                channel
                    .basic_consume(
                        &queue,
                        "response_consumer",
                        BasicConsumeOptions::default(),
                        FieldTable::new()
                    )
                    .and_then(move |stream| {
                        stream
                            .take(1)
                            .into_future()
                            .map_err(|(err, _)| err)
                            .map(move |(message, _)| (channel, queue, message.unwrap(), options))
                    })
            })
            // 6. Prepare a response for a client, serialize and sent via WebSocket transmitter
            .and_then(move |(channel, queue, message, options)| {
                let raw_data = from_utf8(&message.data).unwrap();
                let json = Arc::new(Box::new(json_parse(raw_data).unwrap()));
                let serializer = Serializer::new();
                let response = serializer.serialize(json.dump()).unwrap();
                let transmitter = options.transmitter().clone();
                transmitter.unbounded_send(response).unwrap();

                channel
                    .basic_ack(message.delivery_tag, false)
                    .map(move |_confirmation| (channel, queue, options))
            })
            // 7. Unbind the response queue from the exchange point
            .and_then(move |(channel, _queue, options)| {
                let queue_name = options.queue_name().clone();
                let endpoint = options.endpoint().clone();

                channel
                    .queue_unbind(
                        &queue_name,
                        &endpoint.get_response_exchange(),
                        &endpoint.get_microservice(),
                        QueueUnbindOptions::default(),
                        FieldTable::new(),
                    )
                    .map(move |_| (channel, options))
            })
            // 8. Delete the response queue
            .and_then(move |(channel, options)| {
                let queue_delete_options = QueueDeleteOptions {
                    if_unused: false,
                    if_empty: false,
                    ..Default::default()
                };
                let queue_name = options.queue_name().clone();

                channel
                    .queue_delete(&queue_name, queue_delete_options)
                    .map(move |_| channel)
            })
            // 9. Close the channel
            .and_then(move |channel| {
                channel.close(200, "Close the channel.")
            })
            .then(move |result| match result {
                Ok(_) => Ok(()),
                Err(err) => {
                    error!("Error in RabbitMQ client. Reason -> {}", err);
                    let message = String::from("The request wasn't processed. Please, try once again.");
                    Err(PathfinderError::MessageBrokerError(message))
                }
            })
        )
    }

    /// Prepares a list of key-value pairs for headers in message.
    fn prepare_request_headers(&self, json: &JsonMessage, endpoint: ReadOnlyEndpoint) -> Box<Vec<(String, String)>> {
        Box::new(vec![
            (String::from("microservice_name"), endpoint.get_microservice()),
            (String::from("request_url"), endpoint.get_url()),
            (String::from("permissions"), json["permissions"].as_str().unwrap_or("").to_string()),
            (String::from("user_id"), json["user_id"].as_str().unwrap_or("").to_string())
        ])
    }
}
<commit_msg>Refactored the process_request method<commit_after>//! Proxy engine
//!
//! This module is intended for processing incoming requests from clients,
//! handling occurred errors during a work, communicating with a message
//! broker and preparing appropriate responses in the certain format.
//!

use std::collections::HashMap;
use std::str::from_utf8;
use std::sync::Arc;
use std::vec::Vec;

use futures::future::{lazy, Future};
use futures::sync::mpsc;
use futures::Stream;
use json::parse as json_parse;
use lapin_futures_rustls::lapin::channel::{
    BasicConsumeOptions, BasicProperties, BasicPublishOptions, QueueBindOptions,
    QueueDeclareOptions, QueueDeleteOptions, QueueUnbindOptions,
};
use lapin_futures_rustls::lapin::types::{AMQPValue, FieldTable};
use tungstenite::Message;
use uuid::Uuid;

use super::super::cli::CliOptions;
use super::super::config::get_config;
use super::super::error::{Result, PathfinderError};
use super::super::rabbitmq::RabbitMQClient;
use engine::middleware::{EmptyMiddleware, JwtTokenMiddleware, Middleware, MiddlewareFuture};
use engine::router::{extract_endpoints, ReadOnlyEndpoint, Router};
use engine::options::RpcOptions;
use engine::serializer::{JsonMessage, Serializer};
use engine::utils::{deserialize_message, wrap_an_error};

/// Alias type for msps sender.
pub type MessageSender = Arc<mpsc::UnboundedSender<Message>>;
/// Alias for generic future that can be returned from internal proxy engine
/// handlers and middlewares.
pub type EngineFuture = Box<Future<Item=(), Error=PathfinderError> + Send + Sync + 'static>;

/// Proxy engine for processing messages, handling errors and communicating
/// with a message broker.
pub struct Engine {
    router: Arc<Router>,
    middlewares: Arc<HashMap<String, Arc<Box<Middleware>>>>
}

impl Engine {
    /// Returns a new instance of `Engine`.
    pub fn new(cli: &CliOptions) -> Engine {
        let config = get_config(&cli.config);
        let endpoints = extract_endpoints(config);
        let router = Router::new(endpoints);
        let middlewares_list: Vec<(&str, Box<Middleware>)> = vec![
            ("jwt", Box::new(JwtTokenMiddleware::new())),
            ("empty", Box::new(EmptyMiddleware::new())),
        ];
        let middlewares = middlewares_list
            .into_iter()
            .map(|(key, middleware)| (String::from(key), Arc::new(middleware)))
            .collect();

        Engine {
            router: Arc::new(router),
            middlewares: Arc::new(middlewares),
        }
    }

    pub fn process_request(&self, message: Message, transmitter: MessageSender, rabbitmq_client: Arc<RabbitMQClient>) -> Box<Future<Item=(), Error=()> + Send + Sync + 'static> {
        // 1. Deserialize message into JSON
        let json_message = match self.get_json_message(&message, transmitter.clone()) {
            Ok(json_message) => json_message,
            Err(_error) => return Box::new(lazy(move || Ok(())))
        };

        // 2. Finding an endpoint in according to the URL in the message body
        let url = json_message["url"].as_str().unwrap();
        let endpoint = match self.get_endpoint(url, transmitter.clone()) {
            Ok(endpoint) => endpoint.clone(),
            Err(_error) => return Box::new(lazy(move || Ok(())))
        };

        // 3. Instantiate futures that will be processing client credentials and a request
        let rpc_options = Arc::new(RpcOptions::new(
            endpoint.clone(),
            json_message.clone(),
            transmitter.clone(),
            Arc::new(format!("{}", Uuid::new_v4()))
        ));
        let middleware_future = self.get_middleware_future(rabbitmq_client.clone(), rpc_options.clone());
        let rabbitmq_future = self.rpc_request(rabbitmq_client.clone(), rpc_options.clone());

        let transmitter_nested = transmitter.clone();
        Box::new(
            middleware_future
                .and_then(move |_| rabbitmq_future)
                .map_err(move |error| {
                    let formatted_error = format!("{}", error);
                    let error_message = wrap_an_error(formatted_error.as_str());
                    transmitter_nested.unbounded_send(error_message).unwrap();
                    ()
                })
        )
    }

    /// Returns a messsage in JSON format of a deserialization error.
    fn get_json_message(&self, message: &Message, transmitter: MessageSender) -> Result<JsonMessage> {
        let transmitter_nested = transmitter.clone();

        match deserialize_message(&message) {
            Ok(json_message) => Ok(json_message),
            Err(error) => {
                let formatted_error = format!("{}", error);
                let error_message = wrap_an_error(formatted_error.as_str());
                transmitter_nested.unbounded_send(error_message).unwrap();
                Err(error)
            }
        }
    }

    /// Returns an endpoint based on specified URL.
    fn get_endpoint(&self, url: &str, transmitter: MessageSender) -> Result<ReadOnlyEndpoint> {
        let router = self.router.clone();
        let transmitter_nested = transmitter.clone();

        match router.match_url(&url) {
            Ok(endpoint) => Ok(endpoint),
            Err(error) => {
                let formatted_error = format!("{}", error);
                let error_message = wrap_an_error(formatted_error.as_str());
                transmitter_nested.unbounded_send(error_message).unwrap();
                Err(error)
            }
        }
    }

    /// Returns a middleware for processing client credentials.
    fn get_middleware_future(&self, rabbitmq_client: Arc<RabbitMQClient>, options: Arc<RpcOptions>) -> MiddlewareFuture {
        let endpoint = options.endpoint().clone();
        let middleware = self.get_middleware_by_endpoint(endpoint);

        let json_message = options.message().clone();
        let rabbitmq_client_local = rabbitmq_client.clone();
        middleware.process_request(json_message, rabbitmq_client_local)
    }

    /// Return a middleware that matches to the passed endpoint
    fn get_middleware_by_endpoint(&self, endpoint: ReadOnlyEndpoint) -> Arc<Box<Middleware>> {
        match endpoint.is_token_required() {
            true => self.middlewares.clone()["jwt"].clone(),
            false => self.middlewares.clone()["empty"].clone()
        }
    }

    /// Main handler for generating a response per each incoming request.
    fn rpc_request(&self, rabbitmq_client: Arc<RabbitMQClient>, options: Arc<RpcOptions>) -> EngineFuture {
        let message = options.message().clone();
        let endpoint = options.endpoint().clone();
        let request_headers = self.prepare_request_headers(&message, endpoint);
        let rabbitmq_client_local = rabbitmq_client.clone();

        Box::new(
            // 1. Create a channel
            rabbitmq_client_local.get_channel()
            // 2. Declare a response queue
            .and_then(move |channel| {
                let queue_name = options.queue_name().clone();
                let queue_declare_options = QueueDeclareOptions {
                    passive: false,
                    durable: true,
                    exclusive: true,
                    auto_delete: false,
                    ..Default::default()
                };

                channel
                    .queue_declare(&queue_name, queue_declare_options, FieldTable::new())
                    .map(move |queue| (channel, queue, options))
            })
            // 3. Link the response queue the exchange
            .and_then(move |(channel, queue, options)| {
                let queue_name = options.queue_name().clone();
                let endpoint = options.endpoint().clone();
                let routing_key = options.queue_name().clone();

                channel
                    .queue_bind(
                        &queue_name,
                        &endpoint.get_response_exchange(),
                        &routing_key,
                        QueueBindOptions::default(),
                        FieldTable::new()
                    )
                    .map(move |_| (channel, queue, options))
            })
            // 4. Publish message into the microservice queue and make ensure that it's delivered
            .and_then(move |(channel, queue, options)| {
                let publish_message_options = BasicPublishOptions {
                    mandatory: true,
                    immediate: false,
                    ..Default::default()
                };

                let mut message_headers = FieldTable::new();
                for &(ref key, ref value) in request_headers.clone().iter() {
                    let header_name = key.to_string();
                    let header_value = AMQPValue::LongString(value.to_string());
                    message_headers.insert(header_name, header_value);
                }

                let endpoint = options.endpoint().clone();
                let message = options.message().clone();
                let queue_name_response = options.queue_name().clone();
                let event_name = message["event-name"].as_str().unwrap_or("null");
                let basic_properties = BasicProperties::default()
                    .with_content_type("application/json".to_string())    // Content type
                    .with_headers(message_headers)                        // Headers for the message
                    .with_delivery_mode(2)                                // Message must be persistent
                    .with_reply_to(queue_name_response.to_string())       // Response queue
                    .with_correlation_id(event_name.clone().to_string()); // Event name

                channel
                    .basic_publish(
                        &endpoint.get_request_exchange(),
                        &endpoint.get_microservice(),
                        message["content"].dump().as_bytes().to_vec(),
                        publish_message_options,
                        basic_properties
                    )
                    .map(move |_confirmation| (channel, queue, options))
            })
            // 5. Consume a response message from the queue, that was declared on the 2nd step
            .and_then(move |(channel, queue, options)| {
                channel
                    .basic_consume(
                        &queue,
                        "response_consumer",
                        BasicConsumeOptions::default(),
                        FieldTable::new()
                    )
                    .and_then(move |stream| {
                        stream
                            .take(1)
                            .into_future()
                            .map_err(|(err, _)| err)
                            .map(move |(message, _)| (channel, queue, message.unwrap(), options))
                    })
            })
            // 6. Prepare a response for a client, serialize and sent via WebSocket transmitter
            .and_then(move |(channel, queue, message, options)| {
                let raw_data = from_utf8(&message.data).unwrap();
                let json = Arc::new(Box::new(json_parse(raw_data).unwrap()));
                let serializer = Serializer::new();
                let response = serializer.serialize(json.dump()).unwrap();
                let transmitter = options.transmitter().clone();
                transmitter.unbounded_send(response).unwrap();

                channel
                    .basic_ack(message.delivery_tag, false)
                    .map(move |_confirmation| (channel, queue, options))
            })
            // 7. Unbind the response queue from the exchange point
            .and_then(move |(channel, _queue, options)| {
                let queue_name = options.queue_name().clone();
                let endpoint = options.endpoint().clone();

                channel
                    .queue_unbind(
                        &queue_name,
                        &endpoint.get_response_exchange(),
                        &endpoint.get_microservice(),
                        QueueUnbindOptions::default(),
                        FieldTable::new(),
                    )
                    .map(move |_| (channel, options))
            })
            // 8. Delete the response queue
            .and_then(move |(channel, options)| {
                let queue_delete_options = QueueDeleteOptions {
                    if_unused: false,
                    if_empty: false,
                    ..Default::default()
                };
                let queue_name = options.queue_name().clone();

                channel
                    .queue_delete(&queue_name, queue_delete_options)
                    .map(move |_| channel)
            })
            // 9. Close the channel
            .and_then(move |channel| {
                channel.close(200, "Close the channel.")
            })
            .then(move |result| match result {
                Ok(_) => Ok(()),
                Err(err) => {
                    error!("Error in RabbitMQ client. Reason -> {}", err);
                    let message = String::from("The request wasn't processed. Please, try once again.");
                    Err(PathfinderError::MessageBrokerError(message))
                }
            })
        )
    }

    /// Prepares a list of key-value pairs for headers in message.
    fn prepare_request_headers(&self, json: &JsonMessage, endpoint: ReadOnlyEndpoint) -> Box<Vec<(String, String)>> {
        Box::new(vec![
            (String::from("microservice_name"), endpoint.get_microservice()),
            (String::from("request_url"), endpoint.get_url()),
            (String::from("permissions"), json["permissions"].as_str().unwrap_or("").to_string()),
            (String::from("user_id"), json["user_id"].as_str().unwrap_or("").to_string())
        ])
    }
}
<|endoftext|>


<commit_before>use crate::prelude::*;

/// A `Brush` describes how a shape is filled or stroked.
/// A `Brush` can be parse from a string(as that is how the brushes are parsed in the themes)
/// For example by an expression you have three methods to define a solid color:
///
/// A. Use color codes, for example `#f00` will give you red or if you write #00ff00 you will got blue
/// also you can use alpha.
///
/// B. Use a function, currently the unique available functions to interpret a color are `rgb`, `hsv`, `hsb`
/// (which is the same as `hsv`), `hsl` and its respective alpha variants(rgba by example). So you can define
/// a color as `hsl(197, 71%, 73%)` and you will get a pretty skyblue color. For `rgb` and `rgba` the
/// range of the values are 0-255 but from all others the values are as follows: hsva(0.0-360.0, 0.0-1.0, 0.0-1.0, 0.0-1.0)
/// but the % sign can change that, it can be added to individual parameters and change its range depending on the function,
/// for `rgb` and `rgba` you can write an % after a parameter and it will be mapped from 0.0-100.0, or if the number that you write
/// is between 0.0 and 1.0 you value will be mapped from that range. But if you function is not rgb or rgba, you can not use a
/// percent sign in the first parameters but in the following parameters the sign will make OrbTk interpret it in the 0.0-100.0 range.
///
/// C. Use the color name! so you want to write a colour and do you code work lazily?, no problem! you can directly write the name.
/// For example white, red, olive, ivory, linen or any of the CSS color names!, also the next extra colors are supported:
/// * alabaster
/// * alto
/// * bluebayoux
/// * bombay
/// * brightgrey
/// * dolly
/// * energyellow
/// * fiord
/// * goldendream
/// * goldenfizz
/// * goldtips
/// * gorduroy
/// * governorbay
/// * linkwater
/// * lynch
/// * manatee
/// * manz
/// * mineshaft
/// * periwinklegrey
/// * portage
/// * sandwisp
/// * silverchalice
/// * sunflower
/// You also can define a gradient with an expression, the syntax is as follows:
/// ```
/// [repeating-]linear-gradient({Gradient angle}{deg|rad|turn}, ...) [{X Displacement}px {Y Displacement}px]
/// ```
/// Where {} speaks of a customizable parameter except if it has a | in middle then is only
/// a multiple choice question(so you have to choice between degrees(deg), radians(rad) or
/// turns(turn)) and [] means and optional part(but with a meaning!), now the ... are only
/// referring to the multiples stops that you can define in your gradient, the syntax for a
/// stop is as follows:
/// ```
/// {Color} [{Stop position}{%|px}]
/// ```
/// But enough theory, here are some examples:
/// ```
/// linear-gradient(0deg, #4b6cb7, #182848)
/// repeating-linear-gradient(0.25turn, rgba(255, 255, 0, 0.6), dodgerblue, deepskyblue)
/// linear-gradient(-90deg, hsv(201, 94%, 80.5%), steelblue)
/// ```
/// Yes sir, I like the blue but that examples works and give you some nice degrees and orientation
/// about how to use it.
/// I expected this quick explain will help you, thanks for reading!
#[derive(Clone, PartialEq, Debug)]
pub enum Brush {
    /// Paints an area with a solid color.
    SolidColor(Color),

    /// Paints an area with a gradient.
    Gradient(Gradient),
}

impl Brush {
    pub fn is_transparent(&self) -> bool {
        match self {
            Brush::SolidColor(color) => color.a() == 0,
            _ => false,
        }
    }
}

impl From<Brush> for Color {
    fn from(b: Brush) -> Color {
        match b {
            Brush::SolidColor(color) => color,
            _ => Color::rgb(0, 0, 0),
        }
    }
}

impl From<Brush> for Gradient {
    fn from(b: Brush) -> Gradient {
        match b {
            Brush::Gradient(g) => g,
            _ => Gradient::default(),
        }
    }
}

impl Default for Brush {
    fn default() -> Self {
        Brush::SolidColor(Color::rgba(0, 0, 0, 0))
    }
}

impl From<Color> for Brush {
    fn from(c: Color) -> Brush {
        Brush::SolidColor(c)
    }
}

impl From<Gradient> for Brush {
    fn from(g: Gradient) -> Brush {
        Brush::Gradient(g)
    }
}

impl From<&str> for Brush {
    fn from(s: &str) -> Brush {
        Expression::from(s).brush().unwrap_or_default()
    }
}

impl From<String> for Brush {
    fn from(s: String) -> Brush {
        Self::from(&s[..])
    }
}

impl From<Value> for Brush {
    fn from(v: Value) -> Self {
        let value = v.get::<String>();
        Brush::from(value)
    }
}

#[cfg(test)]
mod tests {
    //  use crate::prelude::*;
    // todo: tbd after brush struct is finished
}
<commit_msg>brush: update documentation section<commit_after>use crate::prelude::*;

/// A `Brush` describes how a shape is filled or stroked.  A `Brush`
/// can be parse from a string (themes are parsing brushes that way).
/// With the given implementation you can choose between three methods
/// to define a solid color:
///
/// A. Use `color codes`
///
/// Define the value with a symbol "#" followed by 6 letters or numbers.
/// These numbers are in hexadecimal numeral system.
/// For example `#f00` will give you red. If you write `#00ff00`, you will
/// get blue and you also included an alpha channel.
///
/// B. Use `a function`
///
/// Currently the unique available functions that interprete a color are
/// destincted with the keywords `rgb`, `hsv`, `hsb`, `hsl`. There are
/// `alpha variants` as well. `hsb` is an alias to `hsv`.
/// Alpha variants are coded with the keywords `rgba`, `abgr`  or `argb`.
/// Here is an example to define a color via the function method:
/// `hsl(197, 71%, 73%)` will provide you a pretty skyblue color.
/// For `rgb` and `rgba` the range of the values are 0-255.
/// Any other keyword will use floating point integers to define the color
/// value. `hsva(0.0-360.0, 0.0-1.0, 0.0-1.0, 0.0-1.0)` is such an example.
/// In addition you can choose to use percent values (`%` sign) for the given
/// parameters.
/// When appending the `%` sign to the range parameters of the `rgb` function
/// call, the values are mapped to 0.0-100.0 (percent) or 0.0-1.0 (min/max).
/// For all other keywords (`hsv`, `hsb`, `hsl`) you are not allowed to append
/// the percent sign to the first parameter. If you append `%` to the following
/// parameters, OrbTk will interpret the values in a range between `0.0-100.0`.
///
/// C. Use the `color name`
///
/// OrbTK maintains a color name map (crates/utils/colors.txt). It enables
/// you, to directly choose from the listed color names. The toolkit will
/// decode the value with the corresponding color code.
/// Example color names are:
///  * white
///  * red
///  * olive,
///  * bombay
///
/// You can also define a gradient with an expression. Its syntax is
/// structured as follows:
/// ```
/// [repeating-]linear-gradient({Gradient-angle}{deg|rad|turn}, ...) [{X Displacement}px {Y Displacement}px]
/// ```
///
/// Within the braces (`{}`) you define the customized parameters.
/// The pipe (`|`) is offering mutual exclusive variants (e.g: degrees(deg),
/// radians(rad) or turns(turn)).
/// The syntax offers optional paramters inside brackets (`[]`).
/// The three points (`...`) refer to multiple stops. They are respected when
/// a gradient is rendered. Use the following syntax:
///
/// ```
/// {Color} [{Stop /// position}{%|px}]
/// ```
///
/// Quite a bit of theory. Lets see some examples:
///
/// ```
/// * linear-gradient(0deg, #4b6cb7, #182848)
/// * repeating-linear-gradient(0.25turn, rgba(255, 255, 0, 0.6), dodgerblue, deepskyblue)
/// * linear-gradient(-90deg, hsv(201, 94%, 80.5%), steelblue)
/// ```
///
/// Oh yes, I do like blue (smile)! Anyway, these examples should
/// introduce the concept and provide some nice implementations using
/// degrees and orientations.
/// You are free to adopt it as appropriate.
#[derive(Clone, PartialEq, Debug)]
pub enum Brush {
    /// Paints an area with a solid color.
    SolidColor(Color),

    /// Paints an area with a gradient.
    Gradient(Gradient),
}

impl Brush {
    pub fn is_transparent(&self) -> bool {
        match self {
            Brush::SolidColor(color) => color.a() == 0,
            _ => false,
        }
    }
}

impl From<Brush> for Color {
    fn from(b: Brush) -> Color {
        match b {
            Brush::SolidColor(color) => color,
            _ => Color::rgb(0, 0, 0),
        }
    }
}

impl From<Brush> for Gradient {
    fn from(b: Brush) -> Gradient {
        match b {
            Brush::Gradient(g) => g,
            _ => Gradient::default(),
        }
    }
}

impl Default for Brush {
    fn default() -> Self {
        Brush::SolidColor(Color::rgba(0, 0, 0, 0))
    }
}

impl From<Color> for Brush {
    fn from(c: Color) -> Brush {
        Brush::SolidColor(c)
    }
}

impl From<Gradient> for Brush {
    fn from(g: Gradient) -> Brush {
        Brush::Gradient(g)
    }
}

impl From<&str> for Brush {
    fn from(s: &str) -> Brush {
        Expression::from(s).brush().unwrap_or_default()
    }
}

impl From<String> for Brush {
    fn from(s: String) -> Brush {
        Self::from(&s[..])
    }
}

impl From<Value> for Brush {
    fn from(v: Value) -> Self {
        let value = v.get::<String>();
        Brush::from(value)
    }
}

#[cfg(test)]
mod tests {
    //  use crate::prelude::*;
    // todo: tbd after brush struct is finished
}
<|endoftext|>


<commit_before>// Copyright © 2016, Canal TP and/or its affiliates. All rights reserved.
//
// This file is part of Navitia,
//     the software to build cool stuff with public transport.
//
// Hope you'll enjoy and contribute to this project,
//     powered by Canal TP (www.canaltp.fr).
// Help us simplify mobility and open public transport:
//     a non ending quest to the responsive locomotion way of traveling!
//
// LICENCE: This program is free software; you can redistribute it
// and/or modify it under the terms of the GNU Affero General Public
// License as published by the Free Software Foundation, either
// version 3 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public
// License along with this program. If not, see
// <http://www.gnu.org/licenses/>.
//
// Stay tuned using
// twitter @navitia
// IRC #navitia on freenode
// https://groups.google.com/d/forum/navitia
// www.navitia.io
use super::model;
use regex;
use rs_es;
use rs_es::query::Query as rs_q;
use rs_es::operations::search::SearchResult;
use rs_es::units as rs_u;
use mimir;
use serde_json;
use serde;
use mimir::objects::{MimirObject, Admin, Addr, Stop};

fn build_rs_client(cnx: &String) -> rs_es::Client {
    let re = regex::Regex::new(r"(?:https?://)?(?P<host>.+?):(?P<port>\d+)").unwrap();
    let cap = re.captures(&cnx).unwrap();
    let host = cap.name("host").unwrap().as_str();
    let port = cap.name("port").unwrap().as_str().parse::<u32>().unwrap();

    rs_es::Client::new(&host, port)
}

/// takes a ES json blob and build a Place from it
/// it uses the _type field of ES to know which type of the Place enum to fill
pub fn make_place(doc_type: String, value: Option<Box<serde_json::Value>>) -> Option<mimir::Place> {
    value.and_then(|v| {
        fn convert<T: serde::Deserialize>(v: serde_json::Value,
                                          f: fn(T) -> mimir::Place)
                                          -> Option<mimir::Place> {
            serde_json::from_value::<T>(v)
                .map_err(|err| warn!("Impossible to load ES result: {}", err))
                .ok()
                .map(f)
        }
        match doc_type.as_ref() {
            "addr" => convert(*v, mimir::Place::Addr),
            "street" => convert(*v, mimir::Place::Street),
            "admin" => convert(*v, mimir::Place::Admin),
            "poi" => convert(*v, mimir::Place::Poi),
            "stop" => convert(*v, mimir::Place::Stop),
            _ => {
                warn!("unknown ES return value, _type field = {}", doc_type);
                None
            }
        }
    })
}

#[derive(Debug, Eq, PartialEq)]
enum MatchType {
    Prefix,
    Fuzzy,
}

fn build_query(q: &str,
               match_type: MatchType,
               coord: &Option<model::Coord>,
               shape: Option<Vec<rs_es::units::Location>>)
               -> rs_es::query::Query {
    use rs_es::query::functions::Function;
    // we order the type of object we want
    // Note: the addresses are boosted more because even if we don't want them first
    // because they are more severely filtered
    let boost_addr = rs_q::build_term("_type", Addr::doc_type()).with_boost(5000).build();
    let boost_admin = rs_q::build_term("_type", Admin::doc_type()).with_boost(3000).build();
    let boost_stop = rs_q::build_term("_type", Stop::doc_type()).with_boost(2000).build();

    let main_match_type = match match_type {
        MatchType::Prefix => "label.prefix",
        MatchType::Fuzzy => "label.ngram",
    };

    let boost_main_match_query = rs_q::build_match(main_match_type.to_string(), q.to_string())
        .with_boost(500)
        .build();

    let boost_zipcode_match_query = rs_q::build_match("zip_codes.prefix".to_string(),
                                                      q.to_string())
        .with_boost(100)
        .build();

    let mut should_query = vec![boost_addr,
                                boost_admin,
                                boost_stop,
                                boost_main_match_query,
                                boost_zipcode_match_query];

    // for fuzzy search we also search by the prefix index (with a greater boost than ngram)
    // to have better results
    if match_type == MatchType::Fuzzy {
        should_query.push(rs_q::build_match("label.prefix", q.to_string())
            .with_boost(1000)
            .build());
    }

    if let &Some(ref c) = coord {
        // if we have coordinate, we boost we result near this coordinate
        let boost_on_proximity =
            rs_q::build_function_score()
                .with_boost_mode(rs_es::query::compound::BoostMode::Multiply)
                .with_boost(1500)
                .with_function(Function::build_decay("coord",
                                           rs_u::Location::LatLon(c.lat, c.lon),
                                           rs_u::Distance::new(50f64,
                                                               rs_u::DistanceUnit::Kilometer))
                                   .build_exp())
                .build();
        should_query.push(boost_on_proximity);
    } else {
        // if we don't have coords, we take the field `weight` into account
        let boost_on_weight = rs_q::build_function_score()
            .with_boost_mode(rs_es::query::compound::BoostMode::Multiply)
            .with_boost(500)
            .with_query(rs_q::build_match_all().build())
            .with_function(Function::build_field_value_factor("weight")
                .with_factor(1)
                .with_modifier(rs_es::query::functions::Modifier::Log1p)
                .build())
            .build();
        should_query.push(boost_on_weight);
    }

    let sub_query = rs_q::build_bool()
        .with_should(should_query)
        .build();

    // filter to handle house number
    // we either want:
    // * to exactly match the document house_number
    // * or that the document has no house_number
    let first_condition = rs_q::build_bool()
        .with_should(vec![rs_q::build_bool()
                              .with_must_not(rs_q::build_exists("house_number").build())
                              .build(),
                          rs_q::build_match("house_number", q.to_string()).build()])
        .build();

    use rs_es::query::MinimumShouldMatch;

    let second_condition = match match_type {
        MatchType::Prefix => {
            rs_q::build_multi_match(vec!["label.prefix".to_string(),
                                         "zip_codes.prefix".to_string()],
                                    q.to_string())
                .with_type(rs_es::query::full_text::MatchQueryType::CrossFields)
                .with_operator("and")
                .build()
        }
        // for fuzzy search we lower our expectation and we accept 50% of token match
        MatchType::Fuzzy => {
            rs_q::build_multi_match(vec!["label.prefix".to_string(),
                                         "zip_codes.prefix".to_string()],
                                    q.to_string())
                .with_minimum_should_match(MinimumShouldMatch::from(50f64))
                .build()
        }
    };

    let mut must = vec![first_condition, second_condition];

    if let Some(s) = shape {
        must.push(rs_q::build_geo_polygon("coord", s).build());
    }
    let filter = rs_q::build_bool()
        .with_must(must)
        .build();

    rs_q::build_bool()
        .with_must(vec![sub_query])
        .with_filter(filter)
        .build()
}

fn is_existing_index(client: &mut rs_es::Client, index: &str) -> bool {
    !index.is_empty() && client.open_index(&index).is_ok()
}

fn get_indexes_by_type(a_type: &str) -> String {
    let doc_type = match a_type {
        "public_transport:stop_area" => "stop",
        "city" => "admin",
        "house" => "addr",
        _ => a_type,
    };

    format!("munin_{}", doc_type)
}

fn make_indexes_impl<F: FnMut(&str) -> bool>(all_data: bool,
                                             pt_dataset_index: &Option<String>,
                                             types: &Option<Vec<&str>>,
                                             mut is_existing_index: F)
                                             -> Vec<String> {
    if all_data {
        return vec!["munin".to_string()];
    }

    let mut result: Vec<String> = vec![];
    let mut push = |result: &mut Vec<_>, i: &str| if is_existing_index(i) {
        result.push(i.into());
    };
    if let Some(ref types) = *types {
        for type_ in types.iter().filter(|t| **t != "public_transport:stop_area") {
            push(&mut result, &get_indexes_by_type(type_));
        }
        match *pt_dataset_index {
            Some(ref index) if types.contains(&"public_transport:stop_area") => {
                push(&mut result, index)
            }
            _ => (),
        }
    } else {
        push(&mut result, &"munin_geo_data".to_string());

        if let Some(ref dataset) = *pt_dataset_index {
            push(&mut result, &dataset.clone());
        }
    }

    result
}

fn make_indexes(all_data: bool,
                pt_dataset_index: &Option<String>,
                types: &Option<Vec<&str>>,
                client: &mut rs_es::Client)
                -> Vec<String> {
    make_indexes_impl(all_data,
                      pt_dataset_index,
                      types,
                      |index| is_existing_index(client, index))
}

fn query(q: &str,
         pt_dataset: &Option<&str>,
         all_data: bool,
         cnx: &str,
         match_type: MatchType,
         offset: u64,
         limit: u64,
         coord: &Option<model::Coord>,
         shape: Option<Vec<rs_es::units::Location>>,
         types: &Option<Vec<&str>>)
         -> Result<Vec<mimir::Place>, rs_es::error::EsError> {
    let query = build_query(q, match_type, coord, shape);

    let mut client = build_rs_client(&cnx.to_string());

    let pt_dataset_index = pt_dataset.map(|d| format!("munin_stop_{}", d));
    let indexes = make_indexes(all_data, &pt_dataset_index, types, &mut client);

    debug!("ES indexes: {:?}", indexes);

    if indexes.is_empty() {
        // if there is no indexes, rs_es search with index "_all"
        // but we want to return emtpy response in this case.
        return Ok(vec![]);
    }

    let result: SearchResult<serde_json::Value> = try!(client.search_query()
        .with_indexes(&indexes.iter().map(|index| index.as_str()).collect::<Vec<&str>>())
        .with_query(&query)
        .with_from(offset)
        .with_size(limit)
        .send());

    debug!("{} documents found", result.hits.total);

    // for the moment rs-es does not handle enum Document,
    // so we need to convert the ES glob to a Place
    Ok(result.hits
        .hits
        .into_iter()
        .filter_map(|hit| make_place(hit.doc_type, hit.source))
        .collect())
}

pub fn autocomplete(q: &str,
                    pt_dataset: &Option<&str>,
                    all_data: bool,
                    offset: u64,
                    limit: u64,
                    coord: Option<model::Coord>,
                    cnx: &str,
                    shape: Option<Vec<(f64, f64)>>,
                    types: Option<Vec<&str>>)
                    -> Result<Vec<mimir::Place>, rs_es::error::EsError> {
    fn make_shape(shape: &Option<Vec<(f64, f64)>>) -> Option<Vec<rs_es::units::Location>> {
        shape.as_ref().map(|v| v.iter().map(|&l| l.into()).collect())
    }

    // First we try a prety exact match on the prefix.
    // If there are no results then we do a new fuzzy search (matching ngrams)
    let results = try!(query(&q,
                             &pt_dataset,
                             all_data,
                             cnx,
                             MatchType::Prefix,
                             offset,
                             limit,
                             &coord,
                             make_shape(&shape),
                             &types));
    if results.is_empty() {
        query(&q,
              &pt_dataset,
              all_data,
              cnx,
              MatchType::Fuzzy,
              offset,
              limit,
              &coord,
              make_shape(&shape),
              &types)
    } else {
        Ok(results)
    }
}

#[test]
fn test_make_indexes_impl() {
    // all_data
    assert_eq!(make_indexes_impl(true, &None, &None, |_index| true),
               vec!["munin"]);

    // no dataset and no types
    assert_eq!(make_indexes_impl(false, &None, &None, |_index| true),
               vec!["munin_geo_data"]);

    // dataset fr + no types
    assert_eq!(make_indexes_impl(false,
                                 &Some("munin_stop_fr".to_string()),
                                 &None,
                                 |_index| true),
               vec!["munin_geo_data", "munin_stop_fr"]);

    // no dataset + types poi, city, street, house and public_transport:stop_area
    // => munin_stop is not included
    assert_eq!(make_indexes_impl(false,
                                 &None,
                                 &Some(vec!["poi",
                                            "city",
                                            "street",
                                            "house",
                                            "public_transport:stop_area"]),
                                 |_index| true),
               vec!["munin_poi", "munin_admin", "munin_street", "munin_addr"]);

    // no dataset fr + type public_transport:stop_area only
    assert_eq!(make_indexes_impl(false,
                                 &None,
                                 &Some(vec!["public_transport:stop_area"]),
                                 |_index| true),
               Vec::<String>::new());

    // dataset fr + types poi, city, street, house and public_transport:stop_area
    assert_eq!(make_indexes_impl(false,
                                 &Some("munin_stop_fr".to_string()),
                                 &Some(vec!["poi",
                                            "city",
                                            "street",
                                            "house",
                                            "public_transport:stop_area"]),
                                 |_index| true),
               vec!["munin_poi", "munin_admin", "munin_street", "munin_addr", "munin_stop_fr"]);

    // dataset fr types poi, city, street, house without public_transport:stop_area
    //  => munin_stop_fr is not included
    assert_eq!(make_indexes_impl(false,
                                 &Some("munin_stop_fr".to_string()),
                                 &Some(vec!["poi", "city", "street", "house"]),
                                 |_index| true),
               vec!["munin_poi", "munin_admin", "munin_street", "munin_addr"]);
}
<commit_msg>change percent in filter<commit_after>// Copyright © 2016, Canal TP and/or its affiliates. All rights reserved.
//
// This file is part of Navitia,
//     the software to build cool stuff with public transport.
//
// Hope you'll enjoy and contribute to this project,
//     powered by Canal TP (www.canaltp.fr).
// Help us simplify mobility and open public transport:
//     a non ending quest to the responsive locomotion way of traveling!
//
// LICENCE: This program is free software; you can redistribute it
// and/or modify it under the terms of the GNU Affero General Public
// License as published by the Free Software Foundation, either
// version 3 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public
// License along with this program. If not, see
// <http://www.gnu.org/licenses/>.
//
// Stay tuned using
// twitter @navitia
// IRC #navitia on freenode
// https://groups.google.com/d/forum/navitia
// www.navitia.io
use super::model;
use regex;
use rs_es;
use rs_es::query::Query as rs_q;
use rs_es::operations::search::SearchResult;
use rs_es::units as rs_u;
use mimir;
use serde_json;
use serde;
use mimir::objects::{MimirObject, Admin, Addr, Stop};

fn build_rs_client(cnx: &String) -> rs_es::Client {
    let re = regex::Regex::new(r"(?:https?://)?(?P<host>.+?):(?P<port>\d+)").unwrap();
    let cap = re.captures(&cnx).unwrap();
    let host = cap.name("host").unwrap().as_str();
    let port = cap.name("port").unwrap().as_str().parse::<u32>().unwrap();

    rs_es::Client::new(&host, port)
}

/// takes a ES json blob and build a Place from it
/// it uses the _type field of ES to know which type of the Place enum to fill
pub fn make_place(doc_type: String, value: Option<Box<serde_json::Value>>) -> Option<mimir::Place> {
    value.and_then(|v| {
        fn convert<T: serde::Deserialize>(v: serde_json::Value,
                                          f: fn(T) -> mimir::Place)
                                          -> Option<mimir::Place> {
            serde_json::from_value::<T>(v)
                .map_err(|err| warn!("Impossible to load ES result: {}", err))
                .ok()
                .map(f)
        }
        match doc_type.as_ref() {
            "addr" => convert(*v, mimir::Place::Addr),
            "street" => convert(*v, mimir::Place::Street),
            "admin" => convert(*v, mimir::Place::Admin),
            "poi" => convert(*v, mimir::Place::Poi),
            "stop" => convert(*v, mimir::Place::Stop),
            _ => {
                warn!("unknown ES return value, _type field = {}", doc_type);
                None
            }
        }
    })
}

#[derive(Debug, Eq, PartialEq)]
enum MatchType {
    Prefix,
    Fuzzy,
}

fn build_query(q: &str,
               match_type: MatchType,
               coord: &Option<model::Coord>,
               shape: Option<Vec<rs_es::units::Location>>)
               -> rs_es::query::Query {
    use rs_es::query::functions::Function;
    // we order the type of object we want
    // Note: the addresses are boosted more because even if we don't want them first
    // because they are more severely filtered
    let boost_addr = rs_q::build_term("_type", Addr::doc_type()).with_boost(5000).build();
    let boost_admin = rs_q::build_term("_type", Admin::doc_type()).with_boost(3000).build();
    let boost_stop = rs_q::build_term("_type", Stop::doc_type()).with_boost(2000).build();

    let main_match_type = match match_type {
        MatchType::Prefix => "label.prefix",
        MatchType::Fuzzy => "label.ngram",
    };

    let boost_main_match_query = rs_q::build_match(main_match_type.to_string(), q.to_string())
        .with_boost(500)
        .build();

    let boost_zipcode_match_query = rs_q::build_match("zip_codes.prefix".to_string(),
                                                      q.to_string())
        .with_boost(100)
        .build();

    let mut should_query = vec![boost_addr,
                                boost_admin,
                                boost_stop,
                                boost_main_match_query,
                                boost_zipcode_match_query];

    // for fuzzy search we also search by the prefix index (with a greater boost than ngram)
    // to have better results
    if match_type == MatchType::Fuzzy {
        should_query.push(rs_q::build_match("label.prefix", q.to_string())
            .with_boost(1000)
            .build());
    }

    if let &Some(ref c) = coord {
        // if we have coordinate, we boost we result near this coordinate
        let boost_on_proximity =
            rs_q::build_function_score()
                .with_boost_mode(rs_es::query::compound::BoostMode::Multiply)
                .with_boost(1500)
                .with_function(Function::build_decay("coord",
                                           rs_u::Location::LatLon(c.lat, c.lon),
                                           rs_u::Distance::new(50f64,
                                                               rs_u::DistanceUnit::Kilometer))
                                   .build_exp())
                .build();
        should_query.push(boost_on_proximity);
    } else {
        // if we don't have coords, we take the field `weight` into account
        let boost_on_weight = rs_q::build_function_score()
            .with_boost_mode(rs_es::query::compound::BoostMode::Multiply)
            .with_boost(500)
            .with_query(rs_q::build_match_all().build())
            .with_function(Function::build_field_value_factor("weight")
                .with_factor(1)
                .with_modifier(rs_es::query::functions::Modifier::Log1p)
                .build())
            .build();
        should_query.push(boost_on_weight);
    }

    let sub_query = rs_q::build_bool()
        .with_should(should_query)
        .build();

    // filter to handle house number
    // we either want:
    // * to exactly match the document house_number
    // * or that the document has no house_number
    let first_condition = rs_q::build_bool()
        .with_should(vec![rs_q::build_bool()
                              .with_must_not(rs_q::build_exists("house_number").build())
                              .build(),
                          rs_q::build_match("house_number", q.to_string()).build()])
        .build();

    use rs_es::query::MinimumShouldMatch;

    let second_condition = match match_type {
        MatchType::Prefix => {
            rs_q::build_multi_match(vec!["label.prefix".to_string(),
                                         "zip_codes.prefix".to_string()],
                                    q.to_string())
                .with_type(rs_es::query::full_text::MatchQueryType::CrossFields)
                .with_operator("and")
                .build()
        }
        // for fuzzy search we lower our expectation and we accept 50% of token match
        MatchType::Fuzzy => {
            rs_q::build_multi_match(vec!["label.prefix".to_string(),
                                         "zip_codes.prefix".to_string()],
                                    q.to_string())
                .with_minimum_should_match(MinimumShouldMatch::from(40f64))
                .build()
        }
    };

    let mut must = vec![first_condition, second_condition];

    if let Some(s) = shape {
        must.push(rs_q::build_geo_polygon("coord", s).build());
    }
    let filter = rs_q::build_bool()
        .with_must(must)
        .build();

    rs_q::build_bool()
        .with_must(vec![sub_query])
        .with_filter(filter)
        .build()
}

fn is_existing_index(client: &mut rs_es::Client, index: &str) -> bool {
    !index.is_empty() && client.open_index(&index).is_ok()
}

fn get_indexes_by_type(a_type: &str) -> String {
    let doc_type = match a_type {
        "public_transport:stop_area" => "stop",
        "city" => "admin",
        "house" => "addr",
        _ => a_type,
    };

    format!("munin_{}", doc_type)
}

fn make_indexes_impl<F: FnMut(&str) -> bool>(all_data: bool,
                                             pt_dataset_index: &Option<String>,
                                             types: &Option<Vec<&str>>,
                                             mut is_existing_index: F)
                                             -> Vec<String> {
    if all_data {
        return vec!["munin".to_string()];
    }

    let mut result: Vec<String> = vec![];
    let mut push = |result: &mut Vec<_>, i: &str| if is_existing_index(i) {
        result.push(i.into());
    };
    if let Some(ref types) = *types {
        for type_ in types.iter().filter(|t| **t != "public_transport:stop_area") {
            push(&mut result, &get_indexes_by_type(type_));
        }
        match *pt_dataset_index {
            Some(ref index) if types.contains(&"public_transport:stop_area") => {
                push(&mut result, index)
            }
            _ => (),
        }
    } else {
        push(&mut result, &"munin_geo_data".to_string());

        if let Some(ref dataset) = *pt_dataset_index {
            push(&mut result, &dataset.clone());
        }
    }

    result
}

fn make_indexes(all_data: bool,
                pt_dataset_index: &Option<String>,
                types: &Option<Vec<&str>>,
                client: &mut rs_es::Client)
                -> Vec<String> {
    make_indexes_impl(all_data,
                      pt_dataset_index,
                      types,
                      |index| is_existing_index(client, index))
}

fn query(q: &str,
         pt_dataset: &Option<&str>,
         all_data: bool,
         cnx: &str,
         match_type: MatchType,
         offset: u64,
         limit: u64,
         coord: &Option<model::Coord>,
         shape: Option<Vec<rs_es::units::Location>>,
         types: &Option<Vec<&str>>)
         -> Result<Vec<mimir::Place>, rs_es::error::EsError> {
    let query = build_query(q, match_type, coord, shape);

    let mut client = build_rs_client(&cnx.to_string());

    let pt_dataset_index = pt_dataset.map(|d| format!("munin_stop_{}", d));
    let indexes = make_indexes(all_data, &pt_dataset_index, types, &mut client);

    debug!("ES indexes: {:?}", indexes);

    if indexes.is_empty() {
        // if there is no indexes, rs_es search with index "_all"
        // but we want to return emtpy response in this case.
        return Ok(vec![]);
    }

    let result: SearchResult<serde_json::Value> = try!(client.search_query()
        .with_indexes(&indexes.iter().map(|index| index.as_str()).collect::<Vec<&str>>())
        .with_query(&query)
        .with_from(offset)
        .with_size(limit)
        .send());

    debug!("{} documents found", result.hits.total);

    // for the moment rs-es does not handle enum Document,
    // so we need to convert the ES glob to a Place
    Ok(result.hits
        .hits
        .into_iter()
        .filter_map(|hit| make_place(hit.doc_type, hit.source))
        .collect())
}

pub fn autocomplete(q: &str,
                    pt_dataset: &Option<&str>,
                    all_data: bool,
                    offset: u64,
                    limit: u64,
                    coord: Option<model::Coord>,
                    cnx: &str,
                    shape: Option<Vec<(f64, f64)>>,
                    types: Option<Vec<&str>>)
                    -> Result<Vec<mimir::Place>, rs_es::error::EsError> {
    fn make_shape(shape: &Option<Vec<(f64, f64)>>) -> Option<Vec<rs_es::units::Location>> {
        shape.as_ref().map(|v| v.iter().map(|&l| l.into()).collect())
    }

    // First we try a prety exact match on the prefix.
    // If there are no results then we do a new fuzzy search (matching ngrams)
    let results = try!(query(&q,
                             &pt_dataset,
                             all_data,
                             cnx,
                             MatchType::Prefix,
                             offset,
                             limit,
                             &coord,
                             make_shape(&shape),
                             &types));
    if results.is_empty() {
        query(&q,
              &pt_dataset,
              all_data,
              cnx,
              MatchType::Fuzzy,
              offset,
              limit,
              &coord,
              make_shape(&shape),
              &types)
    } else {
        Ok(results)
    }
}

#[test]
fn test_make_indexes_impl() {
    // all_data
    assert_eq!(make_indexes_impl(true, &None, &None, |_index| true),
               vec!["munin"]);

    // no dataset and no types
    assert_eq!(make_indexes_impl(false, &None, &None, |_index| true),
               vec!["munin_geo_data"]);

    // dataset fr + no types
    assert_eq!(make_indexes_impl(false,
                                 &Some("munin_stop_fr".to_string()),
                                 &None,
                                 |_index| true),
               vec!["munin_geo_data", "munin_stop_fr"]);

    // no dataset + types poi, city, street, house and public_transport:stop_area
    // => munin_stop is not included
    assert_eq!(make_indexes_impl(false,
                                 &None,
                                 &Some(vec!["poi",
                                            "city",
                                            "street",
                                            "house",
                                            "public_transport:stop_area"]),
                                 |_index| true),
               vec!["munin_poi", "munin_admin", "munin_street", "munin_addr"]);

    // no dataset fr + type public_transport:stop_area only
    assert_eq!(make_indexes_impl(false,
                                 &None,
                                 &Some(vec!["public_transport:stop_area"]),
                                 |_index| true),
               Vec::<String>::new());

    // dataset fr + types poi, city, street, house and public_transport:stop_area
    assert_eq!(make_indexes_impl(false,
                                 &Some("munin_stop_fr".to_string()),
                                 &Some(vec!["poi",
                                            "city",
                                            "street",
                                            "house",
                                            "public_transport:stop_area"]),
                                 |_index| true),
               vec!["munin_poi", "munin_admin", "munin_street", "munin_addr", "munin_stop_fr"]);

    // dataset fr types poi, city, street, house without public_transport:stop_area
    //  => munin_stop_fr is not included
    assert_eq!(make_indexes_impl(false,
                                 &Some("munin_stop_fr".to_string()),
                                 &Some(vec!["poi", "city", "street", "house"]),
                                 |_index| true),
               vec!["munin_poi", "munin_admin", "munin_street", "munin_addr"]);
}
<|endoftext|>


<commit_before>extern crate clap;

use std::env;
use std::ffi::OsStr;

use clap::{App, Arg};

#[test]
fn env() {
    env::set_var("CLP_TEST_ENV", "env");

    let r = App::new("df")
        .arg(Arg::from("[arg] 'some opt'").env("CLP_TEST_ENV"))
        .try_get_matches_from(vec![""]);

    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("arg"));
    assert_eq!(m.occurrences_of("arg"), 0);
    assert_eq!(m.value_of("arg").unwrap(), "env");
}

#[test]
fn env_os() {
    env::set_var("CLP_TEST_ENV", "env");

    let r = App::new("df")
        .arg(Arg::from("[arg] 'some opt'").env_os(OsStr::new("CLP_TEST_ENV")))
        .try_get_matches_from(vec![""]);

    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("arg"));
    assert_eq!(m.occurrences_of("arg"), 0);
    assert_eq!(m.value_of("arg").unwrap(), "env");
}

#[test]
fn no_env() {
    // All the other tests use the presence of the Environment variable...
    // we need another variable just in case one of the others is running at the same time...
    env::remove_var("CLP_TEST_ENV_NONE");

    let r = App::new("df")
        .arg(Arg::from("[arg] 'some opt'").env("CLP_TEST_ENV_NONE"))
        .try_get_matches_from(vec![""]);

    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(!m.is_present("arg"));
    assert_eq!(m.occurrences_of("arg"), 0);
    assert_eq!(m.value_of("arg"), None);
}

#[test]
fn with_default() {
    env::set_var("CLP_TEST_ENV", "env");

    let r = App::new("df")
        .arg(
            Arg::from("[arg] 'some opt'")
                .env("CLP_TEST_ENV")
                .default_value("default"),
        )
        .try_get_matches_from(vec![""]);

    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("arg"));
    assert_eq!(m.occurrences_of("arg"), 0);
    assert_eq!(m.value_of("arg").unwrap(), "env");
}

#[test]
fn opt_user_override() {
    env::set_var("CLP_TEST_ENV", "env");

    let r = App::new("df")
        .arg(Arg::from("--arg [FILE] 'some arg'").env("CLP_TEST_ENV"))
        .try_get_matches_from(vec!["", "--arg", "opt"]);

    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("arg"));
    assert_eq!(m.occurrences_of("arg"), 1);
    assert_eq!(m.value_of("arg").unwrap(), "opt");
}

#[test]
fn positionals() {
    env::set_var("CLP_TEST_ENV", "env");

    let r = App::new("df")
        .arg(Arg::from("[arg] 'some opt'").env("CLP_TEST_ENV"))
        .try_get_matches_from(vec![""]);

    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("arg"));
    assert_eq!(m.occurrences_of("arg"), 0);
    assert_eq!(m.value_of("arg").unwrap(), "env");
}

#[test]
fn positionals_user_override() {
    env::set_var("CLP_TEST_ENV", "env");

    let r = App::new("df")
        .arg(Arg::from("[arg] 'some opt'").env("CLP_TEST_ENV"))
        .try_get_matches_from(vec!["", "opt"]);

    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("arg"));
    assert_eq!(m.occurrences_of("arg"), 1);
    assert_eq!(m.value_of("arg").unwrap(), "opt");
}

#[test]
fn multiple_one() {
    env::set_var("CLP_TEST_ENV", "env");

    let r = App::new("df")
        .arg(
            Arg::from("[arg] 'some opt'")
                .env("CLP_TEST_ENV")
                .use_delimiter(true)
                .multiple(true),
        )
        .try_get_matches_from(vec![""]);

    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("arg"));
    assert_eq!(m.occurrences_of("arg"), 0);
    assert_eq!(m.values_of("arg").unwrap().collect::<Vec<_>>(), vec!["env"]);
}

#[test]
fn multiple_three() {
    env::set_var("CLP_TEST_ENV_MULTI1", "env1,env2,env3");

    let r = App::new("df")
        .arg(
            Arg::from("[arg] 'some opt'")
                .env("CLP_TEST_ENV_MULTI1")
                .use_delimiter(true)
                .multiple(true),
        )
        .try_get_matches_from(vec![""]);

    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("arg"));
    assert_eq!(m.occurrences_of("arg"), 0);
    assert_eq!(
        m.values_of("arg").unwrap().collect::<Vec<_>>(),
        vec!["env1", "env2", "env3"]
    );
}

#[test]
fn multiple_no_delimiter() {
    env::set_var("CLP_TEST_ENV_MULTI2", "env1 env2 env3");

    let r = App::new("df")
        .arg(
            Arg::from("[arg] 'some opt'")
                .env("CLP_TEST_ENV_MULTI2")
                .multiple(true),
        )
        .try_get_matches_from(vec![""]);

    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("arg"));
    assert_eq!(m.occurrences_of("arg"), 0);
    assert_eq!(
        m.values_of("arg").unwrap().collect::<Vec<_>>(),
        vec!["env1 env2 env3"]
    );
}

#[test]
fn possible_value() {
    env::set_var("CLP_TEST_ENV", "env");

    let r = App::new("df")
        .arg(
            Arg::from("[arg] 'some opt'")
                .env("CLP_TEST_ENV")
                .possible_value("env"),
        )
        .try_get_matches_from(vec![""]);

    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("arg"));
    assert_eq!(m.occurrences_of("arg"), 0);
    assert_eq!(m.value_of("arg").unwrap(), "env");
}

#[test]
fn not_possible_value() {
    env::set_var("CLP_TEST_ENV", "env");

    let r = App::new("df")
        .arg(
            Arg::from("[arg] 'some opt'")
                .env("CLP_TEST_ENV")
                .possible_value("never"),
        )
        .try_get_matches_from(vec![""]);

    assert!(r.is_err());
}

#[test]
fn validator() {
    env::set_var("CLP_TEST_ENV", "env");

    let r = App::new("df")
        .arg(
            Arg::from("[arg] 'some opt'")
                .env("CLP_TEST_ENV")
                .validator(|s| {
                    if s == "env" {
                        Ok(())
                    } else {
                        Err("not equal".to_string())
                    }
                }),
        )
        .try_get_matches_from(vec![""]);

    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("arg"));
    assert_eq!(m.occurrences_of("arg"), 0);
    assert_eq!(m.value_of("arg").unwrap(), "env");
}

#[test]
fn validator_output() {
    env::set_var("CLP_TEST_ENV", "42");

    let m = App::new("df")
        .arg(
            Arg::from("[arg] 'some opt'")
                .env("CLP_TEST_ENV")
                .validator(|s| s.parse::<i32>()),
        )
        .get_matches_from(vec![""]);

    assert_eq!(m.value_of("arg").unwrap().parse(), Ok(42));
}

#[test]
fn validator_invalid() {
    env::set_var("CLP_TEST_ENV", "env");

    let r = App::new("df")
        .arg(
            Arg::from("[arg] 'some opt'")
                .env("CLP_TEST_ENV")
                .validator(|s| {
                    if s != "env" {
                        Ok(())
                    } else {
                        Err("is equal".to_string())
                    }
                }),
        )
        .try_get_matches_from(vec![""]);

    assert!(r.is_err());
}
<commit_msg>fix(env tests): fixes the env tests to allow multi-threaded test runs<commit_after>extern crate clap;

use std::env;
use std::ffi::OsStr;

use clap::{App, Arg};

#[test]
fn env() {
    env::set_var("CLP_TEST_ENV", "env");

    let r = App::new("df")
        .arg(Arg::from("[arg] 'some opt'").env("CLP_TEST_ENV"))
        .try_get_matches_from(vec![""]);

    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("arg"));
    assert_eq!(m.occurrences_of("arg"), 0);
    assert_eq!(m.value_of("arg").unwrap(), "env");
}

#[test]
fn env_os() {
    env::set_var("CLP_TEST_ENV_OS", "env");

    let r = App::new("df")
        .arg(Arg::from("[arg] 'some opt'").env_os(OsStr::new("CLP_TEST_ENV_OS")))
        .try_get_matches_from(vec![""]);

    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("arg"));
    assert_eq!(m.occurrences_of("arg"), 0);
    assert_eq!(m.value_of("arg").unwrap(), "env");
}

#[test]
fn no_env() {
    // All the other tests use the presence of the Environment variable...
    // we need another variable just in case one of the others is running at the same time...
    env::remove_var("CLP_TEST_ENV_NONE");

    let r = App::new("df")
        .arg(Arg::from("[arg] 'some opt'").env("CLP_TEST_ENV_NONE"))
        .try_get_matches_from(vec![""]);

    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(!m.is_present("arg"));
    assert_eq!(m.occurrences_of("arg"), 0);
    assert_eq!(m.value_of("arg"), None);
}

#[test]
fn with_default() {
    env::set_var("CLP_TEST_ENV_WD", "env");

    let r = App::new("df")
        .arg(
            Arg::from("[arg] 'some opt'")
                .env("CLP_TEST_ENV_WD")
                .default_value("default"),
        )
        .try_get_matches_from(vec![""]);

    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("arg"));
    assert_eq!(m.occurrences_of("arg"), 0);
    assert_eq!(m.value_of("arg").unwrap(), "env");
}

#[test]
fn opt_user_override() {
    env::set_var("CLP_TEST_ENV_OR", "env");

    let r = App::new("df")
        .arg(Arg::from("--arg [FILE] 'some arg'").env("CLP_TEST_ENV_OR"))
        .try_get_matches_from(vec!["", "--arg", "opt"]);

    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("arg"));
    assert_eq!(m.occurrences_of("arg"), 1);
    assert_eq!(m.value_of("arg").unwrap(), "opt");
}

#[test]
fn positionals() {
    env::set_var("CLP_TEST_ENV_P", "env");

    let r = App::new("df")
        .arg(Arg::from("[arg] 'some opt'").env("CLP_TEST_ENV_P"))
        .try_get_matches_from(vec![""]);

    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("arg"));
    assert_eq!(m.occurrences_of("arg"), 0);
    assert_eq!(m.value_of("arg").unwrap(), "env");
}

#[test]
fn positionals_user_override() {
    env::set_var("CLP_TEST_ENV_POR", "env");

    let r = App::new("df")
        .arg(Arg::from("[arg] 'some opt'").env("CLP_TEST_ENV_POR"))
        .try_get_matches_from(vec!["", "opt"]);

    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("arg"));
    assert_eq!(m.occurrences_of("arg"), 1);
    assert_eq!(m.value_of("arg").unwrap(), "opt");
}

#[test]
fn multiple_one() {
    env::set_var("CLP_TEST_ENV_MO", "env");

    let r = App::new("df")
        .arg(
            Arg::from("[arg] 'some opt'")
                .env("CLP_TEST_ENV_MO")
                .use_delimiter(true)
                .multiple(true),
        )
        .try_get_matches_from(vec![""]);

    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("arg"));
    assert_eq!(m.occurrences_of("arg"), 0);
    assert_eq!(m.values_of("arg").unwrap().collect::<Vec<_>>(), vec!["env"]);
}

#[test]
fn multiple_three() {
    env::set_var("CLP_TEST_ENV_MULTI1", "env1,env2,env3");

    let r = App::new("df")
        .arg(
            Arg::from("[arg] 'some opt'")
                .env("CLP_TEST_ENV_MULTI1")
                .use_delimiter(true)
                .multiple(true),
        )
        .try_get_matches_from(vec![""]);

    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("arg"));
    assert_eq!(m.occurrences_of("arg"), 0);
    assert_eq!(
        m.values_of("arg").unwrap().collect::<Vec<_>>(),
        vec!["env1", "env2", "env3"]
    );
}

#[test]
fn multiple_no_delimiter() {
    env::set_var("CLP_TEST_ENV_MULTI2", "env1 env2 env3");

    let r = App::new("df")
        .arg(
            Arg::from("[arg] 'some opt'")
                .env("CLP_TEST_ENV_MULTI2")
                .multiple(true),
        )
        .try_get_matches_from(vec![""]);

    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("arg"));
    assert_eq!(m.occurrences_of("arg"), 0);
    assert_eq!(
        m.values_of("arg").unwrap().collect::<Vec<_>>(),
        vec!["env1 env2 env3"]
    );
}

#[test]
fn possible_value() {
    env::set_var("CLP_TEST_ENV_PV", "env");

    let r = App::new("df")
        .arg(
            Arg::from("[arg] 'some opt'")
                .env("CLP_TEST_ENV_PV")
                .possible_value("env"),
        )
        .try_get_matches_from(vec![""]);

    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("arg"));
    assert_eq!(m.occurrences_of("arg"), 0);
    assert_eq!(m.value_of("arg").unwrap(), "env");
}

#[test]
fn not_possible_value() {
    env::set_var("CLP_TEST_ENV_NPV", "env");

    let r = App::new("df")
        .arg(
            Arg::from("[arg] 'some opt'")
                .env("CLP_TEST_ENV_NPV")
                .possible_value("never"),
        )
        .try_get_matches_from(vec![""]);

    assert!(r.is_err());
}

#[test]
fn validator() {
    env::set_var("CLP_TEST_ENV_VDOR", "env");

    let r = App::new("df")
        .arg(
            Arg::from("[arg] 'some opt'")
                .env("CLP_TEST_ENV_VDOR")
                .validator(|s| {
                    if s == "env" {
                        Ok(())
                    } else {
                        Err("not equal".to_string())
                    }
                }),
        )
        .try_get_matches_from(vec![""]);

    assert!(r.is_ok());
    let m = r.unwrap();
    assert!(m.is_present("arg"));
    assert_eq!(m.occurrences_of("arg"), 0);
    assert_eq!(m.value_of("arg").unwrap(), "env");
}

#[test]
fn validator_output() {
    env::set_var("CLP_TEST_ENV_VO", "42");

    let m = App::new("df")
        .arg(
            Arg::from("[arg] 'some opt'")
                .env("CLP_TEST_ENV_VO")
                .validator(|s| s.parse::<i32>()),
        )
        .get_matches_from(vec![""]);

    assert_eq!(m.value_of("arg").unwrap().parse(), Ok(42));
}

#[test]
fn validator_invalid() {
    env::set_var("CLP_TEST_ENV_IV", "env");

    let r = App::new("df")
        .arg(
            Arg::from("[arg] 'some opt'")
                .env("CLP_TEST_ENV_IV")
                .validator(|s| {
                    if s != "env" {
                        Ok(())
                    } else {
                        Err("is equal".to_string())
                    }
                }),
        )
        .try_get_matches_from(vec![""]);

    assert!(r.is_err());
}
<|endoftext|>


<commit_before>use libc::{c_int, c_uint, c_char, uint32_t};

// Setup linking for all targets.
#[cfg(target_os="macos")]
mod mac {
    #[cfg(any(mac_framework, feature="use_mac_framework"))]
    #[link(kind="framework", name="SDL2")]
    extern {}

    #[cfg(not(any(mac_framework, feature="use_mac_framework")))]
    #[link(name="SDL2")]
    extern {}
}

#[cfg(any(target_os="windows", target_os="linux", target_os="freebsd"))]
mod others {
    #[link(name="SDL2")]
    extern {}
}

pub type SDL_bool = c_int;

pub type SDL_errorcode = c_uint;
pub const SDL_ENOMEM: SDL_errorcode = 0;
pub const SDL_EFREAD: SDL_errorcode = 1;
pub const SDL_EFWRITE: SDL_errorcode = 2;
pub const SDL_EFSEEK: SDL_errorcode = 3;
pub const SDL_UNSUPPORTED: SDL_errorcode = 4;
pub const SDL_LASTERROR: SDL_errorcode = 5;

pub type SDL_InitFlag = uint32_t;
pub const SDL_INIT_TIMER: SDL_InitFlag = 0x00000001;
pub const SDL_INIT_AUDIO: SDL_InitFlag = 0x00000010;
pub const SDL_INIT_VIDEO: SDL_InitFlag = 0x00000020;
pub const SDL_INIT_JOYSTICK: SDL_InitFlag = 0x00000200;
pub const SDL_INIT_HAPTIC: SDL_InitFlag = 0x00001000;
pub const SDL_INIT_GAMECONTROLLER: SDL_InitFlag = 0x00002000;
pub const SDL_INIT_EVENTS: SDL_InitFlag = 0x00004000;
pub const SDL_INIT_NOPARACHUTE: SDL_InitFlag = 0x00100000;
pub const SDL_INIT_EVERYTHING: SDL_InitFlag = 0x0000FFFF;

//SDL_error.h
extern "C" {
    pub fn SDL_ClearError();
    pub fn SDL_Error(code: SDL_errorcode) -> c_int;
    pub fn SDL_SetError(fmt: *const c_char) -> c_int;
    pub fn SDL_GetError() -> *const c_char;

    //SDL.h
    pub fn SDL_Init(flags: uint32_t) -> c_int;
    pub fn SDL_InitSubSystem(flags: SDL_InitFlag) -> c_int;
    pub fn SDL_QuitSubSystem(flags: SDL_InitFlag);
    pub fn SDL_WasInit(flags: SDL_InitFlag) -> SDL_InitFlag;
    pub fn SDL_Quit();
}
<commit_msg>Remove redundant #[link] attributes (fixes #615)<commit_after>use libc::{c_int, c_uint, c_char, uint32_t};

pub type SDL_bool = c_int;

pub type SDL_errorcode = c_uint;
pub const SDL_ENOMEM: SDL_errorcode = 0;
pub const SDL_EFREAD: SDL_errorcode = 1;
pub const SDL_EFWRITE: SDL_errorcode = 2;
pub const SDL_EFSEEK: SDL_errorcode = 3;
pub const SDL_UNSUPPORTED: SDL_errorcode = 4;
pub const SDL_LASTERROR: SDL_errorcode = 5;

pub type SDL_InitFlag = uint32_t;
pub const SDL_INIT_TIMER: SDL_InitFlag = 0x00000001;
pub const SDL_INIT_AUDIO: SDL_InitFlag = 0x00000010;
pub const SDL_INIT_VIDEO: SDL_InitFlag = 0x00000020;
pub const SDL_INIT_JOYSTICK: SDL_InitFlag = 0x00000200;
pub const SDL_INIT_HAPTIC: SDL_InitFlag = 0x00001000;
pub const SDL_INIT_GAMECONTROLLER: SDL_InitFlag = 0x00002000;
pub const SDL_INIT_EVENTS: SDL_InitFlag = 0x00004000;
pub const SDL_INIT_NOPARACHUTE: SDL_InitFlag = 0x00100000;
pub const SDL_INIT_EVERYTHING: SDL_InitFlag = 0x0000FFFF;

//SDL_error.h
extern "C" {
    pub fn SDL_ClearError();
    pub fn SDL_Error(code: SDL_errorcode) -> c_int;
    pub fn SDL_SetError(fmt: *const c_char) -> c_int;
    pub fn SDL_GetError() -> *const c_char;

    //SDL.h
    pub fn SDL_Init(flags: uint32_t) -> c_int;
    pub fn SDL_InitSubSystem(flags: SDL_InitFlag) -> c_int;
    pub fn SDL_QuitSubSystem(flags: SDL_InitFlag);
    pub fn SDL_WasInit(flags: SDL_InitFlag) -> SDL_InitFlag;
    pub fn SDL_Quit();
}
<|endoftext|>


<commit_before>extern crate stratis_shared as shared;
extern crate uuid;

use ::uuid::Uuid;

use std::io::prelude::*;
use std::io::Cursor;

use shared::player::Player;
use shared::opcode;

use shared::chat;

#[test]
fn player_uuid () {
    let p = Player::default();
    let id = Uuid::new_v4();

    let mut bytes = Player::to_bytes(Some(&id),&p);
    let mut s = Cursor::new(&mut bytes[..]);

    assert!(s.get_ref().len() > 0);

    let mut b = [0u8];
    let r = s.read_exact(&mut b);
    assert!(r.is_ok());
    assert_eq!(b[0], opcode::PLAYER);

    let r = Player::from_stream(&mut s, true);
    assert!(r.0.is_some());
    assert!(r.1.is_some());

    assert_eq!(id,r.0.unwrap());
    assert_eq!(p,r.1.unwrap());
}

#[test]
fn player () {
    let p = Player::default();

    let mut bytes = Player::to_bytes(None,&p);
    let mut s = Cursor::new(&mut bytes[..]);

    assert!(s.get_ref().len() > 0);

    let mut b = [0u8];
    let r = s.read_exact(&mut b);
    assert!(r.is_ok());
    assert_eq!(b[0], opcode::PLAYER);

    let r = Player::from_stream(&mut s, false);
    assert!(r.0.is_none());
    assert!(r.1.is_some());

    assert_eq!(p,r.1.unwrap());
}

#[test]
fn chat() {
    let text = "test";
    let mut s = Cursor::new(vec![]);
    chat::write_text(&mut s, text);

    assert!(s.get_ref().len() > 0);

    s.set_position(0);
    let mut b = [0u8];
    let r = s.read_exact(&mut b);
    assert!(r.is_ok());
    assert_eq!(b[0], opcode::CHAT);

    let r = chat::read_text(&mut s);
    assert!(r.is_some());

    assert_eq!(text, r.unwrap());
}
<commit_msg>convenience functions<commit_after>extern crate stratis_shared as shared;
extern crate uuid;

use ::uuid::Uuid;

use std::io::prelude::*;
use std::io::Cursor;

use shared::player::Player;
use shared::opcode;

use shared::chat;

/// recreate to appease type checker
fn cursor_transform (s: &mut Cursor<Vec<u8>>) -> Cursor<&mut [u8]> {
 Cursor::new(&mut s.get_mut()[..])
}

fn assert_opcode(s: &mut Cursor<&mut [u8]>, op: u8) {
    s.set_position(0);
    let mut b = [0u8];
    let r = s.read_exact(&mut b);
    assert!(r.is_ok());
    assert_eq!(b[0], op);
}

#[test]
fn player_uuid () {
    let p = Player::default();
    let id = Uuid::new_v4();

    let mut bytes = Player::to_bytes(Some(&id),&p);
    let mut s = Cursor::new(&mut bytes[..]);

    assert!(s.get_ref().len() > 0);

    assert_opcode(&mut s,opcode::PLAYER);

    let r = Player::from_stream(&mut s, true);
    assert!(r.0.is_some());
    assert!(r.1.is_some());

    assert_eq!(id,r.0.unwrap());
    assert_eq!(p,r.1.unwrap());
}

#[test]
fn player () {
    let p = Player::default();

    let mut bytes = Player::to_bytes(None,&p);
    let mut s = Cursor::new(&mut bytes[..]);

    assert!(s.get_ref().len() > 0);

    assert_opcode(&mut s,opcode::PLAYER);

    let r = Player::from_stream(&mut s, false);
    assert!(r.0.is_none());
    assert!(r.1.is_some());

    assert_eq!(p,r.1.unwrap());
}

#[test]
fn chat() {
    let text = "test";
    let mut s = Cursor::new(vec![]);
    chat::write_text(&mut s, text);

    let mut s = cursor_transform(&mut s);
    assert!(s.get_ref().len() > 0);
    assert_opcode(&mut s,opcode::CHAT);

    let r = chat::read_text(&mut s);
    assert!(r.is_some());

    assert_eq!(text, r.unwrap());
}
<|endoftext|>


<commit_before>use std::io::net::udp::UdpSocket;
use std::io::net::ip::{Ipv4Addr, SocketAddr};

#[allow(dead_code,non_camel_case_types)]
enum UtpPacketType {
    ST_DATA  = 0,
    ST_FIN   = 1,
    ST_STATE = 2,
    ST_RESET = 3,
    ST_SYN   = 4,
}

#[allow(dead_code)]
struct UtpPacketHeader {
    type_ver: u8, // type: u4, ver: u4
    extension: u8,
    connection_id: u16,
    timestamp_microseconds: u32,
    timestamp_difference_microseconds: u32,
    wnd_size: u32,
    seq_nr: u16,
    ack_nr: u16,
}

impl UtpPacketHeader {
    fn bytes(&self) -> &[u8] {
        unsafe {
            let buf: &[u8, ..20] = std::mem::transmute(self);
            return buf.as_slice();
        }
    }

    fn len(&self) -> uint {
        let header_size = 20;
        return header_size;
    }
}

#[allow(dead_code)]
struct UtpPacket {
    header: UtpPacketHeader,
    payload: Vec<u8>,
}

impl UtpPacket {
    /// Constructs a new, empty UtpPacket.
    fn new() -> UtpPacket {
        UtpPacket {
            header: UtpPacketHeader {
                type_ver: ST_DATA as u8 << 4 | 1,
                extension: 0,
                connection_id: 0,
                timestamp_microseconds: 0,
                timestamp_difference_microseconds: 0,
                wnd_size: 0,
                seq_nr: 0,
                ack_nr: 0,
            },
            payload: Vec::new(),
        }
    }

    fn bytes(&self) -> Vec<u8> {
        let mut buf: Vec<u8> = Vec::with_capacity(self.len());
        buf.push_all(self.header.bytes());
        buf.push_all(self.payload.as_slice());
        return buf;
    }

    fn len(&self) -> uint {
        self.header.len() + self.payload.len()
    }
}

fn main() {
    let mut buf = [0, ..512];
    let mut sock = UdpSocket::bind(SocketAddr { ip: Ipv4Addr(127,0,0,1), port: 8080 }).unwrap();

    match sock.recvfrom(buf) {
        Ok((_, src)) => {
            let mut packet = UtpPacket::new();
            packet.payload = String::from_str("Hello\n").into_bytes();

            let _ = sock.sendto(packet.bytes().as_slice(), src);
        }
        Err(_) => {}
    }
    drop(sock);
}
<commit_msg>Send timestamp.<commit_after>extern crate time;

use std::io::net::udp::UdpSocket;
use std::io::net::ip::{Ipv4Addr, SocketAddr};

#[allow(dead_code,non_camel_case_types)]
enum UtpPacketType {
    ST_DATA  = 0,
    ST_FIN   = 1,
    ST_STATE = 2,
    ST_RESET = 3,
    ST_SYN   = 4,
}

#[allow(dead_code)]
struct UtpPacketHeader {
    type_ver: u8, // type: u4, ver: u4
    extension: u8,
    connection_id: u16,
    timestamp_microseconds: u32,
    timestamp_difference_microseconds: u32,
    wnd_size: u32,
    seq_nr: u16,
    ack_nr: u16,
}

impl UtpPacketHeader {
    fn bytes(&self) -> &[u8] {
        unsafe {
            let buf: &[u8, ..20] = std::mem::transmute(self);
            return buf.as_slice();
        }
    }

    fn len(&self) -> uint {
        let header_size = 20;
        return header_size;
    }
}

#[allow(dead_code)]
struct UtpPacket {
    header: UtpPacketHeader,
    payload: Vec<u8>,
}

impl UtpPacket {
    /// Constructs a new, empty UtpPacket.
    fn new() -> UtpPacket {
        UtpPacket {
            header: UtpPacketHeader {
                type_ver: ST_DATA as u8 << 4 | 1,
                extension: 0,
                connection_id: 0,
                timestamp_microseconds: 0,
                timestamp_difference_microseconds: 0,
                wnd_size: 0,
                seq_nr: 0,
                ack_nr: 0,
            },
            payload: Vec::new(),
        }
    }

    fn bytes(&self) -> Vec<u8> {
        let mut buf: Vec<u8> = Vec::with_capacity(self.len());
        buf.push_all(self.header.bytes());
        buf.push_all(self.payload.as_slice());
        return buf;
    }

    fn len(&self) -> uint {
        self.header.len() + self.payload.len()
    }
}

fn main() {
    let mut buf = [0, ..512];
    let mut sock = UdpSocket::bind(SocketAddr { ip: Ipv4Addr(127,0,0,1), port: 8080 }).unwrap();

    match sock.recvfrom(buf) {
        Ok((_, src)) => {
            let mut packet = UtpPacket::new();
            packet.payload = String::from_str("Hello\n").into_bytes();
            let t = time::get_time();
            packet.header.timestamp_microseconds = std::mem::to_be32((t.sec * 1_000_000) as u32 + (t.nsec/1000) as u32);

            let _ = sock.sendto(packet.bytes().as_slice(), src);
        }
        Err(_) => {}
    }
    drop(sock);
}
<|endoftext|>