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

//! # gl_generator
//!
//! `gl_generator` is an OpenGL bindings generator plugin. It defines a macro named
//!  `generate_gl_bindings!` which can be used to generate all constants and functions of a
//!  given OpenGL version.
//!
//! ## Example
//!
//! ```rust
//! #[phase(plugin)]
//! extern crate gl_generator;
//! extern crate libc;
//!
//! use std::mem;
//! use self::types::*;
//!
//! generate_gl_bindings!("gl", "core", "4.5", "global", [ "GL_EXT_texture_filter_anisotropic" ])
//! ```
//!
//! ## Parameters
//!
//! * API: Can be `gl`, `gles1`, `gles2`, `wgl`, `glx`, `egl`.
//! * Profile: Can be `core` or `compatibility`. `core` will only include all functions supported
//!    by the requested version it self, while `compatibility` will include all the functions from
//!    previous versions as well.
//! * Version: The requested version of OpenGL, WGL, GLX or EGL in the format `x.x`.
//! * Generator: Can be `static`, `global` or `struct`.
//! * Extensions (optional): An array of extensions to include in the bindings.
//!
//! ## About EGL
//!
//! When you generate bindings for EGL, the following platform-specific types must be declared
//!  *at the same level where you call `generate_gl_bindings`*:
//!
//! - khronos_utime_nanoseconds_t
//! - khronos_uint64_t
//! - khronos_ssize_t
//! - EGLNativeDisplayType
//! - EGLNativePixmapType
//! - EGLNativeWindowType
//! - EGLint
//! - NativeDisplayType
//! - NativePixmapType
//! - NativeWindowType
//!

#![crate_name = "gl_generator"]
#![comment = "OpenGL function loader generator."]
#![license = "ASL2"]
#![crate_type = "dylib"]

#![feature(phase)]
#![feature(globs)]
#![feature(macro_rules)]
#![feature(plugin_registrar)]
#![feature(quote)]

#[phase(plugin, link)]
extern crate log;

#[phase(plugin)]
extern crate regex_macros;

extern crate khronos_api;
extern crate regex;
extern crate rustc;
extern crate syntax;

use syntax::parse::token;
use syntax::ast::{ Item, TokenTree };
use syntax::ext::base::{expr_to_string, get_exprs_from_tts, DummyResult, ExtCtxt, MacResult};
use syntax::codemap::Span;

use registry::*;
use global_gen::GlobalGenerator;
use static_gen::StaticGenerator;
use struct_gen::StructGenerator;

mod common;
pub mod global_gen;
pub mod static_gen;
pub mod struct_gen;
pub mod registry;
pub mod ty;

#[plugin_registrar]
#[doc(hidden)]
pub fn plugin_registrar(reg: &mut ::rustc::plugin::Registry) {
    reg.register_macro("generate_gl_bindings", macro_handler);
}

// this is the object that we will return from the "generate_gl_bindings" macro expansion
struct MacroResult {
    content: Vec<::std::gc::Gc<Item>>
}
impl MacResult for MacroResult {
    fn make_def(&self) -> Option<::syntax::ext::base::MacroDef> { None }
    fn make_expr(&self) -> Option<::std::gc::Gc<::syntax::ast::Expr>> { None }
    fn make_pat(&self) -> Option<::std::gc::Gc<::syntax::ast::Pat>> { None }
    fn make_stmt(&self) -> Option<::std::gc::Gc<::syntax::ast::Stmt>> { None }

    fn make_items(&self) -> Option<::syntax::util::small_vector::SmallVector<::std::gc::Gc<Item>>> {
        Some(::syntax::util::small_vector::SmallVector::many(self.content.clone()))
    }
}

// handler for generate_gl_bindings!
fn macro_handler(ecx: &mut ExtCtxt, span: Span, token_tree: &[TokenTree]) -> Box<MacResult+'static> {
    // getting the arguments from the macro
    let (api, profile, version, generator, extensions) = match parse_macro_arguments(ecx, span.clone(), token_tree) {
        Some(t) => t,
        None => return DummyResult::any(span)
    };

    let (ns, source) = match api.as_slice() {
        "gl"  => (Gl, khronos_api::GL_XML),
        "glx" => (Glx, khronos_api::GLX_XML),
        "wgl" => (Wgl, khronos_api::WGL_XML),
        "egl" => (Egl, khronos_api::EGL_XML),
        "gles1"  => (Gles1, khronos_api::GL_XML),
        "gles2"  => (Gles2, khronos_api::GL_XML),
        ns => {
            ecx.span_err(span, format!("Unexpected opengl namespace '{}'", ns).as_slice());
            return DummyResult::any(span)
        }
    };

    let filter = Some(Filter {
        extensions: extensions,
        profile: profile,
        version: version,
        api: api,
    });

    // generating the registry of all bindings
    let reg = {
        use std::io::BufReader;
        use std::task;

        let result = task::try(proc() {
            let reader = BufReader::new(source.as_bytes());
            Registry::from_xml(reader, ns, filter)
        });

        match result {
            Ok(reg) => reg,
            Err(err) => {
                use std::any::{Any, AnyRefExt};
                let err: &Any = &*err;

                match err {
                    err if err.is::<String>() => {
                        ecx.span_err(span, "error while parsing the registry");
                        ecx.span_err(span, err.downcast_ref::<String>().unwrap().as_slice());
                    },
                    err if err.is::<&'static str>() => {
                        ecx.span_err(span, "error while parsing the registry");
                        ecx.span_err(span, err.downcast_ref::<&'static str>().unwrap().as_slice());
                    },
                    _ => {
                        ecx.span_err(span, "unknown error while parsing the registry");
                    }
                }

                return DummyResult::any(span);
            }
        }
    };

    // generating the Rust bindings as a source code into "buffer"
    let buffer = {
        use std::io::MemWriter;
        use std::task;

        // calling the generator
        let result = match generator.as_slice() {
            "global" => task::try(proc() {
                let mut buffer = MemWriter::new();
                GlobalGenerator::write(&mut buffer, &reg, ns);
                buffer
            }),

            "struct" => task::try(proc() {
                let mut buffer = MemWriter::new();
                StructGenerator::write(&mut buffer, &reg, ns);
                buffer
            }),

            "static" => task::try(proc() {
                let mut buffer = MemWriter::new();
                StaticGenerator::write(&mut buffer, &reg, ns);
                buffer
            }),

            generator => {
                ecx.span_err(span, format!("unknown generator type: {}", generator).as_slice());
                return DummyResult::any(span);
            },
        };

        // processing the result
        match result {
            Ok(buffer) => buffer.unwrap(),
            Err(err) => {
                use std::any::{Any, AnyRefExt};
                let err: &Any = &*err;

                match err {
                    err if err.is::<String>() => {
                        ecx.span_err(span, "error while generating the bindings");
                        ecx.span_err(span, err.downcast_ref::<String>().unwrap().as_slice());
                    },
                    err if err.is::<&'static str>() => {
                        ecx.span_err(span, "error while generating the bindings");
                        ecx.span_err(span, err.downcast_ref::<&'static str>().unwrap().as_slice());
                    },
                    _ => {
                        ecx.span_err(span, "unknown error while generating the bindings");
                    }
                }

                return DummyResult::any(span);
            }
        }
    };

    // creating a new Rust parser from these bindings
    let content = match String::from_utf8(buffer) {
        Ok(s) => s,
        Err(err) => {
            ecx.span_err(span, format!("{}", err).as_slice());
            return DummyResult::any(span)
        }
    };
    let mut parser = ::syntax::parse::new_parser_from_source_str(ecx.parse_sess(), ecx.cfg(),
        Path::new(ecx.codemap().span_to_filename(span)).display().to_string(), content);

    // getting all the items defined by the bindings
    let mut items = Vec::new();
    loop {
        match parser.parse_item_with_outer_attributes() {
            None => break,
            Some(i) => items.push(i)
        }
    }
    if !parser.eat(&token::EOF) {
        ecx.span_err(span, "the rust parser failed to compile all the generated bindings (meaning there is a bug in this library!)");
        return DummyResult::any(span)
    }
    box MacroResult { content: items } as Box<MacResult>
}

fn parse_macro_arguments(ecx: &mut ExtCtxt, span: Span, tts: &[syntax::ast::TokenTree])
                        -> Option<(String, String, String, String, Vec<String>)>
{
    // getting parameters list
    let values = match get_exprs_from_tts(ecx, span, tts) {
        Some(v) => v,
        None => return None
    };

    if values.len() != 4 && values.len() != 5 {
        ecx.span_err(span, format!("expected 4 or 5 arguments but got {}", values.len())
            .as_slice());
        return None;
    }

    // computing the extensions (last parameter)
    let extensions: Vec<String> = match values.as_slice().get(4) {
        None => Vec::new(),
        Some(vector) => {
            use syntax::ast::ExprVec;

            match vector.node {
                // only [ ... ] is accepted
                ExprVec(ref list) => {
                    // turning each element into a string
                    let mut result = Vec::new();
                    for element in list.iter() {
                        match expr_to_string(ecx, element.clone(), "expected string literal") {
                            Some((s, _)) => result.push(s.get().to_string()),
                            None => return None
                        }
                    }
                    result
                },
                _ => {
                    ecx.span_err(span, format!("last argument must be a vector").as_slice());
                    return None;
                }
            }
        }
    };

    // computing other parameters
    match (
        expr_to_string(ecx, values[0].clone(), "expected string literal")
            .map(|e| match e { (s, _) => s.get().to_string() }),
        expr_to_string(ecx, values[1].clone(), "expected string literal")
            .map(|e| match e { (s, _) => s.get().to_string() }),
        expr_to_string(ecx, values[2].clone(), "expected string literal")
            .map(|e| match e { (s, _) => s.get().to_string() }),
        expr_to_string(ecx, values[3].clone(), "expected string literal")
            .map(|e| match e { (s, _) => s.get().to_string() })
    ) {
        (Some(a), Some(b), Some(c), Some(d)) => Some((a, b, c, d, extensions)),
        _ => None
    }
}
<commit_msg>Update for rust 21d1f4d7c 2014-09-14 10:36:08 +0000<commit_after>// Copyright 2013-2014 The gl-rs developers. For a full listing of the authors,
// refer to the AUTHORS file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! # gl_generator
//!
//! `gl_generator` is an OpenGL bindings generator plugin. It defines a macro named
//!  `generate_gl_bindings!` which can be used to generate all constants and functions of a
//!  given OpenGL version.
//!
//! ## Example
//!
//! ```rust
//! #[phase(plugin)]
//! extern crate gl_generator;
//! extern crate libc;
//!
//! use std::mem;
//! use self::types::*;
//!
//! generate_gl_bindings!("gl", "core", "4.5", "global", [ "GL_EXT_texture_filter_anisotropic" ])
//! ```
//!
//! ## Parameters
//!
//! * API: Can be `gl`, `gles1`, `gles2`, `wgl`, `glx`, `egl`.
//! * Profile: Can be `core` or `compatibility`. `core` will only include all functions supported
//!    by the requested version it self, while `compatibility` will include all the functions from
//!    previous versions as well.
//! * Version: The requested version of OpenGL, WGL, GLX or EGL in the format `x.x`.
//! * Generator: Can be `static`, `global` or `struct`.
//! * Extensions (optional): An array of extensions to include in the bindings.
//!
//! ## About EGL
//!
//! When you generate bindings for EGL, the following platform-specific types must be declared
//!  *at the same level where you call `generate_gl_bindings`*:
//!
//! - khronos_utime_nanoseconds_t
//! - khronos_uint64_t
//! - khronos_ssize_t
//! - EGLNativeDisplayType
//! - EGLNativePixmapType
//! - EGLNativeWindowType
//! - EGLint
//! - NativeDisplayType
//! - NativePixmapType
//! - NativeWindowType
//!

#![crate_name = "gl_generator"]
#![comment = "OpenGL function loader generator."]
#![license = "ASL2"]
#![crate_type = "dylib"]

#![feature(phase)]
#![feature(globs)]
#![feature(macro_rules)]
#![feature(plugin_registrar)]
#![feature(quote)]

#[phase(plugin, link)]
extern crate log;

#[phase(plugin)]
extern crate regex_macros;

extern crate khronos_api;
extern crate regex;
extern crate rustc;
extern crate syntax;

use syntax::parse::token;
use syntax::ast::{ Item, TokenTree };
use syntax::ext::base::{expr_to_string, get_exprs_from_tts, DummyResult, ExtCtxt, MacResult};
use syntax::codemap::Span;
use syntax::ptr::P;

use registry::*;
use global_gen::GlobalGenerator;
use static_gen::StaticGenerator;
use struct_gen::StructGenerator;

mod common;
pub mod global_gen;
pub mod static_gen;
pub mod struct_gen;
pub mod registry;
pub mod ty;

#[plugin_registrar]
#[doc(hidden)]
pub fn plugin_registrar(reg: &mut ::rustc::plugin::Registry) {
    reg.register_macro("generate_gl_bindings", macro_handler);
}

// this is the object that we will return from the "generate_gl_bindings" macro expansion
struct MacroResult {
    content: Vec<P<Item>>
}
impl MacResult for MacroResult {
    fn make_def(&mut self) -> Option<::syntax::ext::base::MacroDef> { None }
    fn make_expr(self: Box<MacroResult>) -> Option<P<::syntax::ast::Expr>> { None }
    fn make_pat(self: Box<MacroResult>) -> Option<P<::syntax::ast::Pat>> { None }
    fn make_stmt(self: Box<MacroResult>) -> Option<P<::syntax::ast::Stmt>> { None }

    fn make_items(self: Box<MacroResult>) -> Option<::syntax::util::small_vector::SmallVector<P<Item>>> {
        Some(::syntax::util::small_vector::SmallVector::many(self.content.clone()))
    }
}

// handler for generate_gl_bindings!
fn macro_handler(ecx: &mut ExtCtxt, span: Span, token_tree: &[TokenTree]) -> Box<MacResult+'static> {
    // getting the arguments from the macro
    let (api, profile, version, generator, extensions) = match parse_macro_arguments(ecx, span.clone(), token_tree) {
        Some(t) => t,
        None => return DummyResult::any(span)
    };

    let (ns, source) = match api.as_slice() {
        "gl"  => (Gl, khronos_api::GL_XML),
        "glx" => (Glx, khronos_api::GLX_XML),
        "wgl" => (Wgl, khronos_api::WGL_XML),
        "egl" => (Egl, khronos_api::EGL_XML),
        "gles1"  => (Gles1, khronos_api::GL_XML),
        "gles2"  => (Gles2, khronos_api::GL_XML),
        ns => {
            ecx.span_err(span, format!("Unexpected opengl namespace '{}'", ns).as_slice());
            return DummyResult::any(span)
        }
    };

    let filter = Some(Filter {
        extensions: extensions,
        profile: profile,
        version: version,
        api: api,
    });

    // generating the registry of all bindings
    let reg = {
        use std::io::BufReader;
        use std::task;

        let result = task::try(proc() {
            let reader = BufReader::new(source.as_bytes());
            Registry::from_xml(reader, ns, filter)
        });

        match result {
            Ok(reg) => reg,
            Err(err) => {
                use std::any::{Any, AnyRefExt};
                let err: &Any = &*err;

                match err {
                    err if err.is::<String>() => {
                        ecx.span_err(span, "error while parsing the registry");
                        ecx.span_err(span, err.downcast_ref::<String>().unwrap().as_slice());
                    },
                    err if err.is::<&'static str>() => {
                        ecx.span_err(span, "error while parsing the registry");
                        ecx.span_err(span, err.downcast_ref::<&'static str>().unwrap().as_slice());
                    },
                    _ => {
                        ecx.span_err(span, "unknown error while parsing the registry");
                    }
                }

                return DummyResult::any(span);
            }
        }
    };

    // generating the Rust bindings as a source code into "buffer"
    let buffer = {
        use std::io::MemWriter;
        use std::task;

        // calling the generator
        let result = match generator.as_slice() {
            "global" => task::try(proc() {
                let mut buffer = MemWriter::new();
                GlobalGenerator::write(&mut buffer, &reg, ns);
                buffer
            }),

            "struct" => task::try(proc() {
                let mut buffer = MemWriter::new();
                StructGenerator::write(&mut buffer, &reg, ns);
                buffer
            }),

            "static" => task::try(proc() {
                let mut buffer = MemWriter::new();
                StaticGenerator::write(&mut buffer, &reg, ns);
                buffer
            }),

            generator => {
                ecx.span_err(span, format!("unknown generator type: {}", generator).as_slice());
                return DummyResult::any(span);
            },
        };

        // processing the result
        match result {
            Ok(buffer) => buffer.unwrap(),
            Err(err) => {
                use std::any::{Any, AnyRefExt};
                let err: &Any = &*err;

                match err {
                    err if err.is::<String>() => {
                        ecx.span_err(span, "error while generating the bindings");
                        ecx.span_err(span, err.downcast_ref::<String>().unwrap().as_slice());
                    },
                    err if err.is::<&'static str>() => {
                        ecx.span_err(span, "error while generating the bindings");
                        ecx.span_err(span, err.downcast_ref::<&'static str>().unwrap().as_slice());
                    },
                    _ => {
                        ecx.span_err(span, "unknown error while generating the bindings");
                    }
                }

                return DummyResult::any(span);
            }
        }
    };

    // creating a new Rust parser from these bindings
    let content = match String::from_utf8(buffer) {
        Ok(s) => s,
        Err(err) => {
            ecx.span_err(span, format!("{}", err).as_slice());
            return DummyResult::any(span)
        }
    };
    let mut parser = ::syntax::parse::new_parser_from_source_str(ecx.parse_sess(), ecx.cfg(),
        Path::new(ecx.codemap().span_to_filename(span)).display().to_string(), content);

    // getting all the items defined by the bindings
    let mut items = Vec::new();
    loop {
        match parser.parse_item_with_outer_attributes() {
            None => break,
            Some(i) => items.push(i)
        }
    }
    if !parser.eat(&token::EOF) {
        ecx.span_err(span, "the rust parser failed to compile all the generated bindings (meaning there is a bug in this library!)");
        return DummyResult::any(span)
    }
    box MacroResult { content: items } as Box<MacResult>
}

fn parse_macro_arguments(ecx: &mut ExtCtxt, span: Span, tts: &[syntax::ast::TokenTree])
                        -> Option<(String, String, String, String, Vec<String>)>
{
    // getting parameters list
    let values = match get_exprs_from_tts(ecx, span, tts) {
        Some(v) => v,
        None => return None
    };

    if values.len() != 4 && values.len() != 5 {
        ecx.span_err(span, format!("expected 4 or 5 arguments but got {}", values.len())
            .as_slice());
        return None;
    }

    // computing the extensions (last parameter)
    let extensions: Vec<String> = match values.as_slice().get(4) {
        None => Vec::new(),
        Some(vector) => {
            use syntax::ast::ExprVec;

            match vector.node {
                // only [ ... ] is accepted
                ExprVec(ref list) => {
                    // turning each element into a string
                    let mut result = Vec::new();
                    for element in list.iter() {
                        match expr_to_string(ecx, element.clone(), "expected string literal") {
                            Some((s, _)) => result.push(s.get().to_string()),
                            None => return None
                        }
                    }
                    result
                },
                _ => {
                    ecx.span_err(span, format!("last argument must be a vector").as_slice());
                    return None;
                }
            }
        }
    };

    // computing other parameters
    match (
        expr_to_string(ecx, values.as_slice().get(0).unwrap().clone(), "expected string literal")
            .map(|e| match e { (s, _) => s.get().to_string() }),
        expr_to_string(ecx, values.as_slice().get(1).unwrap().clone(), "expected string literal")
            .map(|e| match e { (s, _) => s.get().to_string() }),
        expr_to_string(ecx, values.as_slice().get(2).unwrap().clone(), "expected string literal")
            .map(|e| match e { (s, _) => s.get().to_string() }),
        expr_to_string(ecx, values.as_slice().get(3).unwrap().clone(), "expected string literal")
            .map(|e| match e { (s, _) => s.get().to_string() })
    ) {
        (Some(a), Some(b), Some(c), Some(d)) => Some((a, b, c, d, extensions)),
        _ => None
    }
}
<|endoftext|>


<commit_before>extern crate iron;
extern crate mount;
extern crate rand;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate staticfile;

use iron::method::Method;
use iron::prelude::*;
use iron::status::Status;
use mount::Mount;
use rand::Rng;
use staticfile::Static;
use std::collections::HashMap;
use std::collections::HashSet;
use std::fs::File;
use std::io::Read;
use std::path::Path;
use std::sync::Arc;
use std::sync::Mutex;
use std::thread;
use std::time::Duration;
use std::time::Instant;

static CONFIG_FILE_NAME: &str = "find-reviewer.json";

#[derive(Debug, Deserialize, Eq, PartialEq)]
enum FindReviewerRequest {
    NeedReviewer { coder: String },
    HaveTimeForReview { reviewer: String },
    WillReview { review_id: usize },
    WontReview { review_id: usize },
}

#[derive(Debug, Eq, PartialEq, Serialize)]
enum FindReviewerResponse {
    Accepted {},
    NoReviewerNeeded {},
    AlreadyRegistered {},
    NeedsReviewer { coder: String, review_id: usize },
    ReviewNotFound {},
}

type SharedApplication = Arc<Mutex<Application<RandomIdGenerator>>>;

fn main() {
    let configuration = load_configuration();
    let application = SharedApplication::new(Mutex::new(Application::new(configuration)));

    let mut mount = Mount::new();
    mount
        .mount("/find-reviewer", {
            let application = application.clone();
            move |request: &mut Request| Ok(process_request(request, &application))
        })
        .mount("/", Static::new(Path::new("www")));

    thread::spawn(move || loop {
        thread::sleep(Duration::from_secs(1));
        application.lock().unwrap().process_timeouts();
    });

    Iron::new(mount).http("localhost:3000").unwrap();
}

fn load_configuration() -> ApplicationConfiguration {
    let config = File::open(CONFIG_FILE_NAME)
        .map(|open_file| serde_json::from_reader(open_file).expect(&format!("Could not parse {}", CONFIG_FILE_NAME)))
        .unwrap_or_else(|err| {
            println!("Could not read {}: {}\nFile will be created", CONFIG_FILE_NAME, err);
            ApplicationConfiguration::default()
        });

    File::create(CONFIG_FILE_NAME)
        .map(|created_file| {
            serde_json::to_writer_pretty(created_file, &config).expect(&format!("Could not serialize {}", CONFIG_FILE_NAME))
        })
        .unwrap_or_else(|err| println!("Could not write {}: {}", CONFIG_FILE_NAME, err));

    config
}

fn process_request(request: &mut Request, application: &SharedApplication) -> Response {
    if request.method != Method::Post {
        return Response::with((Status::BadRequest, "Must be a POST request"));
    }

    let parsed = match serde_json::from_reader(request.body.by_ref()) {
        Err(message) => return Response::with((Status::BadRequest, format!("JSON error: {}", message))),
        Ok(request) => request,
    };

    let response = dispatch_request(&mut application.lock().unwrap(), parsed);

    Response::with((Status::Ok, serde_json::to_string_pretty(&response).unwrap()))
}

fn dispatch_request<G: IdGenerator>(application: &mut Application<G>, request: FindReviewerRequest) -> FindReviewerResponse {
    match request {
        FindReviewerRequest::NeedReviewer { coder } => application.need_reviewer(coder),
        FindReviewerRequest::HaveTimeForReview { reviewer } => application.have_time_for_review(&reviewer),
        FindReviewerRequest::WillReview { review_id } => application.will_review(review_id),
        FindReviewerRequest::WontReview { review_id } => application.wont_review(review_id),
    }
}

use std::collections::hash_map::DefaultHasher;
use std::hash::BuildHasherDefault;

struct Application<G> {
    configuration: ApplicationConfiguration,
    waiting_coders: HashSet<String, BuildHasherDefault<DefaultHasher>>,
    active_reviews: HashMap<usize, Review>,
    id_generator: G,
}

#[derive(Serialize, Deserialize)]
struct ApplicationConfiguration {
    timeout: Duration,
    wip_limit: usize,
}

impl Default for ApplicationConfiguration {
    fn default() -> Self {
        ApplicationConfiguration {
            timeout: Duration::from_secs(30),
            wip_limit: 5,
        }
    }
}

#[derive(Clone)]
struct Review {
    pub coder: String,
    pub enqueued_coder: Option<String>,
    pub started: Instant,
}

impl<G: IdGenerator> Application<G> {
    fn new(configuration: ApplicationConfiguration) -> Application<G> {
        Application {
            configuration,
            waiting_coders: HashSet::default(),
            active_reviews: HashMap::default(),
            id_generator: G::new(),
        }
    }

    fn need_reviewer(&mut self, incoming_coder: String) -> FindReviewerResponse {
        if self.is_already_registered(&incoming_coder) {
            FindReviewerResponse::AlreadyRegistered {}
        } else if self.waiting_coders.len() >= self.configuration.wip_limit {
            let random_waiting_coder = self.waiting_coders.iter().next().unwrap().clone();
            self.start_review(random_waiting_coder, Some(incoming_coder))
        } else {
            self.waiting_coders.insert(incoming_coder);
            FindReviewerResponse::Accepted {}
        }
    }

    fn is_already_registered(&self, coder: &String) -> bool {
        self.waiting_coders.contains(coder) ||
            self.active_reviews
                .values()
                .any(|review| &review.coder == coder || review.enqueued_coder.as_ref() == Some(coder))
    }

    fn have_time_for_review(&mut self, incoming_reviewer: &String) -> FindReviewerResponse {
        let random_coder_except_incoming_reviewer = self.waiting_coders
            .iter()
            .filter(|&coder| coder != incoming_reviewer)
            .next()
            .cloned();
        match random_coder_except_incoming_reviewer {
            Some(coder) => self.start_review(coder, None),
            None => FindReviewerResponse::NoReviewerNeeded {},
        }
    }

    fn start_review(&mut self, coder: String, enqueued_coder: Option<String>) -> FindReviewerResponse {
        let review = Review {
            coder: coder.clone(),
            enqueued_coder,
            started: Instant::now(),
        };
        self.remove_coder(&coder);
        let review_id = self.insert_review(review);

        FindReviewerResponse::NeedsReviewer { coder, review_id }
    }

    fn generate_id(&mut self) -> usize {
        loop {
            let id = self.id_generator.generate_id();
            if !self.active_reviews.contains_key(&id) {
                return id;
            }
        }
    }

    fn will_review(&mut self, review_id: usize) -> FindReviewerResponse {
        match self.active_reviews.remove(&review_id) {
            Some(review) => {
                review
                    .enqueued_coder
                    .map(|coder| self.waiting_coders.insert(coder));
                FindReviewerResponse::Accepted {}
            }
            None => FindReviewerResponse::ReviewNotFound {},
        }
    }

    fn wont_review(&mut self, review_id: usize) -> FindReviewerResponse {
        match self.active_reviews.remove(&review_id) {
            Some(review) => {
                self.waiting_coders.insert(review.coder);
                FindReviewerResponse::Accepted {}
            }
            None => FindReviewerResponse::ReviewNotFound {},
        }
    }

    fn remove_coder(&mut self, coder: &String) {
        self.waiting_coders.remove(coder);
    }

    fn insert_review(&mut self, review: Review) -> usize {
        let id = self.generate_id();
        self.active_reviews.insert(id, review);
        id
    }

    fn process_timeouts(&mut self) {
        let now = Instant::now();
        let timed_out: Vec<_> = self.active_reviews
            .iter()
            .filter(|&(_, review)| (now - review.started) > self.configuration.timeout)
            .map(|(&id, _)| id)
            .collect();
        for review_id in timed_out {
            self.wont_review(review_id);
        }
    }
}

trait IdGenerator {
    fn generate_id(&mut self) -> usize;

    fn new() -> Self;
}

struct RandomIdGenerator;

impl IdGenerator for RandomIdGenerator {
    fn generate_id(&mut self) -> usize {
        rand::thread_rng().gen_range(0, 16383)
    }

    fn new() -> Self {
        RandomIdGenerator
    }
}

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

    struct SequenceIdGenerator {
        id: usize,
    }

    impl IdGenerator for SequenceIdGenerator {
        fn generate_id(&mut self) -> usize {
            self.id += 1;
            self.id
        }

        fn new() -> Self {
            SequenceIdGenerator { id: 0 }
        }
    }

    #[test]
    fn add_coder_no_more_than_once() {
        let mut app = create_application();

        let resp = app.need_reviewer("coder1".to_owned());
        assert_eq!(resp, FindReviewerResponse::Accepted {});

        let resp = app.need_reviewer("coder1".to_owned());
        assert_eq!(resp, FindReviewerResponse::AlreadyRegistered {});

        let resp = app.need_reviewer("coder2".to_owned());
        assert_eq!(resp, FindReviewerResponse::Accepted {});

        let resp = app.need_reviewer("coder3".to_owned());
        assert_eq!(resp, FindReviewerResponse::Accepted {});

        let resp = app.need_reviewer("coder2".to_owned());
        assert_eq!(resp, FindReviewerResponse::AlreadyRegistered {});

        let resp = app.need_reviewer("coder3".to_owned());
        assert_eq!(resp, FindReviewerResponse::AlreadyRegistered {});
    }

    #[test]
    fn respect_wip_limit() {
        let mut application = create_application();
        let coder_set = (0..5).map(|x| format!("coder{}", x) ).collect::<HashSet<_>>();
        let coder_set_cloned = coder_set.clone();
        for coder in coder_set {
            let review_request = FindReviewerRequest::NeedReviewer {
                coder,
            };
            assert_eq!(dispatch_request(&mut application, review_request), FindReviewerResponse::Accepted {});
        }
        let answer = dispatch_request(
            &mut application,
            FindReviewerRequest::NeedReviewer {
                coder: format!("anothercoder"),
            },
        );
        match answer{
            FindReviewerResponse::NeedsReviewer {coder: coder_name,review_id: _} =>
                assert!(coder_set_cloned.iter().any(|x| *x==coder_name)),
            _ => panic!(),
        }
    }

    fn create_application() -> Application<SequenceIdGenerator> {
        Application::new(ApplicationConfiguration::default())
    }
}
<commit_msg>Revert idea of deterministic IDs and hashes<commit_after>extern crate iron;
extern crate mount;
extern crate rand;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate staticfile;

use iron::method::Method;
use iron::prelude::*;
use iron::status::Status;
use mount::Mount;
use rand::Rng;
use staticfile::Static;
use std::collections::HashMap;
use std::collections::HashSet;
use std::fs::File;
use std::io::Read;
use std::path::Path;
use std::sync::Arc;
use std::sync::Mutex;
use std::thread;
use std::time::Duration;
use std::time::Instant;

static CONFIG_FILE_NAME: &str = "find-reviewer.json";

#[derive(Debug, Deserialize, Eq, PartialEq)]
enum FindReviewerRequest {
    NeedReviewer { coder: String },
    HaveTimeForReview { reviewer: String },
    WillReview { review_id: u32 },
    WontReview { review_id: u32 },
}

#[derive(Debug, Eq, PartialEq, Serialize)]
enum FindReviewerResponse {
    Accepted {},
    NoReviewerNeeded {},
    AlreadyRegistered {},
    NeedsReviewer { coder: String, review_id: u32 },
    ReviewNotFound {},
}

type SharedApplication = Arc<Mutex<Application>>;

fn main() {
    let configuration = load_configuration();
    let application = SharedApplication::new(Mutex::new(Application::new(configuration)));

    let mut mount = Mount::new();
    mount
        .mount("/find-reviewer", {
            let application = application.clone();
            move |request: &mut Request| Ok(process_request(request, &application))
        })
        .mount("/", Static::new(Path::new("www")));

    thread::spawn(move || loop {
        thread::sleep(Duration::from_secs(1));
        application.lock().unwrap().process_timeouts();
    });

    Iron::new(mount).http("localhost:3000").unwrap();
}

fn load_configuration() -> ApplicationConfiguration {
    let config = File::open(CONFIG_FILE_NAME)
        .map(|open_file| serde_json::from_reader(open_file).expect(&format!("Could not parse {}", CONFIG_FILE_NAME)))
        .unwrap_or_else(|err| {
            println!("Could not read {}: {}\nFile will be created", CONFIG_FILE_NAME, err);
            ApplicationConfiguration::default()
        });

    File::create(CONFIG_FILE_NAME)
        .map(|created_file| {
            serde_json::to_writer_pretty(created_file, &config).expect(&format!("Could not serialize {}", CONFIG_FILE_NAME))
        })
        .unwrap_or_else(|err| println!("Could not write {}: {}", CONFIG_FILE_NAME, err));

    config
}

fn process_request(request: &mut Request, application: &SharedApplication) -> Response {
    if request.method != Method::Post {
        return Response::with((Status::BadRequest, "Must be a POST request"));
    }

    let parsed = match serde_json::from_reader(request.body.by_ref()) {
        Err(message) => return Response::with((Status::BadRequest, format!("JSON error: {}", message))),
        Ok(request) => request,
    };

    let response = dispatch_request(&mut application.lock().unwrap(), parsed);

    Response::with((Status::Ok, serde_json::to_string_pretty(&response).unwrap()))
}

fn dispatch_request<G: IdGenerator>(application: &mut Application<G>, request: FindReviewerRequest) -> FindReviewerResponse {
    match request {
        FindReviewerRequest::NeedReviewer { coder } => application.need_reviewer(coder),
        FindReviewerRequest::HaveTimeForReview { reviewer } => application.have_time_for_review(&reviewer),
        FindReviewerRequest::WillReview { review_id } => application.will_review(review_id),
        FindReviewerRequest::WontReview { review_id } => application.wont_review(review_id),
    }
}

struct Application {
    configuration: ApplicationConfiguration,
    waiting_coders: HashSet<String>,
    active_reviews: HashMap<u32, Review>,
}

#[derive(Serialize, Deserialize)]
struct ApplicationConfiguration {
    timeout: Duration,
    wip_limit: usize,
}

impl Default for ApplicationConfiguration {
    fn default() -> Self {
        ApplicationConfiguration {
            timeout: Duration::from_secs(30),
            wip_limit: 5,
        }
    }
}

#[derive(Clone)]
struct Review {
    pub coder: String,
    pub enqueued_coder: Option<String>,
    pub started: Instant,
}

impl Application {
    fn new(configuration: ApplicationConfiguration) -> Application {
        Application {
            configuration,
            waiting_coders: HashSet::new(),
            active_reviews: HashMap::new(),
        }
    }

    fn need_reviewer(&mut self, incoming_coder: String) -> FindReviewerResponse {
        if self.is_already_registered(&incoming_coder) {
            FindReviewerResponse::AlreadyRegistered {}
        } else if self.waiting_coders.len() >= self.configuration.wip_limit {
            let random_waiting_coder = self.waiting_coders.iter().next().unwrap().clone();
            self.start_review(random_waiting_coder, Some(incoming_coder))
        } else {
            self.waiting_coders.insert(incoming_coder);
            FindReviewerResponse::Accepted {}
        }
    }

    fn is_already_registered(&self, coder: &String) -> bool {
        self.waiting_coders.contains(coder) ||
            self.active_reviews
                .values()
                .any(|review| &review.coder == coder || review.enqueued_coder.as_ref() == Some(coder))
    }

    fn have_time_for_review(&mut self, incoming_reviewer: &String) -> FindReviewerResponse {
        let random_coder_except_incoming_reviewer = self.waiting_coders
            .iter()
            .filter(|&coder| coder != incoming_reviewer)
            .next()
            .cloned();
        match random_coder_except_incoming_reviewer {
            Some(coder) => self.start_review(coder, None),
            None => FindReviewerResponse::NoReviewerNeeded {},
        }
    }

    fn start_review(&mut self, coder: String, enqueued_coder: Option<String>) -> FindReviewerResponse {
        let review = Review {
            coder: coder.clone(),
            enqueued_coder,
            started: Instant::now(),
        };
        self.remove_coder(&coder);
        let review_id = self.insert_review(review);

        FindReviewerResponse::NeedsReviewer { coder, review_id }
    }

    fn generate_id(&mut self) -> u32 {
        loop {
            let id = rand::thread_rng().gen();
            if !self.active_reviews.contains_key(&id) {
                return id;
            }
        }
    }

    fn will_review(&mut self, review_id: u32) -> FindReviewerResponse {
        match self.active_reviews.remove(&review_id) {
            Some(review) => {
                review
                    .enqueued_coder
                    .map(|coder| self.waiting_coders.insert(coder));
                FindReviewerResponse::Accepted {}
            }
            None => FindReviewerResponse::ReviewNotFound {},
        }
    }

    fn wont_review(&mut self, review_id: u32) -> FindReviewerResponse {
        match self.active_reviews.remove(&review_id) {
            Some(review) => {
                self.waiting_coders.insert(review.coder);
                FindReviewerResponse::Accepted {}
            }
            None => FindReviewerResponse::ReviewNotFound {},
        }
    }

    fn remove_coder(&mut self, coder: &String) {
        self.waiting_coders.remove(coder);
    }

    fn insert_review(&mut self, review: Review) -> u32 {
        let id = self.generate_id();
        self.active_reviews.insert(id, review);
        id
    }

    fn process_timeouts(&mut self) {
        let now = Instant::now();
        let timed_out: Vec<_> = self.active_reviews
            .iter()
            .filter(|&(_, review)| (now - review.started) > self.configuration.timeout)
            .map(|(&id, _)| id)
            .collect();
        for review_id in timed_out {
            self.wont_review(review_id);
        }
    }
}

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

    #[test]
    fn add_coder_no_more_than_once() {
        let mut app = create_application();

        let resp = app.need_reviewer("coder1".to_owned());
        assert_eq!(resp, FindReviewerResponse::Accepted {});

        let resp = app.need_reviewer("coder1".to_owned());
        assert_eq!(resp, FindReviewerResponse::AlreadyRegistered {});

        let resp = app.need_reviewer("coder2".to_owned());
        assert_eq!(resp, FindReviewerResponse::Accepted {});

        let resp = app.need_reviewer("coder3".to_owned());
        assert_eq!(resp, FindReviewerResponse::Accepted {});

        let resp = app.need_reviewer("coder2".to_owned());
        assert_eq!(resp, FindReviewerResponse::AlreadyRegistered {});

        let resp = app.need_reviewer("coder3".to_owned());
        assert_eq!(resp, FindReviewerResponse::AlreadyRegistered {});
    }

    #[test]
    fn respect_wip_limit() {
        let mut application = create_application();
        let coder_set = (0..5).map(|x| format!("coder{}", x) ).collect::<HashSet<_>>();
        let coder_set_cloned = coder_set.clone();
        for coder in coder_set {
            let review_request = FindReviewerRequest::NeedReviewer {
                coder,
            };
            assert_eq!(dispatch_request(&mut application, review_request), FindReviewerResponse::Accepted {});
        }
        let answer = dispatch_request(
            &mut application,
            FindReviewerRequest::NeedReviewer {
                coder: format!("anothercoder"),
            },
        );
        match answer{
            FindReviewerResponse::NeedsReviewer {coder: coder_name,review_id: _} =>
                assert!(coder_set_cloned.iter().any(|x| *x==coder_name)),
            _ => panic!(),
        }
    }

    fn create_application() -> Application {
        Application::new(ApplicationConfiguration::default())
    }
}
<|endoftext|>


<commit_before>//! Module: czmq-zmonitor

use {czmq_sys, Error, ErrorKind, Result, ZActor, ZMsg, ZSock};
use std::{error, fmt, ptr, result};
use std::os::raw::c_void;
use zmsg::ZMsgable;

#[derive(Debug, PartialEq)]
pub enum ZMonitorEvents {
    Connected,
    ConnectDelayed,
    ConnectRetried,
    Listening,
    BindFailed,
    Accepted,
    AcceptFailed,
    Closed,
    CloseFailed,
    Disconnected,
    MonitorStopped,
    All,
    Unknown,
}

impl ZMonitorEvents {
    pub fn to_str<'a>(&'a self) -> &'a str {
        match self {
            &ZMonitorEvents::Connected      => "CONNECTED",
            &ZMonitorEvents::ConnectDelayed => "CONNECT_DELAYED",
            &ZMonitorEvents::ConnectRetried => "CONNECT_RETRIED",
            &ZMonitorEvents::Listening      => "LISTENING",
            &ZMonitorEvents::BindFailed     => "BIND_FAILED",
            &ZMonitorEvents::Accepted       => "ACCEPTED",
            &ZMonitorEvents::AcceptFailed   => "ACCEPT_FAILED",
            &ZMonitorEvents::Closed         => "CLOSED",
            &ZMonitorEvents::CloseFailed    => "CLOSE_FAILED",
            &ZMonitorEvents::Disconnected   => "DISCONNECTED",
            &ZMonitorEvents::MonitorStopped => "MONITOR_STOPPED",
            &ZMonitorEvents::All            => "ALL",
            &ZMonitorEvents::Unknown        => "UNKNOWN",
        }
    }

    pub fn from_str(event: &str) -> ZMonitorEvents {
        match event {
            "CONNECTED"       => ZMonitorEvents::Connected,
            "CONNECT_DELAYED" => ZMonitorEvents::ConnectDelayed,
            "CONNECT_RETRIED" => ZMonitorEvents::ConnectRetried,
            "LISTENING"       => ZMonitorEvents::Listening,
            "BIND_FAILED"     => ZMonitorEvents::BindFailed,
            "ACCEPTED"        => ZMonitorEvents::Accepted,
            "ACCEPT_FAILED"   => ZMonitorEvents::AcceptFailed,
            "CLOSED"          => ZMonitorEvents::Closed,
            "CLOSE_FAILED"    => ZMonitorEvents::CloseFailed,
            "DISCONNECTED"    => ZMonitorEvents::Disconnected,
            "MONITOR_STOPPED" => ZMonitorEvents::MonitorStopped,
            "ALL"             => ZMonitorEvents::All,
            _                 => ZMonitorEvents::Unknown,
        }
    }
}

pub struct ZMonitor {
    zactor: ZActor,
}

unsafe impl Send for ZMonitor {}

impl ZMonitor {
    pub fn new(zsock: &ZSock) -> Result<ZMonitor> {
        let zactor = unsafe { czmq_sys::zactor_new(czmq_sys::zmonitor, zsock.borrow_raw() as *mut c_void) };

        if zactor == ptr::null_mut() {
            Err(Error::new(ErrorKind::NullPtr, ZMonitorError::Instantiate))
        } else {
            Ok(ZMonitor {
                zactor: ZActor::from_raw(zactor),
            })
        }
    }

    pub fn set_attrs(&self, attrs: &[ZMonitorEvents]) -> Result<()> {
        let msg = ZMsg::new();
        try!(msg.addstr("LISTEN"));
        for a in attrs {
            try!(msg.addstr(a.to_str()));
        }

        self.zactor.send(msg)
    }

    pub fn get_attr(&self) -> Result<result::Result<ZMonitorEvents, Vec<u8>>> {
        let msg = try!(ZMsg::recv(&self.zactor));
        match try!(msg.popstr()) {
            Ok(s) => Ok(Ok(ZMonitorEvents::from_str(&s))),
            Err(v) => Ok(Err(v))
        }
    }

    pub fn start(&self) -> Result<()> {
        try!(self.zactor.send_str("START"));
        self.zactor.sock().wait()
    }

    pub fn verbose(&self) -> Result<()> {
        self.zactor.send_str("VERBOSE")
    }
}

#[derive(Debug)]
pub enum ZMonitorError {
    Instantiate,
}

impl fmt::Display for ZMonitorError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ZMonitorError::Instantiate => write!(f, "Could not instantiate new ZMonitor struct"),
        }
    }
}

impl error::Error for ZMonitorError {
    fn description(&self) -> &str {
        match *self {
            ZMonitorError::Instantiate => "Could not instantiate new ZMonitor struct",
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use {ZSock, ZSockType, zsys_init};

    #[test]
    fn test_attrs() {
        zsys_init();

        let server = ZSock::new(ZSockType::PULL);
        let server_mon = ZMonitor::new(&server).unwrap();
        server_mon.set_attrs(&[ZMonitorEvents::All]).unwrap();
        server_mon.start().unwrap();

        let client = ZSock::new(ZSockType::PUSH);
        let client_mon = ZMonitor::new(&client).unwrap();
        client_mon.set_attrs(&[ZMonitorEvents::All]).unwrap();
        client_mon.start().unwrap();

        server.bind("ipc://zmonitor_test").unwrap();
        assert_eq!(server_mon.get_attr().unwrap().unwrap(), ZMonitorEvents::Listening);

        client.connect("ipc://zmonitor_test").unwrap();
        assert_eq!(client_mon.get_attr().unwrap().unwrap(), ZMonitorEvents::Connected);
    }

    #[test]
    fn test_verbose() {
        zsys_init();

        let zsock = ZSock::new(ZSockType::REP);
        let zmonitor = ZMonitor::new(&zsock).unwrap();
        assert!(zmonitor.verbose().is_ok());
    }
}
<commit_msg>Implemented Display trait for ZMonitorEvents<commit_after>//! Module: czmq-zmonitor

use {czmq_sys, Error, ErrorKind, Result, ZActor, ZMsg, ZSock};
use std::{error, ptr, result};
use std::fmt::{Display, Formatter, Result as FmtResult};
use std::os::raw::c_void;
use zmsg::ZMsgable;

#[derive(Debug, PartialEq)]
pub enum ZMonitorEvents {
    Connected,
    ConnectDelayed,
    ConnectRetried,
    Listening,
    BindFailed,
    Accepted,
    AcceptFailed,
    Closed,
    CloseFailed,
    Disconnected,
    MonitorStopped,
    All,
    Unknown,
}

impl ZMonitorEvents {
    pub fn to_str<'a>(&'a self) -> &'a str {
        match self {
            &ZMonitorEvents::Connected      => "CONNECTED",
            &ZMonitorEvents::ConnectDelayed => "CONNECT_DELAYED",
            &ZMonitorEvents::ConnectRetried => "CONNECT_RETRIED",
            &ZMonitorEvents::Listening      => "LISTENING",
            &ZMonitorEvents::BindFailed     => "BIND_FAILED",
            &ZMonitorEvents::Accepted       => "ACCEPTED",
            &ZMonitorEvents::AcceptFailed   => "ACCEPT_FAILED",
            &ZMonitorEvents::Closed         => "CLOSED",
            &ZMonitorEvents::CloseFailed    => "CLOSE_FAILED",
            &ZMonitorEvents::Disconnected   => "DISCONNECTED",
            &ZMonitorEvents::MonitorStopped => "MONITOR_STOPPED",
            &ZMonitorEvents::All            => "ALL",
            &ZMonitorEvents::Unknown        => "UNKNOWN",
        }
    }

    pub fn from_str(event: &str) -> ZMonitorEvents {
        match event {
            "CONNECTED"       => ZMonitorEvents::Connected,
            "CONNECT_DELAYED" => ZMonitorEvents::ConnectDelayed,
            "CONNECT_RETRIED" => ZMonitorEvents::ConnectRetried,
            "LISTENING"       => ZMonitorEvents::Listening,
            "BIND_FAILED"     => ZMonitorEvents::BindFailed,
            "ACCEPTED"        => ZMonitorEvents::Accepted,
            "ACCEPT_FAILED"   => ZMonitorEvents::AcceptFailed,
            "CLOSED"          => ZMonitorEvents::Closed,
            "CLOSE_FAILED"    => ZMonitorEvents::CloseFailed,
            "DISCONNECTED"    => ZMonitorEvents::Disconnected,
            "MONITOR_STOPPED" => ZMonitorEvents::MonitorStopped,
            "ALL"             => ZMonitorEvents::All,
            _                 => ZMonitorEvents::Unknown,
        }
    }
}

impl Display for ZMonitorEvents {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        write!(f, "ZMonitorEvent: {}", self.to_str())
    }
}

pub struct ZMonitor {
    zactor: ZActor,
}

unsafe impl Send for ZMonitor {}

impl ZMonitor {
    pub fn new(zsock: &ZSock) -> Result<ZMonitor> {
        let zactor = unsafe { czmq_sys::zactor_new(czmq_sys::zmonitor, zsock.borrow_raw() as *mut c_void) };

        if zactor == ptr::null_mut() {
            Err(Error::new(ErrorKind::NullPtr, ZMonitorError::Instantiate))
        } else {
            Ok(ZMonitor {
                zactor: ZActor::from_raw(zactor),
            })
        }
    }

    pub fn set_attrs(&self, attrs: &[ZMonitorEvents]) -> Result<()> {
        let msg = ZMsg::new();
        try!(msg.addstr("LISTEN"));
        for a in attrs {
            try!(msg.addstr(a.to_str()));
        }

        self.zactor.send(msg)
    }

    pub fn get_attr(&self) -> Result<result::Result<ZMonitorEvents, Vec<u8>>> {
        let msg = try!(ZMsg::recv(&self.zactor));
        match try!(msg.popstr()) {
            Ok(s) => Ok(Ok(ZMonitorEvents::from_str(&s))),
            Err(v) => Ok(Err(v))
        }
    }

    pub fn start(&self) -> Result<()> {
        try!(self.zactor.send_str("START"));
        self.zactor.sock().wait()
    }

    pub fn verbose(&self) -> Result<()> {
        self.zactor.send_str("VERBOSE")
    }
}

#[derive(Debug)]
pub enum ZMonitorError {
    Instantiate,
}

impl Display for ZMonitorError {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        match *self {
            ZMonitorError::Instantiate => write!(f, "Could not instantiate new ZMonitor struct"),
        }
    }
}

impl error::Error for ZMonitorError {
    fn description(&self) -> &str {
        match *self {
            ZMonitorError::Instantiate => "Could not instantiate new ZMonitor struct",
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use {ZSock, ZSockType, zsys_init};

    #[test]
    fn test_attrs() {
        zsys_init();

        let server = ZSock::new(ZSockType::PULL);
        let server_mon = ZMonitor::new(&server).unwrap();
        server_mon.set_attrs(&[ZMonitorEvents::All]).unwrap();
        server_mon.start().unwrap();

        let client = ZSock::new(ZSockType::PUSH);
        let client_mon = ZMonitor::new(&client).unwrap();
        client_mon.set_attrs(&[ZMonitorEvents::All]).unwrap();
        client_mon.start().unwrap();

        server.bind("ipc://zmonitor_test").unwrap();
        assert_eq!(server_mon.get_attr().unwrap().unwrap(), ZMonitorEvents::Listening);

        client.connect("ipc://zmonitor_test").unwrap();
        assert_eq!(client_mon.get_attr().unwrap().unwrap(), ZMonitorEvents::Connected);
    }

    #[test]
    fn test_verbose() {
        zsys_init();

        let zsock = ZSock::new(ZSockType::REP);
        let zmonitor = ZMonitor::new(&zsock).unwrap();
        assert!(zmonitor.verbose().is_ok());
    }
}
<|endoftext|>


<commit_before>use std;

use std::cmp::PartialOrd;

extern crate num;
use self::num::traits::{Zero, One, FromPrimitive, ToPrimitive, Float};

extern crate nalgebra;
use self::nalgebra::{DMat};

extern crate image;
use self::image::{ImageBuffer, Luma, DynamicImage};

quick_error! {
    #[derive(Debug)]
    pub enum ImageSaveError {
        /// an error has occured when opening the file
        Io(err: std::io::Error) {}
        /// an error has occured when writing the image into the file
        Image(err: image::ImageError) {}
    }
}

/// make images from things containing brightness information
pub trait ToImage {
    /// # Panics
    /// panics unless all the values in `self` are between
    /// `0` (inclusive) and `1` (inclusive)
    fn to_image(&self) -> DynamicImage;
    /// # Panics
    /// panics unless all the values in `self` are between
    /// `0` (inclusive) and `1` (inclusive)
    fn save_to_png(&self, filename: &str) -> Result<(), ImageSaveError>;
}

impl<T: Zero + One + FromPrimitive + ToPrimitive + PartialOrd + Float> ToImage for DMat<T> {
    fn to_image(&self) -> DynamicImage {
        let mut image_buffer = ImageBuffer::new(self.ncols() as u32, self.nrows() as u32);
        for (x, y, pixel) in image_buffer.enumerate_pixels_mut() {
            let value = self[(y as usize, x as usize)];
            assert!(T::zero() <= value);
            assert!(value <= T::one());
            let max = T::from_u8(std::u8::MAX).unwrap();
            let byte = (value * max).round().to_u8().unwrap();
            *pixel = Luma([byte]);
        }
        DynamicImage::ImageLuma8(image_buffer)
    }

    fn save_to_png(&self, filename: &str) -> Result<(), ImageSaveError> {
        let image = self.to_image();
        let path = std::path::Path::new(filename);
        let mut file = try!(std::fs::File::create(&path).map_err(ImageSaveError::Io));
        image.save(&mut file, image::PNG).map_err(ImageSaveError::Image)
    }
}
<commit_msg>expose clearer that the error is that numbers are NaN<commit_after>use std;

use std::cmp::PartialOrd;

extern crate num;
use self::num::traits::{Zero, One, FromPrimitive, ToPrimitive, Float};

extern crate nalgebra;
use self::nalgebra::{DMat};

extern crate image;
use self::image::{ImageBuffer, Luma, DynamicImage};

quick_error! {
    #[derive(Debug)]
    pub enum ImageSaveError {
        /// an error has occured when opening the file
        Io(err: std::io::Error) {}
        /// an error has occured when writing the image into the file
        Image(err: image::ImageError) {}
    }
}

/// make images from things containing brightness information
pub trait ToImage {
    /// # Panics
    /// panics unless all the values in `self` are between
    /// `0` (inclusive) and `1` (inclusive)
    fn to_image(&self) -> DynamicImage;
    /// # Panics
    /// panics unless all the values in `self` are between
    /// `0` (inclusive) and `1` (inclusive)
    fn save_to_png(&self, filename: &str) -> Result<(), ImageSaveError>;
}

impl<T: Zero + One + FromPrimitive + ToPrimitive + PartialOrd + Float> ToImage for DMat<T> {
    fn to_image(&self) -> DynamicImage {
        let mut image_buffer = ImageBuffer::new(self.ncols() as u32, self.nrows() as u32);
        for (x, y, pixel) in image_buffer.enumerate_pixels_mut() {
            let value = self[(y as usize, x as usize)];
            assert!(value.is_nan());
            assert!(T::zero() <= value);
            assert!(value <= T::one());
            let max = T::from_u8(std::u8::MAX).unwrap();
            let byte = (value * max).round().to_u8().unwrap();
            *pixel = Luma([byte]);
        }
        DynamicImage::ImageLuma8(image_buffer)
    }

    fn save_to_png(&self, filename: &str) -> Result<(), ImageSaveError> {
        let image = self.to_image();
        let path = std::path::Path::new(filename);
        let mut file = try!(std::fs::File::create(&path).map_err(ImageSaveError::Io));
        image.save(&mut file, image::PNG).map_err(ImageSaveError::Image)
    }
}
<|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 geo;
use geojson;
use heck::SnakeCase;
use mimir;
use rs_es::error::EsError;
use std::sync::Arc;

#[derive(Fail, Debug)]
pub enum BragiError {
    #[fail(display = "Unable to find object")]
    ObjectNotFound,
    #[fail(display = "Impossible to find object")]
    IndexNotFound,
    #[fail(display = "invalid query {}", _0)]
    Es(EsError),
}

impl From<EsError> for BragiError {
    fn from(e: EsError) -> Self {
        BragiError::Es(e)
    }
}

#[derive(Serialize, Deserialize, Debug)]
pub struct Geocoding {
    version: String,
    query: Option<String>,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct Feature {
    #[serde(rename = "type")]
    pub feature_type: String,
    pub geometry: geojson::Geometry,
    pub properties: Properties,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct Properties {
    pub geocoding: GeocodingResponse,
}

#[derive(Serialize, Deserialize, Debug, Default)]
pub struct GeocodingResponse {
    pub id: String,
    #[serde(rename = "type")]
    pub place_type: String, // FIXME: use an enum?
    pub label: Option<String>,
    pub name: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub housenumber: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub street: Option<String>,
    pub postcode: Option<String>,
    pub city: Option<String>,
    pub citycode: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub level: Option<u32>,
    // pub accuracy: Option<i32>,
    // pub district: Option<String>,
    // pub county: Option<String>,
    // pub state: Option<String>,
    // pub country: Option<String>,
    // pub geohash: Option<String>,
    pub administrative_regions: Vec<Arc<mimir::Admin>>,
    #[serde(skip_serializing_if = "Vec::is_empty", default)]
    pub poi_types: Vec<mimir::PoiType>,
    #[serde(skip_serializing_if = "Vec::is_empty", default)]
    pub properties: Vec<mimir::Property>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub address: Option<Box<GeocodingResponse>>,
    #[serde(skip_serializing_if = "Vec::is_empty", default)]
    pub commercial_modes: Vec<mimir::CommercialMode>,
    #[serde(skip_serializing_if = "Vec::is_empty", default)]
    pub comments: Vec<mimir::Comment>,
    #[serde(skip_serializing_if = "Vec::is_empty", default)]
    pub physical_modes: Vec<mimir::PhysicalMode>,
    #[serde(skip_serializing_if = "Option::is_none", default)]
    pub timezone: Option<String>,
    #[serde(skip_serializing_if = "Vec::is_empty", default)]
    pub codes: Vec<mimir::Code>,
    #[serde(skip_serializing_if = "Vec::is_empty", default)]
    pub feed_publishers: Vec<mimir::FeedPublisher>,
}

trait ToGeom {
    fn to_geom(&self) -> geojson::Geometry;
}

impl ToGeom for mimir::Place {
    fn to_geom(&self) -> geojson::Geometry {
        match self {
            &mimir::Place::Admin(ref admin) => admin.coord.to_geom(),
            &mimir::Place::Street(ref street) => street.coord.to_geom(),
            &mimir::Place::Addr(ref addr) => addr.coord.to_geom(),
            &mimir::Place::Poi(ref poi) => poi.coord.to_geom(),
            &mimir::Place::Stop(ref stop) => stop.coord.to_geom(),
        }
    }
}

impl ToGeom for geo::Coordinate<f64> {
    fn to_geom(&self) -> geojson::Geometry {
        geojson::Geometry::new(geojson::Value::Point(vec![self.x, self.y]))
    }
}

impl From<mimir::Place> for Feature {
    fn from(other: mimir::Place) -> Feature {
        let geom = other.to_geom();
        let geocoding = match other {
            mimir::Place::Admin(admin) => GeocodingResponse::from(admin),
            mimir::Place::Street(street) => GeocodingResponse::from(street),
            mimir::Place::Addr(addr) => GeocodingResponse::from(addr),
            mimir::Place::Poi(poi) => GeocodingResponse::from(poi),
            mimir::Place::Stop(poi) => GeocodingResponse::from(poi),
        };
        Feature {
            feature_type: "Feature".to_string(),
            geometry: geom,
            properties: Properties {
                geocoding: geocoding,
            },
        }
    }
}

impl From<mimir::Admin> for GeocodingResponse {
    fn from(other: mimir::Admin) -> GeocodingResponse {
        let type_ = get_admin_type(&other);
        let name = Some(other.name);
        let insee = Some(other.insee);
        let level = Some(other.level); //might be used for type_ and become useless
        let postcode = if other.zip_codes.is_empty() {
            None
        } else {
            Some(other.zip_codes.join(";"))
        };
        let label = Some(other.label);
        GeocodingResponse {
            id: other.id,
            citycode: insee,
            level: level,
            place_type: type_,
            name: name,
            postcode: postcode,
            label: label,
            ..Default::default()
        }
    }
}

fn get_admin_type(adm: &mimir::Admin) -> String {
    match adm.zone_type {
        Some(t) => format!("{:?}", t).to_snake_case(),
        // TODO below return administrative_region when admin_type is removed
        None => adm.admin_type.to_string(),
    }
}

fn get_city_name(admins: &Vec<Arc<mimir::Admin>>) -> Option<String> {
    admins
        .iter()
        .find(|a| a.is_city())
        .map(|admin| admin.name.clone())
}

fn get_citycode(admins: &Vec<Arc<mimir::Admin>>) -> Option<String> {
    admins
        .iter()
        .find(|a| a.is_city())
        .map(|admin| admin.insee.clone())
}

impl From<mimir::Street> for GeocodingResponse {
    fn from(other: mimir::Street) -> GeocodingResponse {
        let type_ = "street".to_string();
        let name = Some(other.name);
        let label = Some(other.label);
        let admins = other.administrative_regions;
        let city = get_city_name(&admins);
        let postcode = if other.zip_codes.is_empty() {
            None
        } else {
            Some(other.zip_codes.join(";"))
        };
        let citycode = get_citycode(&admins);

        GeocodingResponse {
            id: other.id,
            citycode: citycode,
            place_type: type_,
            name: name.clone(),
            postcode: postcode,
            label: label,
            street: name,
            city: city,
            administrative_regions: admins,
            ..Default::default()
        }
    }
}

impl From<mimir::Addr> for GeocodingResponse {
    fn from(other: mimir::Addr) -> GeocodingResponse {
        let type_ = "house".to_string();
        let label = Some(other.label);
        let housenumber = Some(other.house_number.to_string());
        let street_name = Some(other.street.name.to_string());
        let name = Some(format!("{} {}", other.house_number, other.street.name));
        let admins = other.street.administrative_regions;
        let city = get_city_name(&admins);
        let postcode = if other.zip_codes.is_empty() {
            None
        } else {
            Some(other.zip_codes.join(";"))
        };
        let citycode = get_citycode(&admins);

        GeocodingResponse {
            id: other.id,
            citycode: citycode,
            place_type: type_,
            name: name,
            postcode: postcode,
            label: label,
            housenumber: housenumber,
            street: street_name,
            city: city,
            administrative_regions: admins,
            ..Default::default()
        }
    }
}

impl From<mimir::Poi> for GeocodingResponse {
    fn from(other: mimir::Poi) -> GeocodingResponse {
        let type_ = "poi".to_string();
        let label = Some(other.label);
        let name = Some(other.name);
        let admins = other.administrative_regions;
        let city = get_city_name(&admins);
        let postcode = if other.zip_codes.is_empty() {
            None
        } else {
            Some(other.zip_codes.join(";"))
        };
        let citycode = get_citycode(&admins);

        GeocodingResponse {
            id: other.id,
            citycode: citycode,
            place_type: type_,
            name: name,
            postcode: postcode,
            label: label,
            city: city,
            administrative_regions: admins,
            poi_types: vec![other.poi_type],
            properties: other.properties,
            address: match other.address {
                Some(mimir::Address::Addr(addr)) => Some(Box::new(GeocodingResponse::from(addr))),
                Some(mimir::Address::Street(street)) => {
                    Some(Box::new(GeocodingResponse::from(street)))
                }
                _ => None,
            },
            ..Default::default()
        }
    }
}

impl From<mimir::Stop> for GeocodingResponse {
    fn from(other: mimir::Stop) -> GeocodingResponse {
        let type_ = "public_transport:stop_area".to_string();
        let label = Some(other.label);
        let name = Some(other.name);
        let admins = other.administrative_regions;
        let city = get_city_name(&admins);
        let postcode = if other.zip_codes.is_empty() {
            None
        } else {
            Some(other.zip_codes.join(";"))
        };
        let citycode = get_citycode(&admins);

        GeocodingResponse {
            id: other.id,
            citycode: citycode,
            place_type: type_,
            name: name,
            postcode: postcode,
            label: label,
            city: city,
            administrative_regions: admins,
            commercial_modes: other.commercial_modes,
            physical_modes: other.physical_modes,
            comments: other.comments,
            timezone: Some(other.timezone),
            codes: other.codes,
            properties: other.properties,
            feed_publishers: other.feed_publishers,
            ..Default::default()
        }
    }
}

#[derive(Serialize, Deserialize, Debug)]
pub struct Autocomplete {
    #[serde(rename = "type")]
    format_type: String,
    geocoding: Geocoding,
    features: Vec<Feature>,
}

impl Autocomplete {
    pub fn new(q: String, features: Vec<Feature>) -> Autocomplete {
        // TODO couldn't we mode this function ? in Autocomplete ?
        Autocomplete {
            format_type: "FeatureCollection".to_string(),
            geocoding: Geocoding {
                version: "0.1.0".to_string(),
                query: Some(q),
            },
            features: features,
        }
    }
}

impl From<Vec<mimir::Place>> for Autocomplete {
    fn from(places: Vec<mimir::Place>) -> Autocomplete {
        Autocomplete::new(
            "".to_string(),
            places.into_iter().map(|p| Feature::from(p)).collect(),
        )
    }
}

#[derive(Serialize, Deserialize, Debug)]
pub struct Coord {
    pub lat: f64,
    pub lon: f64,
}

pub mod v1 {
    use super::BragiError;
    use iron;
    use mimir;
    use rs_es;

    pub trait HasStatus {
        fn status(&self) -> iron::status::Status;
    }

    // Note: I think this should be in api.rs but with the serde stuff it's easier for all
    // serde struct to be in the same file

    #[derive(Serialize, Deserialize, Debug)]
    pub struct EndPoint {
        pub description: String,
    }

    impl HasStatus for EndPoint {
        fn status(&self) -> iron::status::Status {
            default_status()
        }
    }

    #[derive(Serialize, Deserialize, Debug)]
    pub struct CustomError {
        pub short: String,
        pub long: String,
        #[serde(skip, default = "default_status")]
        pub status: iron::status::Status,
    }

    fn default_status() -> iron::status::Status {
        iron::status::Status::Ok
    }

    #[derive(Debug)]
    pub enum AutocompleteResponse {
        Error(CustomError),
        Autocomplete(super::Autocomplete),
    }

    impl HasStatus for AutocompleteResponse {
        fn status(&self) -> iron::status::Status {
            match self {
                AutocompleteResponse::Error(e) => e.status,
                AutocompleteResponse::Autocomplete(_) => iron::status::Status::Ok,
            }
        }
    }

    use serde;
    impl serde::Serialize for AutocompleteResponse {
        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where
            S: serde::Serializer,
        {
            match self {
                &AutocompleteResponse::Autocomplete(ref a) => serializer.serialize_some(a),
                &AutocompleteResponse::Error(ref e) => serializer.serialize_some(e),
            }
        }
    }

    #[derive(Serialize, Deserialize, Debug)]
    pub struct Status {
        pub version: String,
        pub es: String,
        pub status: String,
    }

    impl HasStatus for Status {
        fn status(&self) -> iron::status::Status {
            iron::status::Status::Ok
        }
    }

    impl From<Result<Vec<mimir::Place>, BragiError>> for AutocompleteResponse {
        fn from(r: Result<Vec<mimir::Place>, BragiError>) -> AutocompleteResponse {
            match r {
                Ok(places) => AutocompleteResponse::Autocomplete(super::Autocomplete::from(places)),
                Err(e) => AutocompleteResponse::Error(CustomError {
                    short: "query error".to_string(),
                    long: format!("{}", e),
                    status: match e {
                        BragiError::ObjectNotFound => iron::status::Status::NotFound,
                        BragiError::IndexNotFound => iron::status::Status::NotFound,
                        BragiError::Es(es_error) => match es_error {
                            rs_es::error::EsError::HttpError(_) => {
                                iron::status::Status::ServiceUnavailable
                            }
                            _ => iron::status::Status::InternalServerError,
                        },
                    },
                }),
            }
        }
    }
}
<commit_msg>correction after review<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 geo;
use geojson;
use heck::SnakeCase;
use mimir;
use rs_es::error::EsError;
use std::sync::Arc;

#[derive(Fail, Debug)]
pub enum BragiError {
    #[fail(display = "Unable to find object")]
    ObjectNotFound,
    #[fail(display = "Impossible to find object")]
    IndexNotFound,
    #[fail(display = "invalid query {}", _0)]
    Es(EsError),
}

impl From<EsError> for BragiError {
    fn from(e: EsError) -> Self {
        BragiError::Es(e)
    }
}

#[derive(Serialize, Deserialize, Debug)]
pub struct Geocoding {
    version: String,
    query: Option<String>,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct Feature {
    #[serde(rename = "type")]
    pub feature_type: String,
    pub geometry: geojson::Geometry,
    pub properties: Properties,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct Properties {
    pub geocoding: GeocodingResponse,
}

#[derive(Serialize, Deserialize, Debug, Default)]
pub struct GeocodingResponse {
    pub id: String,
    #[serde(rename = "type")]
    pub place_type: String, // FIXME: use an enum?
    pub label: Option<String>,
    pub name: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub housenumber: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub street: Option<String>,
    pub postcode: Option<String>,
    pub city: Option<String>,
    pub citycode: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub level: Option<u32>,
    // pub accuracy: Option<i32>,
    // pub district: Option<String>,
    // pub county: Option<String>,
    // pub state: Option<String>,
    // pub country: Option<String>,
    // pub geohash: Option<String>,
    pub administrative_regions: Vec<Arc<mimir::Admin>>,
    #[serde(skip_serializing_if = "Vec::is_empty", default)]
    pub poi_types: Vec<mimir::PoiType>,
    #[serde(skip_serializing_if = "Vec::is_empty", default)]
    pub properties: Vec<mimir::Property>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub address: Option<Box<GeocodingResponse>>,
    #[serde(skip_serializing_if = "Vec::is_empty", default)]
    pub commercial_modes: Vec<mimir::CommercialMode>,
    #[serde(skip_serializing_if = "Vec::is_empty", default)]
    pub comments: Vec<mimir::Comment>,
    #[serde(skip_serializing_if = "Vec::is_empty", default)]
    pub physical_modes: Vec<mimir::PhysicalMode>,
    #[serde(skip_serializing_if = "Option::is_none", default)]
    pub timezone: Option<String>,
    #[serde(skip_serializing_if = "Vec::is_empty", default)]
    pub codes: Vec<mimir::Code>,
    #[serde(skip_serializing_if = "Vec::is_empty", default)]
    pub feed_publishers: Vec<mimir::FeedPublisher>,
}

trait ToGeom {
    fn to_geom(&self) -> geojson::Geometry;
}

impl ToGeom for mimir::Place {
    fn to_geom(&self) -> geojson::Geometry {
        match self {
            &mimir::Place::Admin(ref admin) => admin.coord.to_geom(),
            &mimir::Place::Street(ref street) => street.coord.to_geom(),
            &mimir::Place::Addr(ref addr) => addr.coord.to_geom(),
            &mimir::Place::Poi(ref poi) => poi.coord.to_geom(),
            &mimir::Place::Stop(ref stop) => stop.coord.to_geom(),
        }
    }
}

impl ToGeom for geo::Coordinate<f64> {
    fn to_geom(&self) -> geojson::Geometry {
        geojson::Geometry::new(geojson::Value::Point(vec![self.x, self.y]))
    }
}

impl From<mimir::Place> for Feature {
    fn from(other: mimir::Place) -> Feature {
        let geom = other.to_geom();
        let geocoding = match other {
            mimir::Place::Admin(admin) => GeocodingResponse::from(admin),
            mimir::Place::Street(street) => GeocodingResponse::from(street),
            mimir::Place::Addr(addr) => GeocodingResponse::from(addr),
            mimir::Place::Poi(poi) => GeocodingResponse::from(poi),
            mimir::Place::Stop(poi) => GeocodingResponse::from(poi),
        };
        Feature {
            feature_type: "Feature".to_string(),
            geometry: geom,
            properties: Properties {
                geocoding: geocoding,
            },
        }
    }
}

impl From<mimir::Admin> for GeocodingResponse {
    fn from(other: mimir::Admin) -> GeocodingResponse {
        let type_ = get_admin_type(&other);
        let name = Some(other.name);
        let insee = Some(other.insee);
        let level = Some(other.level); //might be used for type_ and become useless
        let postcode = if other.zip_codes.is_empty() {
            None
        } else {
            Some(other.zip_codes.join(";"))
        };
        let label = Some(other.label);
        GeocodingResponse {
            id: other.id,
            citycode: insee,
            level: level,
            place_type: type_,
            name: name,
            postcode: postcode,
            label: label,
            ..Default::default()
        }
    }
}

fn get_admin_type(adm: &mimir::Admin) -> String {
    match adm.zone_type {
        Some(t) => format!("{:?}", t).to_snake_case(),
        // TODO below return administrative_region when admin_type is removed
        None => adm.admin_type.to_string(),
    }
}

fn get_city_name(admins: &Vec<Arc<mimir::Admin>>) -> Option<String> {
    admins
        .iter()
        .find(|a| a.is_city())
        .map(|admin| admin.name.clone())
}

fn get_citycode(admins: &Vec<Arc<mimir::Admin>>) -> Option<String> {
    admins
        .iter()
        .find(|a| a.is_city())
        .map(|admin| admin.insee.clone())
}

impl From<mimir::Street> for GeocodingResponse {
    fn from(other: mimir::Street) -> GeocodingResponse {
        let type_ = "street".to_string();
        let name = Some(other.name);
        let label = Some(other.label);
        let admins = other.administrative_regions;
        let city = get_city_name(&admins);
        let postcode = if other.zip_codes.is_empty() {
            None
        } else {
            Some(other.zip_codes.join(";"))
        };
        let citycode = get_citycode(&admins);

        GeocodingResponse {
            id: other.id,
            citycode: citycode,
            place_type: type_,
            name: name.clone(),
            postcode: postcode,
            label: label,
            street: name,
            city: city,
            administrative_regions: admins,
            ..Default::default()
        }
    }
}

impl From<mimir::Addr> for GeocodingResponse {
    fn from(other: mimir::Addr) -> GeocodingResponse {
        let type_ = "house".to_string();
        let label = Some(other.label);
        let housenumber = Some(other.house_number.to_string());
        let street_name = Some(other.street.name.to_string());
        let name = Some(other.name.to_string());
        let admins = other.street.administrative_regions;
        let city = get_city_name(&admins);
        let postcode = if other.zip_codes.is_empty() {
            None
        } else {
            Some(other.zip_codes.join(";"))
        };
        let citycode = get_citycode(&admins);

        GeocodingResponse {
            id: other.id,
            citycode: citycode,
            place_type: type_,
            name: name,
            postcode: postcode,
            label: label,
            housenumber: housenumber,
            street: street_name,
            city: city,
            administrative_regions: admins,
            ..Default::default()
        }
    }
}

impl From<mimir::Poi> for GeocodingResponse {
    fn from(other: mimir::Poi) -> GeocodingResponse {
        let type_ = "poi".to_string();
        let label = Some(other.label);
        let name = Some(other.name);
        let admins = other.administrative_regions;
        let city = get_city_name(&admins);
        let postcode = if other.zip_codes.is_empty() {
            None
        } else {
            Some(other.zip_codes.join(";"))
        };
        let citycode = get_citycode(&admins);

        GeocodingResponse {
            id: other.id,
            citycode: citycode,
            place_type: type_,
            name: name,
            postcode: postcode,
            label: label,
            city: city,
            administrative_regions: admins,
            poi_types: vec![other.poi_type],
            properties: other.properties,
            address: match other.address {
                Some(mimir::Address::Addr(addr)) => Some(Box::new(GeocodingResponse::from(addr))),
                Some(mimir::Address::Street(street)) => {
                    Some(Box::new(GeocodingResponse::from(street)))
                }
                _ => None,
            },
            ..Default::default()
        }
    }
}

impl From<mimir::Stop> for GeocodingResponse {
    fn from(other: mimir::Stop) -> GeocodingResponse {
        let type_ = "public_transport:stop_area".to_string();
        let label = Some(other.label);
        let name = Some(other.name);
        let admins = other.administrative_regions;
        let city = get_city_name(&admins);
        let postcode = if other.zip_codes.is_empty() {
            None
        } else {
            Some(other.zip_codes.join(";"))
        };
        let citycode = get_citycode(&admins);

        GeocodingResponse {
            id: other.id,
            citycode: citycode,
            place_type: type_,
            name: name,
            postcode: postcode,
            label: label,
            city: city,
            administrative_regions: admins,
            commercial_modes: other.commercial_modes,
            physical_modes: other.physical_modes,
            comments: other.comments,
            timezone: Some(other.timezone),
            codes: other.codes,
            properties: other.properties,
            feed_publishers: other.feed_publishers,
            ..Default::default()
        }
    }
}

#[derive(Serialize, Deserialize, Debug)]
pub struct Autocomplete {
    #[serde(rename = "type")]
    format_type: String,
    geocoding: Geocoding,
    features: Vec<Feature>,
}

impl Autocomplete {
    pub fn new(q: String, features: Vec<Feature>) -> Autocomplete {
        // TODO couldn't we mode this function ? in Autocomplete ?
        Autocomplete {
            format_type: "FeatureCollection".to_string(),
            geocoding: Geocoding {
                version: "0.1.0".to_string(),
                query: Some(q),
            },
            features: features,
        }
    }
}

impl From<Vec<mimir::Place>> for Autocomplete {
    fn from(places: Vec<mimir::Place>) -> Autocomplete {
        Autocomplete::new(
            "".to_string(),
            places.into_iter().map(|p| Feature::from(p)).collect(),
        )
    }
}

#[derive(Serialize, Deserialize, Debug)]
pub struct Coord {
    pub lat: f64,
    pub lon: f64,
}

pub mod v1 {
    use super::BragiError;
    use iron;
    use mimir;
    use rs_es;

    pub trait HasStatus {
        fn status(&self) -> iron::status::Status;
    }

    // Note: I think this should be in api.rs but with the serde stuff it's easier for all
    // serde struct to be in the same file

    #[derive(Serialize, Deserialize, Debug)]
    pub struct EndPoint {
        pub description: String,
    }

    impl HasStatus for EndPoint {
        fn status(&self) -> iron::status::Status {
            default_status()
        }
    }

    #[derive(Serialize, Deserialize, Debug)]
    pub struct CustomError {
        pub short: String,
        pub long: String,
        #[serde(skip, default = "default_status")]
        pub status: iron::status::Status,
    }

    fn default_status() -> iron::status::Status {
        iron::status::Status::Ok
    }

    #[derive(Debug)]
    pub enum AutocompleteResponse {
        Error(CustomError),
        Autocomplete(super::Autocomplete),
    }

    impl HasStatus for AutocompleteResponse {
        fn status(&self) -> iron::status::Status {
            match self {
                AutocompleteResponse::Error(e) => e.status,
                AutocompleteResponse::Autocomplete(_) => iron::status::Status::Ok,
            }
        }
    }

    use serde;
    impl serde::Serialize for AutocompleteResponse {
        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where
            S: serde::Serializer,
        {
            match self {
                &AutocompleteResponse::Autocomplete(ref a) => serializer.serialize_some(a),
                &AutocompleteResponse::Error(ref e) => serializer.serialize_some(e),
            }
        }
    }

    #[derive(Serialize, Deserialize, Debug)]
    pub struct Status {
        pub version: String,
        pub es: String,
        pub status: String,
    }

    impl HasStatus for Status {
        fn status(&self) -> iron::status::Status {
            iron::status::Status::Ok
        }
    }

    impl From<Result<Vec<mimir::Place>, BragiError>> for AutocompleteResponse {
        fn from(r: Result<Vec<mimir::Place>, BragiError>) -> AutocompleteResponse {
            match r {
                Ok(places) => AutocompleteResponse::Autocomplete(super::Autocomplete::from(places)),
                Err(e) => AutocompleteResponse::Error(CustomError {
                    short: "query error".to_string(),
                    long: format!("{}", e),
                    status: match e {
                        BragiError::ObjectNotFound => iron::status::Status::NotFound,
                        BragiError::IndexNotFound => iron::status::Status::NotFound,
                        BragiError::Es(es_error) => match es_error {
                            rs_es::error::EsError::HttpError(_) => {
                                iron::status::Status::ServiceUnavailable
                            }
                            _ => iron::status::Status::InternalServerError,
                        },
                    },
                }),
            }
        }
    }
}
<|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 mimir;
use serde_json;

fn build_rs_client(cnx: &String) -> rs_es::Client {
    let re = regex::Regex::new(r"(?:https?://)?(?P<host>.+?):(?P<port>\d+)/(?P<index>\w+)")
                 .unwrap();
    let cap = re.captures(&cnx).unwrap();
    let host = cap.name("host").unwrap();
    let port = cap.name("port").unwrap().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
fn make_place(doc_type: String, value: Option<Box<serde_json::Value>>) -> Option<mimir::Place> {
    value.and_then(|v| {
        match doc_type.as_ref() {
            "addr" => serde_json::from_value::<mimir::Addr>(*v).ok().and_then(|o| Some(mimir::Place::Addr(o))),
            "street" => serde_json::from_value::<mimir::Street>(*v).ok().and_then(|o| Some(mimir::Place::Street(o))),
            "admin" => serde_json::from_value::<mimir::Admin>(*v).ok().and_then(|o| Some(mimir::Place::Admin(o))),
            _    => {
                warn!("unknown ES return value, _type field = {}", doc_type);
                None
            }
        }
    })
}

fn query(q: &String, cnx: &String, match_type: &str) -> Result<Vec<mimir::Place>, rs_es::error::EsError> {
    let sub_query = rs_q::build_bool()
                        .with_should(vec![
                       rs_q::build_term("_type","addr").with_boost(1000).build(),
                       rs_q::build_match(match_type, q.to_string())
                              .with_boost(100)
                              .build(),
                       rs_q::build_function_score()
                              .with_boost_mode(rs_es::query::compound::BoostMode::Multiply)
                              .with_boost(30)
                              .with_query(rs_q::build_match_all().build())
			                  .with_function(
                          rs_es::query::functions::Function::build_field_value_factor("weight")
			                                  .with_factor(1)
                                      .with_modifier(rs_es::query::functions::Modifier::Log1p)
			                                                          .build())
                              .build()])
                        .build();
    let filter = 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()])
                     .with_must(vec![rs_q::build_match(match_type, q.to_string())
             .with_minimum_should_match(rs_es::query::MinimumShouldMatch::from(100f64)).build()])
                     .build();

    let final_query = rs_q::build_bool()
                          .with_must(vec![sub_query])
                          .with_filter(filter)
                          .build();

    let mut client = build_rs_client(cnx);

    let result: SearchResult<serde_json::Value> = try!(client.search_query()
                                                       .with_indexes(&["munin"])
                                                       .with_query(&final_query)
                                                       .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())
}

fn query_location(_q: &String,
                  _coord: &model::Coord)
                  -> Result<Vec<mimir::Place>, rs_es::error::EsError> {
    panic!("todo!");
}

pub fn autocomplete(q: String,
                    coord: Option<model::Coord>,
                    cnx: &String)
                    -> Result<Vec<mimir::Place>, rs_es::error::EsError> {
    if let Some(ref coord) = coord {
        query_location(&q, coord)
    } else {
    	//First search with match = "name.prefix".
    	//If no result then another search with match = "name.ngram"
    	let results = try!(query(&q, cnx, "name.prefix"));
        if results.is_empty() {
        	query(&q, cnx, "name.ngram")
        } else {
        	Ok(results)
        }
    }
}
<commit_msg>bragi: correction on a comment<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 mimir;
use serde_json;

fn build_rs_client(cnx: &String) -> rs_es::Client {
    let re = regex::Regex::new(r"(?:https?://)?(?P<host>.+?):(?P<port>\d+)/(?P<index>\w+)")
                 .unwrap();
    let cap = re.captures(&cnx).unwrap();
    let host = cap.name("host").unwrap();
    let port = cap.name("port").unwrap().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
fn make_place(doc_type: String, value: Option<Box<serde_json::Value>>) -> Option<mimir::Place> {
    value.and_then(|v| {
        match doc_type.as_ref() {
            "addr" => serde_json::from_value::<mimir::Addr>(*v).ok().and_then(|o| Some(mimir::Place::Addr(o))),
            "street" => serde_json::from_value::<mimir::Street>(*v).ok().and_then(|o| Some(mimir::Place::Street(o))),
            "admin" => serde_json::from_value::<mimir::Admin>(*v).ok().and_then(|o| Some(mimir::Place::Admin(o))),
            _    => {
                warn!("unknown ES return value, _type field = {}", doc_type);
                None
            }
        }
    })
}

fn query(q: &String, cnx: &String, match_type: &str) -> Result<Vec<mimir::Place>, rs_es::error::EsError> {
    let sub_query = rs_q::build_bool()
                        .with_should(vec![
                       rs_q::build_term("_type","addr").with_boost(1000).build(),
                       rs_q::build_match(match_type, q.to_string())
                              .with_boost(100)
                              .build(),
                       rs_q::build_function_score()
                              .with_boost_mode(rs_es::query::compound::BoostMode::Multiply)
                              .with_boost(30)
                              .with_query(rs_q::build_match_all().build())
			                  .with_function(
                          rs_es::query::functions::Function::build_field_value_factor("weight")
			                                  .with_factor(1)
                                      .with_modifier(rs_es::query::functions::Modifier::Log1p)
			                                                          .build())
                              .build()])
                        .build();
    let filter = 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()])
                     .with_must(vec![rs_q::build_match(match_type, q.to_string())
             .with_minimum_should_match(rs_es::query::MinimumShouldMatch::from(100f64)).build()])
                     .build();

    let final_query = rs_q::build_bool()
                          .with_must(vec![sub_query])
                          .with_filter(filter)
                          .build();

    let mut client = build_rs_client(cnx);

    let result: SearchResult<serde_json::Value> = try!(client.search_query()
                                                       .with_indexes(&["munin"])
                                                       .with_query(&final_query)
                                                       .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())
}

fn query_location(_q: &String,
                  _coord: &model::Coord)
                  -> Result<Vec<mimir::Place>, rs_es::error::EsError> {
    panic!("todo!");
}

fn query_prefix(q: &String, cnx: &String) -> Result<Vec<mimir::Place>, rs_es::error::EsError> {
	query(&q, cnx, "name.prefix")
}

fn query_ngram(q: &String, cnx: &String) -> Result<Vec<mimir::Place>, rs_es::error::EsError> {
	query(&q, cnx, "name.ngram")
}

pub fn autocomplete(q: String,
                    coord: Option<model::Coord>,
                    cnx: &String)
                    -> Result<Vec<mimir::Place>, rs_es::error::EsError> {
    if let Some(ref coord) = coord {
        query_location(&q, coord)
    } else {
    	//First search with match = "name.prefix".
    	//If no result then another search with match = "name.ngram"
    	let results = try!(query_prefix(&q, cnx));
        if results.is_empty() {
        	query_ngram(&q, cnx)
        } else {
        	Ok(results)
        }
    }
}
<|endoftext|>