<commit_before>use std::borrow;
use std::error;
use std::fmt;
use std::result;

/// Convenience type alias for Liquid compiler errors
pub type Result<T> = result::Result<T, Error>;

/// `Result` extension methods for adapting third party errors to `Error`.
pub trait ResultLiquidChainExt<T> {
    /// Create an `Error` with `E` as the cause.
    fn chain(self, msg: &'static str) -> Result<T>;
    /// Create an `Error` with `E` as the cause.
    fn chain_with<F>(self, msg: F) -> Result<T>
    where
        F: FnOnce() -> String;
}

impl<T, E> ResultLiquidChainExt<T> for result::Result<T, E>
where
    E: error::Error + Send + Sync + 'static,
{
    fn chain(self, msg: &'static str) -> Result<T> {
        self.map_err(|err| Error::with_msg(msg).cause(err))
    }

    fn chain_with<F>(self, msg: F) -> Result<T>
    where
        F: FnOnce() -> String,
    {
        self.map_err(|err| Error::with_msg(msg()).cause(err))
    }
}

/// Add context to a `liquid_error::Error`.
pub trait ResultLiquidExt<T> {
    /// Add a new stack frame to the `liquid_error::Error`.
    fn trace<S>(self, trace: S) -> Result<T>
    where
        S: Into<borrow::Cow<'static, str>>;

    /// Add a new stack frame to the `liquid_error::Error`.
    fn trace_with<F>(self, trace: F) -> Result<T>
    where
        F: FnOnce() -> String;

    /// Add state the current stack frame.
    fn context<K, V>(self, key: K, value: V) -> Result<T>
    where
        K: Into<borrow::Cow<'static, str>>,
        V: Into<borrow::Cow<'static, str>>;

    /// Add state the current stack frame.
    fn context_with<F>(self, context: F) -> Result<T>
    where
        F: FnOnce() -> (String, String);
}

impl<T> ResultLiquidExt<T> for Result<T> {
    fn trace<S>(self, trace: S) -> Result<T>
    where
        S: Into<borrow::Cow<'static, str>>,
    {
        self.map_err(|err| err.trace(trace))
    }

    fn trace_with<F>(self, trace: F) -> Result<T>
    where
        F: FnOnce() -> String,
    {
        self.map_err(|err| err.trace(trace()))
    }

    fn context<K, V>(self, key: K, value: V) -> Result<T>
    where
        K: Into<borrow::Cow<'static, str>>,
        V: Into<borrow::Cow<'static, str>>,
    {
        self.map_err(|err| err.context(key, value))
    }

    fn context_with<F>(self, context: F) -> Result<T>
    where
        F: FnOnce() -> (String, String),
    {
        let (key, value) = context();
        self.map_err(|err| err.context(key, value))
    }
}

/// Compiler error
#[derive(Clone, Debug)]
pub struct Error {
    inner: Box<InnerError>,
}

// Guts of `Error` here to keep `Error` small to avoid bloating the size of `Result<T>` in the
// success case.  There are already enough memory allocations below, one more shouldn't hurt.
#[derive(Clone, Debug)]
struct InnerError {
    msg: borrow::Cow<'static, str>,
    user_backtrace: Vec<Trace>,
    cause: Option<ErrorCause>,
}

impl Error {
    /// Create a new compiler error with the given message
    pub fn with_msg<S: Into<borrow::Cow<'static, str>>>(msg: S) -> Self {
        Self::with_msg_cow(msg.into())
    }

    fn with_msg_cow(msg: borrow::Cow<'static, str>) -> Self {
        let error = InnerError {
            msg,
            user_backtrace: vec![Trace::empty()],
            cause: None,
        };
        Self {
            inner: Box::new(error),
        }
    }

    /// Add a new call to the user-visible backtrace
    pub fn trace<T>(self, trace: T) -> Self
    where
        T: Into<borrow::Cow<'static, str>>,
    {
        self.trace_trace(trace.into())
    }

    fn trace_trace(mut self, trace: borrow::Cow<'static, str>) -> Self {
        let trace = Trace::new(trace);
        self.inner.user_backtrace.push(trace);
        self
    }

    /// Add context to the last traced call.
    ///
    /// Example context: Value that parameters from the `trace` evaluate to.
    pub fn context<K, V>(self, key: K, value: V) -> Self
    where
        K: Into<borrow::Cow<'static, str>>,
        V: Into<borrow::Cow<'static, str>>,
    {
        self.context_cow_string(key.into(), value.into())
    }

    fn context_cow_string(
        mut self,
        key: borrow::Cow<'static, str>,
        value: borrow::Cow<'static, str>,
    ) -> Self {
        self.inner
            .user_backtrace
            .last_mut()
            .expect("always a trace available")
            .append_context(key, value);
        self
    }

    /// Add an external cause to the error for debugging purposes.
    pub fn cause<E: error::Error + Send + Sync + 'static>(self, cause: E) -> Self {
        let cause = Box::new(cause);
        self.cause_error(cause)
    }

    fn cause_error(mut self, cause: Box<error::Error + Send + Sync + 'static>) -> Self {
        let cause = Some(ErrorCause::Generic(GenericError(cause)));
        self.inner.cause = cause;
        self
    }
}

const ERROR_DESCRIPTION: &str = "liquid";

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        writeln!(f, "{}: {}", ERROR_DESCRIPTION, self.inner.msg)?;
        for trace in &self.inner.user_backtrace {
            if let Some(trace) = trace.get_trace() {
                writeln!(f, "from: {}", trace)?;
            }
            if !trace.get_context().is_empty() {
                writeln!(f, "  with:")?;
            }
            for &(ref key, ref value) in trace.get_context() {
                writeln!(f, "    {}={}", key, value)?;
            }
        }
        Ok(())
    }
}

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

    fn cause(&self) -> Option<&error::Error> {
        match self.inner.cause {
            Some(ErrorCause::Generic(ref e)) => Some(e.0.as_ref()),
            _ => None,
        }
    }
}

/// User-visible call trace
#[derive(Clone, PartialEq, Eq, Debug, Default)]
struct Trace {
    trace: Option<borrow::Cow<'static, str>>,
    context: Vec<(borrow::Cow<'static, str>, borrow::Cow<'static, str>)>,
}

impl Trace {
    fn new(trace: borrow::Cow<'static, str>) -> Self {
        Self {
            trace: Some(trace),
            context: vec![],
        }
    }

    fn empty() -> Self {
        Self {
            trace: None,
            context: vec![],
        }
    }

    fn append_context(&mut self, key: borrow::Cow<'static, str>, value: borrow::Cow<'static, str>) {
        self.context.push((key, value));
    }

    fn get_trace(&self) -> Option<&str> {
        self.trace.as_ref().map(|s| s.as_ref())
    }

    fn get_context(&self) -> &[(borrow::Cow<'static, str>, borrow::Cow<'static, str>)] {
        self.context.as_ref()
    }
}

#[derive(Debug)]
enum ErrorCause {
    Generic(GenericError),
    Missing,
}

impl Clone for ErrorCause {
    fn clone(&self) -> Self {
        match *self {
            ErrorCause::Generic(_) | ErrorCause::Missing => ErrorCause::Missing,
        }
    }
}

impl fmt::Display for ErrorCause {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ErrorCause::Generic(ref e) => fmt::Display::fmt(e, f),
            ErrorCause::Missing => write!(f, "Unknown error"),
        }
    }
}

struct GenericError(Box<error::Error + Send + Sync + 'static>);

impl fmt::Debug for GenericError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self.0, f)
    }
}

impl fmt::Display for GenericError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(&self.0, f)
    }
}
<commit_msg>refactor(error): Simplify implementation<commit_after>use std::borrow;
use std::error;
use std::fmt;
use std::result;

/// Convenience type alias for Liquid compiler errors
pub type Result<T> = result::Result<T, Error>;

/// `Result` extension methods for adapting third party errors to `Error`.
pub trait ResultLiquidChainExt<T> {
    /// Create an `Error` with `E` as the cause.
    fn chain(self, msg: &'static str) -> Result<T>;
    /// Create an `Error` with `E` as the cause.
    fn chain_with<F>(self, msg: F) -> Result<T>
    where
        F: FnOnce() -> String;
}

impl<T, E> ResultLiquidChainExt<T> for result::Result<T, E>
where
    E: error::Error + Send + Sync + 'static,
{
    fn chain(self, msg: &'static str) -> Result<T> {
        self.map_err(|err| Error::with_msg(msg).cause(err))
    }

    fn chain_with<F>(self, msg: F) -> Result<T>
    where
        F: FnOnce() -> String,
    {
        self.map_err(|err| Error::with_msg(msg()).cause(err))
    }
}

/// Add context to a `liquid_error::Error`.
pub trait ResultLiquidExt<T> {
    /// Add a new stack frame to the `liquid_error::Error`.
    fn trace<S>(self, trace: S) -> Result<T>
    where
        S: Into<borrow::Cow<'static, str>>;

    /// Add a new stack frame to the `liquid_error::Error`.
    fn trace_with<F>(self, trace: F) -> Result<T>
    where
        F: FnOnce() -> String;

    /// Add state the current stack frame.
    fn context<K, V>(self, key: K, value: V) -> Result<T>
    where
        K: Into<borrow::Cow<'static, str>>,
        V: Into<borrow::Cow<'static, str>>;

    /// Add state the current stack frame.
    fn context_with<F>(self, context: F) -> Result<T>
    where
        F: FnOnce() -> (String, String);
}

impl<T> ResultLiquidExt<T> for Result<T> {
    fn trace<S>(self, trace: S) -> Result<T>
    where
        S: Into<borrow::Cow<'static, str>>,
    {
        self.map_err(|err| err.trace(trace))
    }

    fn trace_with<F>(self, trace: F) -> Result<T>
    where
        F: FnOnce() -> String,
    {
        self.map_err(|err| err.trace(trace()))
    }

    fn context<K, V>(self, key: K, value: V) -> Result<T>
    where
        K: Into<borrow::Cow<'static, str>>,
        V: Into<borrow::Cow<'static, str>>,
    {
        self.map_err(|err| err.context(key, value))
    }

    fn context_with<F>(self, context: F) -> Result<T>
    where
        F: FnOnce() -> (String, String),
    {
        let (key, value) = context();
        self.map_err(|err| err.context(key, value))
    }
}

/// Compiler error
#[derive(Clone, Debug)]
pub struct Error {
    inner: Box<InnerError>,
}

// Guts of `Error` here to keep `Error` small to avoid bloating the size of `Result<T>` in the
// success case.  There are already enough memory allocations below, one more shouldn't hurt.
#[derive(Clone, Debug)]
struct InnerError {
    msg: borrow::Cow<'static, str>,
    user_backtrace: Vec<Trace>,
    cause: Option<ErrorCause>,
}

impl Error {
    /// Create a new compiler error with the given message
    pub fn with_msg<S: Into<borrow::Cow<'static, str>>>(msg: S) -> Self {
        Self::with_msg_cow(msg.into())
    }

    fn with_msg_cow(msg: borrow::Cow<'static, str>) -> Self {
        let error = InnerError {
            msg,
            user_backtrace: vec![Trace::empty()],
            cause: None,
        };
        Self {
            inner: Box::new(error),
        }
    }

    /// Add a new call to the user-visible backtrace
    pub fn trace<T>(self, trace: T) -> Self
    where
        T: Into<borrow::Cow<'static, str>>,
    {
        self.trace_trace(trace.into())
    }

    fn trace_trace(mut self, trace: borrow::Cow<'static, str>) -> Self {
        let trace = Trace::new(trace);
        self.inner.user_backtrace.push(trace);
        self
    }

    /// Add context to the last traced call.
    ///
    /// Example context: Value that parameters from the `trace` evaluate to.
    pub fn context<K, V>(self, key: K, value: V) -> Self
    where
        K: Into<borrow::Cow<'static, str>>,
        V: Into<borrow::Cow<'static, str>>,
    {
        self.context_cow_string(key.into(), value.into())
    }

    fn context_cow_string(
        mut self,
        key: borrow::Cow<'static, str>,
        value: borrow::Cow<'static, str>,
    ) -> Self {
        self.inner
            .user_backtrace
            .last_mut()
            .expect("always a trace available")
            .append_context(key, value);
        self
    }

    /// Add an external cause to the error for debugging purposes.
    pub fn cause<E: error::Error + Send + Sync + 'static>(self, cause: E) -> Self {
        let cause = Box::new(cause);
        self.cause_error(cause)
    }

    fn cause_error(mut self, cause: Box<error::Error + Send + Sync + 'static>) -> Self {
        let cause = Some(ErrorCause::new(cause));
        self.inner.cause = cause;
        self
    }
}

const ERROR_DESCRIPTION: &str = "liquid";

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        writeln!(f, "{}: {}", ERROR_DESCRIPTION, self.inner.msg)?;
        for trace in &self.inner.user_backtrace {
            if let Some(trace) = trace.get_trace() {
                writeln!(f, "from: {}", trace)?;
            }
            if !trace.get_context().is_empty() {
                writeln!(f, "  with:")?;
            }
            for &(ref key, ref value) in trace.get_context() {
                writeln!(f, "    {}={}", key, value)?;
            }
        }
        Ok(())
    }
}

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

    fn cause(&self) -> Option<&error::Error> {
        self.inner.cause.as_ref().and_then(|e| e.cause())
    }
}

/// User-visible call trace
#[derive(Clone, PartialEq, Eq, Debug, Default)]
struct Trace {
    trace: Option<borrow::Cow<'static, str>>,
    context: Vec<(borrow::Cow<'static, str>, borrow::Cow<'static, str>)>,
}

impl Trace {
    fn new(trace: borrow::Cow<'static, str>) -> Self {
        Self {
            trace: Some(trace),
            context: vec![],
        }
    }

    fn empty() -> Self {
        Self {
            trace: None,
            context: vec![],
        }
    }

    fn append_context(&mut self, key: borrow::Cow<'static, str>, value: borrow::Cow<'static, str>) {
        self.context.push((key, value));
    }

    fn get_trace(&self) -> Option<&str> {
        self.trace.as_ref().map(|s| s.as_ref())
    }

    fn get_context(&self) -> &[(borrow::Cow<'static, str>, borrow::Cow<'static, str>)] {
        self.context.as_ref()
    }
}

#[derive(Debug)]
enum ErrorCause {
    Generic(GenericError),
    Missing,
}

type GenericError = Box<error::Error + Send + Sync + 'static>;

impl ErrorCause {
    fn new(error: GenericError) -> Self {
        ErrorCause::Generic(error)
    }

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

impl Clone for ErrorCause {
    fn clone(&self) -> Self {
        match *self {
            ErrorCause::Generic(_) | ErrorCause::Missing => ErrorCause::Missing,
        }
    }
}

impl fmt::Display for ErrorCause {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ErrorCause::Generic(ref e) => fmt::Display::fmt(e, f),
            ErrorCause::Missing => write!(f, "Unknown error"),
        }
    }
}
<|endoftext|>


<commit_before>//#![deny(warnings)]
extern crate futures;
extern crate hyper;
extern crate pretty_env_logger;
#[macro_use]
extern crate log;

use hyper::{Get, Post, StatusCode};
use hyper::header::ContentLength;
use hyper::server::{Http, Service, Request, Response};

static INDEX: &'static [u8] = b"Try POST /echo";

#[derive(Clone, Copy)]
struct Echo;

impl Service for Echo {
    type Request = Request;
    type Response = Response;
    type Error = hyper::Error;
    type Future = ::futures::Finished<Response, hyper::Error>;

    fn call(&self, req: Request) -> Self::Future {
        ::futures::finished(match (req.method(), req.path()) {
            (&Get, "/") | (&Get, "/echo") => {
                Response::new()
                    .with_header(ContentLength(INDEX.len() as u64))
                    .with_body(INDEX)
            },
            (&Post, "/echo") => {
                let mut res = Response::new();
                if let Some(len) = req.headers().get::<ContentLength>() {
                    res.headers_mut().set(len.clone());
                }
                res.with_body(req.body())
            },
            _ => {
                Response::new()
                    .with_status(StatusCode::NotFound)
            }
        })
    }

}

fn main() {
    pretty_env_logger::init().unwrap();
    let addr = "127.0.0.1:1337".parse().unwrap();

    let server = Http::new().bind(&addr, || Ok(Echo)).unwrap();
    println!("Listening on http://{}", server.local_addr().unwrap());
    server.run().unwrap();
}
<commit_msg>refactor(examples): remove log crate from server example<commit_after>#![deny(warnings)]
extern crate futures;
extern crate hyper;
extern crate pretty_env_logger;

use hyper::{Get, Post, StatusCode};
use hyper::header::ContentLength;
use hyper::server::{Http, Service, Request, Response};

static INDEX: &'static [u8] = b"Try POST /echo";

#[derive(Clone, Copy)]
struct Echo;

impl Service for Echo {
    type Request = Request;
    type Response = Response;
    type Error = hyper::Error;
    type Future = ::futures::Finished<Response, hyper::Error>;

    fn call(&self, req: Request) -> Self::Future {
        ::futures::finished(match (req.method(), req.path()) {
            (&Get, "/") | (&Get, "/echo") => {
                Response::new()
                    .with_header(ContentLength(INDEX.len() as u64))
                    .with_body(INDEX)
            },
            (&Post, "/echo") => {
                let mut res = Response::new();
                if let Some(len) = req.headers().get::<ContentLength>() {
                    res.headers_mut().set(len.clone());
                }
                res.with_body(req.body())
            },
            _ => {
                Response::new()
                    .with_status(StatusCode::NotFound)
            }
        })
    }

}

fn main() {
    pretty_env_logger::init().unwrap();
    let addr = "127.0.0.1:1337".parse().unwrap();

    let server = Http::new().bind(&addr, || Ok(Echo)).unwrap();
    println!("Listening on http://{}", server.local_addr().unwrap());
    server.run().unwrap();
}
<|endoftext|>


<commit_before>extern crate handlebars;
extern crate itertools;
extern crate serde;
#[macro_use] extern crate serde_derive;
extern crate serde_json;

use handlebars::Handlebars;
use itertools::Itertools;
use serde_json::Value;
use std::collections::BTreeMap;
use std::ascii::AsciiExt;

trait Codegen {
    fn codegen(&self, handlebars: &Handlebars) -> String;
}

#[derive(Debug, Deserialize)]
pub struct AMQProtocolDefinition {
    pub name:          String,
    #[serde(rename="major-version")]
    pub major_version: u8,
    #[serde(rename="minor-version")]
    pub minor_version: u8,
    pub revision:      u8,
    pub port:          u32,
    pub copyright:     Vec<String>,
    pub domains:       Vec<AMQPDomain>,
    pub constants:     Vec<AMQPConstant>,
    pub classes:       Vec<AMQPClass>,
}

impl AMQProtocolDefinition {
    pub fn load() -> AMQProtocolDefinition {
        let specs = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/specs/amqp-rabbitmq-0.9.1.json"));

        serde_json::from_str::<AMQProtocolDefinition>(specs).expect("Failed to parse AMQP specs file")
    }

    pub fn codegen(&self, templates: &AMQPTemplates) -> String {
        let handlebars = register_templates(templates);
        let mut data   = BTreeMap::new();

        data.insert("name".to_string(),          self.name.clone());
        data.insert("major_version".to_string(), format!("{}", self.major_version));
        data.insert("minor_version".to_string(), format!("{}", self.minor_version));
        data.insert("revision".to_string(),      format!("{}", self.revision));
        data.insert("port".to_string(),          format!("{}", self.port));
        data.insert("copyright".to_string(),     self.copyright.iter().join(""));
        data.insert("domains".to_string(),       self.domains.iter().map(|domain| domain.codegen(&handlebars)).join("\n"));
        data.insert("constants".to_string(),     self.constants.iter().map(|constant| constant.codegen(&handlebars)).join("\n"));
        data.insert("classes".to_string(),       self.classes.iter().map(|klass| klass.codegen(&handlebars)).join("\n"));

        handlebars.render("main", &data).expect("Failed to render main template")
    }
}

fn register_templates(templates: &AMQPTemplates) -> Handlebars {
    let mut handlebars = Handlebars::new();

    handlebars.register_escape_fn(handlebars::no_escape);

    handlebars.register_template_string("main",     &templates.main).expect("Failed to register main template");
    handlebars.register_template_string("domain",   &templates.domain).expect("Failed to register domain template");
    handlebars.register_template_string("constant", &templates.constant).expect("Failed to register constant template");
    handlebars.register_template_string("class",    &templates.klass).expect("Failed to register class template");
    handlebars.register_template_string("method",   &templates.method).expect("Failed to register method template");
    handlebars.register_template_string("argument", &templates.argument).expect("Failed to register argument template");
    handlebars.register_template_string("property", &templates.property).expect("Failed to register property template");

    handlebars
}

pub struct AMQPTemplates {
    pub main:     String,
    pub domain:   String,
    pub constant: String,
    pub klass:    String,
    pub method:   String,
    pub argument: String,
    pub property: String,
}

#[derive(Debug, Deserialize)]
pub enum AMQPType {
    #[serde(rename="bit")]
    Bit,
    #[serde(rename="octet")]
    Octet,
    #[serde(rename="short")]
    Short,
    #[serde(rename="long")]
    Long,
    #[serde(rename="longlong")]
    LongLong,
    #[serde(rename="shortstr")]
    ShortStr,
    #[serde(rename="longstr")]
    LongStr,
    #[serde(rename="table")]
    Table,
    #[serde(rename="timestamp")]
    Timestamp,
}

impl AMQPType {
    fn to_string(&self) -> String {
        match *self {
            AMQPType::Bit       => "bit",
            AMQPType::Octet     => "octet",
            AMQPType::Short     => "short",
            AMQPType::Long      => "long",
            AMQPType::LongLong  => "longlong",
            AMQPType::ShortStr  => "shortstr",
            AMQPType::LongStr   => "longstr",
            AMQPType::Table     => "table",
            AMQPType::Timestamp => "timestamp",
        }.to_string()
    }

    fn to_rust_type(&self) -> String {
        match *self {
            AMQPType::Bit       => "bool",
            AMQPType::Octet     => "u8",
            AMQPType::Short     => "u16",
            AMQPType::Long      => "u32",
            AMQPType::LongLong  => "u64",
            AMQPType::ShortStr  => "String",
            AMQPType::LongStr   => "String",
            AMQPType::Table     => "String", /* FIXME: add a custom type */
            AMQPType::Timestamp => "u64",
        }.to_string()
    }
}

fn camel_name(name: &str) -> String {
    let mut new_word: bool = true;
    name.chars().fold("".to_string(), |mut result, ch| {
        if ch == '-' || ch == '_' || ch == ' ' {
            new_word = true;
            result
        } else {
            result.push(if new_word { ch.to_ascii_uppercase() } else { ch.to_ascii_lowercase() });
            new_word = false;
            result
        }
    })
}

fn snake_name(name: &str) -> String {
    match name {
        "type"   => "amqp_type".to_string(),
        "return" => "amqp_return".to_string(),
        name     => name.replace("-", "_"),
    }
}

#[derive(Debug, Deserialize)]
pub struct AMQPDomain(String, AMQPType);

impl Codegen for AMQPDomain {
    fn codegen(&self, handlebars: &Handlebars) -> String {
        let mut data = BTreeMap::new();

        data.insert("name".to_string(), self.0.clone());
        data.insert("type".to_string(), self.1.to_string());

        handlebars.render("domain", &data).expect("Failed to render domain template")
    }
}

#[derive(Debug, Deserialize)]
pub struct AMQPConstant {
    pub name:  String,
    pub value: u16,
    #[serde(rename="class")]
    pub klass: Option<String>,
}

impl Codegen for AMQPConstant {
    fn codegen(&self, handlebars: &Handlebars) -> String {
        let mut data = BTreeMap::new();

        data.insert("name".to_string(),  self.name.clone());
        data.insert("value".to_string(), format!("{}", self.value));
        if let Some(ref klass) = self.klass {
            data.insert("class".to_string(), klass.clone());
        }

        handlebars.render("constant", &data).expect("Failed to render constant template")
    }
}

#[derive(Debug, Deserialize)]
pub struct AMQPClass {
    pub id:         u8,
    pub methods:    Vec<AMQPMethod>,
    pub name:       String,
    pub properties: Option<Vec<AMQPProperty>>,
}

impl Codegen for AMQPClass {
    fn codegen(&self, handlebars: &Handlebars) -> String {
        let mut data = BTreeMap::new();

        data.insert("id".to_string(),         format!("{}", self.id));
        data.insert("methods".to_string(),    self.methods.iter().map(|method| method.codegen(&handlebars)).join("\n"));
        data.insert("name".to_string(),       self.name.clone());
        data.insert("snake_name".to_string(), snake_name(&self.name));
        if let Some(ref properties) = self.properties {
            data.insert("properties".to_string(), properties.iter().map(|prop| prop.codegen(&handlebars)).join("\n"));
        }

        handlebars.render("class", &data).expect("Failed to render class template")
    }
}

#[derive(Debug, Deserialize)]
pub struct AMQPMethod {
    pub id:          u8,
    pub arguments:   Vec<AMQPArgument>,
    pub name:        String,
    pub synchronous: Option<bool>,
}

impl Codegen for AMQPMethod {
    fn codegen(&self, handlebars: &Handlebars) -> String {
        let mut data = BTreeMap::new();

        data.insert("id".to_string(),              format!("{}", self.id));
        data.insert("arguments".to_string(),       self.arguments.iter().map(|arg| arg.codegen(&handlebars)).join("\n"));
        data.insert("argument_fields".to_string(), self.arguments.iter().map(|arg| arg.codegen_field()).join("\n"));
        data.insert("name".to_string(),            self.name.clone());
        data.insert("synchronous".to_string(),     format!("{}", self.synchronous.unwrap_or(false)));
        data.insert("camel_name".to_string(),      camel_name(&self.name));
        data.insert("snake_name".to_string(),      snake_name(&self.name));

        handlebars.render("method", &data).expect("Failed to render method template")
    }
}

#[derive(Debug, Deserialize)]
pub struct AMQPArgument {
    #[serde(rename="type")]
    pub amqp_type:     Option<AMQPType>,
    pub name:          String,
    #[serde(rename="default-value")]
    pub default_value: Option<Value>,
    pub domain:        Option<String>,
}

impl AMQPArgument {
    fn serialize_default_value(&self) -> String {
        if let Some(ref default_value) = self.default_value {
            let s = default_value.to_string();
            match default_value {
                &Value::String(_) => format!("Some({}.to_string())", s),
                &Value::Number(_) => format!("Some({})", s),
                &Value::Bool(_)   => format!("Some({})", s),
                _                 => "None".to_string(),
            }
        } else {
            "None".to_string()
        }
    }

    fn serialize_domain(&self) -> String {
        if let Some(ref domain) = self.domain {
            format!("Some(\"{}\".to_string())", domain)
        } else {
            "None".to_string()
        }
    }

    fn codegen_field(&self) -> String {
        format!("pub {}: {},", snake_name(&self.name), camel_name(&self.name))
    }
}

impl Codegen for AMQPArgument {
    fn codegen(&self, handlebars: &Handlebars) -> String {
        let mut data = BTreeMap::new();

        if let Some(ref amqp_type) = self.amqp_type {
            data.insert("type".to_string(), amqp_type.to_string());
            data.insert("value_field".to_string(), format!("pub value: {},", amqp_type.to_rust_type()));
            data.insert("default_value_method".to_string(), format!("pub fn default_value() -> Option<{}> {{ {} }}", amqp_type.to_rust_type(), &self.serialize_default_value()));
        }
        data.insert("name".to_string(),          self.name.clone());
        data.insert("camel_name".to_string(),    camel_name(&self.name));
        data.insert("snake_name".to_string(),    snake_name(&self.name));
        data.insert("domain".to_string(),        self.serialize_domain());

        handlebars.render("argument", &data).expect("Failed to render argument template")
    }
}

#[derive(Debug, Deserialize)]
pub struct AMQPProperty {
    #[serde(rename="type")]
    pub amqp_type: AMQPType,
    pub name:      String,
}

impl Codegen for AMQPProperty {
    fn codegen(&self, handlebars: &Handlebars) -> String {
        let mut data = BTreeMap::new();

        data.insert("type".to_string(),       self.amqp_type.to_string());
        data.insert("rust_type".to_string(),  self.amqp_type.to_rust_type());
        data.insert("name".to_string(),       self.name.clone());
        data.insert("camel_name".to_string(), camel_name(&self.name));

        handlebars.render("property", &data).expect("Failed to render property template")
    }
}

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

    fn specs() -> AMQProtocolDefinition {
        AMQProtocolDefinition {
            name:          "AMQP".to_string(),
            major_version: 0,
            minor_version: 9,
            revision:      1,
            port:          5672,
            copyright:     vec!["Copyright 1\n".to_string(), "Copyright 2".to_string()],
            domains:       vec![AMQPDomain("domain1".to_string(), AMQPType::Octet)],
            constants:     vec![
                AMQPConstant {
                    name:  "constant1".to_string(),
                    value: 128,
                    klass: Some("class1".to_string()),
                }
            ],
            classes:       vec![
                AMQPClass {
                    id:         42,
                    methods:    vec![
                        AMQPMethod {
                            id:          64,
                            arguments:   vec![
                                AMQPArgument {
                                    amqp_type:     Some(AMQPType::LongStr),
                                    name:          "argument1".to_string(),
                                    default_value: Some(Value::String("value1".to_string())),
                                    domain:        Some("domain1".to_string()),
                                }
                            ],
                            name:        "method1".to_string(),
                            synchronous: Some(true),
                        }
                    ],
                    name:       "class1".to_string(),
                    properties: Some(vec![
                        AMQPProperty {
                            amqp_type: AMQPType::LongStr,
                            name:      "property1".to_string(),
                        }
                    ]),
                }
            ],
        }
    }

    fn templates() -> AMQPTemplates {
        AMQPTemplates {
            main:     r#"
{{name}} - {{major_version}}.{{minor_version}}.{{revision}}
{{copyright}}
port {{port}}
{{domains}}
{{constants}}
{{classes}}
"#.to_string(),
            domain:   "{{name}}: {{type}}".to_string(),
            constant: "{{name}}({{class}}) = {{value}}".to_string(),
            klass:    r#"
{{id}} - {{name}}
{{properties}}
{{methods}}
"#.to_string(),
            method:   r#"
{{id}} - {{name}}
synchronous: {{synchronous}}
{{arguments}}
"#.to_string(),
            argument: "{{name}}({{domain}}): {{type}} = {{default_value_method}}".to_string(),
            property: "{{name}}: {{type}}".to_string(),
        }
    }

    #[test]
    fn main_template() {
        assert_eq!(specs().codegen(&templates()), r#"
AMQP - 0.9.1
Copyright 1
Copyright 2
port 5672
domain1: octet
constant1(class1) = 128

42 - class1
property1: longstr

64 - method1
synchronous: true
argument1(Some("domain1".to_string())): longstr = pub fn default_value() -> Option<String> { Some("value1".to_string()) }

"#);
    }
}
<commit_msg>add TODO<commit_after>extern crate handlebars;
extern crate itertools;
extern crate serde;
#[macro_use] extern crate serde_derive;
extern crate serde_json;

use handlebars::Handlebars;
use itertools::Itertools;
use serde_json::Value;
use std::collections::BTreeMap;
use std::ascii::AsciiExt;

trait Codegen {
    fn codegen(&self, handlebars: &Handlebars) -> String;
}

#[derive(Debug, Deserialize)]
pub struct AMQProtocolDefinition {
    pub name:          String,
    #[serde(rename="major-version")]
    pub major_version: u8,
    #[serde(rename="minor-version")]
    pub minor_version: u8,
    pub revision:      u8,
    pub port:          u32,
    pub copyright:     Vec<String>,
    pub domains:       Vec<AMQPDomain>,
    pub constants:     Vec<AMQPConstant>,
    pub classes:       Vec<AMQPClass>,
}

impl AMQProtocolDefinition {
    pub fn load() -> AMQProtocolDefinition {
        let specs = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/specs/amqp-rabbitmq-0.9.1.json"));

        serde_json::from_str::<AMQProtocolDefinition>(specs).expect("Failed to parse AMQP specs file")
    }

    pub fn codegen(&self, templates: &AMQPTemplates) -> String {
        let handlebars = register_templates(templates);
        let mut data   = BTreeMap::new();

        data.insert("name".to_string(),          self.name.clone());
        data.insert("major_version".to_string(), format!("{}", self.major_version));
        data.insert("minor_version".to_string(), format!("{}", self.minor_version));
        data.insert("revision".to_string(),      format!("{}", self.revision));
        data.insert("port".to_string(),          format!("{}", self.port));
        data.insert("copyright".to_string(),     self.copyright.iter().join(""));
        data.insert("domains".to_string(),       self.domains.iter().map(|domain| domain.codegen(&handlebars)).join("\n"));
        data.insert("constants".to_string(),     self.constants.iter().map(|constant| constant.codegen(&handlebars)).join("\n"));
        data.insert("classes".to_string(),       self.classes.iter().map(|klass| klass.codegen(&handlebars)).join("\n"));

        handlebars.render("main", &data).expect("Failed to render main template")
    }
}

fn register_templates(templates: &AMQPTemplates) -> Handlebars {
    let mut handlebars = Handlebars::new();

    handlebars.register_escape_fn(handlebars::no_escape);

    handlebars.register_template_string("main",     &templates.main).expect("Failed to register main template");
    handlebars.register_template_string("domain",   &templates.domain).expect("Failed to register domain template");
    handlebars.register_template_string("constant", &templates.constant).expect("Failed to register constant template");
    handlebars.register_template_string("class",    &templates.klass).expect("Failed to register class template");
    handlebars.register_template_string("method",   &templates.method).expect("Failed to register method template");
    handlebars.register_template_string("argument", &templates.argument).expect("Failed to register argument template");
    handlebars.register_template_string("property", &templates.property).expect("Failed to register property template");

    handlebars
}

pub struct AMQPTemplates {
    pub main:     String,
    pub domain:   String,
    pub constant: String,
    pub klass:    String,
    pub method:   String,
    pub argument: String,
    pub property: String,
}

#[derive(Debug, Deserialize)]
pub enum AMQPType {
    #[serde(rename="bit")]
    Bit,
    #[serde(rename="octet")]
    Octet,
    #[serde(rename="short")]
    Short,
    #[serde(rename="long")]
    Long,
    #[serde(rename="longlong")]
    LongLong,
    #[serde(rename="shortstr")]
    ShortStr,
    #[serde(rename="longstr")]
    LongStr,
    #[serde(rename="table")]
    Table,
    #[serde(rename="timestamp")]
    Timestamp,
}

impl AMQPType {
    fn to_string(&self) -> String {
        match *self {
            AMQPType::Bit       => "bit",
            AMQPType::Octet     => "octet",
            AMQPType::Short     => "short",
            AMQPType::Long      => "long",
            AMQPType::LongLong  => "longlong",
            AMQPType::ShortStr  => "shortstr",
            AMQPType::LongStr   => "longstr",
            AMQPType::Table     => "table",
            AMQPType::Timestamp => "timestamp",
        }.to_string()
    }

    fn to_rust_type(&self) -> String {
        match *self {
            AMQPType::Bit       => "bool",
            AMQPType::Octet     => "u8",
            AMQPType::Short     => "u16",
            AMQPType::Long      => "u32",
            AMQPType::LongLong  => "u64",
            AMQPType::ShortStr  => "String",
            AMQPType::LongStr   => "String",
            AMQPType::Table     => "String", /* FIXME: add a custom type */
            AMQPType::Timestamp => "u64",
        }.to_string()
    }
}

fn camel_name(name: &str) -> String {
    let mut new_word: bool = true;
    name.chars().fold("".to_string(), |mut result, ch| {
        if ch == '-' || ch == '_' || ch == ' ' {
            new_word = true;
            result
        } else {
            result.push(if new_word { ch.to_ascii_uppercase() } else { ch.to_ascii_lowercase() });
            new_word = false;
            result
        }
    })
}

fn snake_name(name: &str) -> String {
    match name {
        "type"   => "amqp_type".to_string(),
        "return" => "amqp_return".to_string(),
        name     => name.replace("-", "_"),
    }
}

#[derive(Debug, Deserialize)]
pub struct AMQPDomain(String, AMQPType);

impl Codegen for AMQPDomain {
    fn codegen(&self, handlebars: &Handlebars) -> String {
        let mut data = BTreeMap::new();

        data.insert("name".to_string(), self.0.clone());
        data.insert("type".to_string(), self.1.to_string());

        handlebars.render("domain", &data).expect("Failed to render domain template")
    }
}

#[derive(Debug, Deserialize)]
pub struct AMQPConstant {
    pub name:  String,
    pub value: u16,
    #[serde(rename="class")]
    pub klass: Option<String>,
}

impl Codegen for AMQPConstant {
    fn codegen(&self, handlebars: &Handlebars) -> String {
        let mut data = BTreeMap::new();

        data.insert("name".to_string(),  self.name.clone());
        data.insert("value".to_string(), format!("{}", self.value));
        if let Some(ref klass) = self.klass {
            data.insert("class".to_string(), klass.clone());
        }

        handlebars.render("constant", &data).expect("Failed to render constant template")
    }
}

#[derive(Debug, Deserialize)]
pub struct AMQPClass {
    pub id:         u8,
    pub methods:    Vec<AMQPMethod>,
    pub name:       String,
    pub properties: Option<Vec<AMQPProperty>>,
}

impl Codegen for AMQPClass {
    fn codegen(&self, handlebars: &Handlebars) -> String {
        let mut data = BTreeMap::new();

        data.insert("id".to_string(),         format!("{}", self.id));
        data.insert("methods".to_string(),    self.methods.iter().map(|method| method.codegen(&handlebars)).join("\n"));
        data.insert("name".to_string(),       self.name.clone());
        data.insert("snake_name".to_string(), snake_name(&self.name));
        if let Some(ref properties) = self.properties {
            data.insert("properties".to_string(), properties.iter().map(|prop| prop.codegen(&handlebars)).join("\n"));
        }

        handlebars.render("class", &data).expect("Failed to render class template")
    }
}

#[derive(Debug, Deserialize)]
pub struct AMQPMethod {
    pub id:          u8,
    pub arguments:   Vec<AMQPArgument>,
    pub name:        String,
    pub synchronous: Option<bool>,
}

impl Codegen for AMQPMethod {
    fn codegen(&self, handlebars: &Handlebars) -> String {
        let mut data = BTreeMap::new();

        data.insert("id".to_string(),              format!("{}", self.id));
        data.insert("arguments".to_string(),       self.arguments.iter().map(|arg| arg.codegen(&handlebars)).join("\n"));
        data.insert("argument_fields".to_string(), self.arguments.iter().map(|arg| arg.codegen_field()).join("\n"));
        data.insert("name".to_string(),            self.name.clone());
        data.insert("synchronous".to_string(),     format!("{}", self.synchronous.unwrap_or(false)));
        data.insert("camel_name".to_string(),      camel_name(&self.name));
        data.insert("snake_name".to_string(),      snake_name(&self.name));

        handlebars.render("method", &data).expect("Failed to render method template")
    }
}

#[derive(Debug, Deserialize)]
pub struct AMQPArgument {
    #[serde(rename="type")]
    pub amqp_type:     Option<AMQPType>,
    pub name:          String,
    #[serde(rename="default-value")]
    pub default_value: Option<Value>,
    pub domain:        Option<String>,
}

impl AMQPArgument {
    fn serialize_default_value(&self) -> String {
        if let Some(ref default_value) = self.default_value {
            let s = default_value.to_string();
            match default_value {
                /* TODO: simplify that, handle Table */
                &Value::String(_) => format!("Some({}.to_string())", s),
                &Value::Number(_) => format!("Some({})", s),
                &Value::Bool(_)   => format!("Some({})", s),
                _                 => "None".to_string(),
            }
        } else {
            "None".to_string()
        }
    }

    fn serialize_domain(&self) -> String {
        if let Some(ref domain) = self.domain {
            format!("Some(\"{}\".to_string())", domain)
        } else {
            "None".to_string()
        }
    }

    fn codegen_field(&self) -> String {
        format!("pub {}: {},", snake_name(&self.name), camel_name(&self.name))
    }
}

impl Codegen for AMQPArgument {
    fn codegen(&self, handlebars: &Handlebars) -> String {
        let mut data = BTreeMap::new();

        if let Some(ref amqp_type) = self.amqp_type {
            data.insert("type".to_string(), amqp_type.to_string());
            data.insert("value_field".to_string(), format!("pub value: {},", amqp_type.to_rust_type()));
            data.insert("default_value_method".to_string(), format!("pub fn default_value() -> Option<{}> {{ {} }}", amqp_type.to_rust_type(), &self.serialize_default_value()));
        }
        data.insert("name".to_string(),          self.name.clone());
        data.insert("camel_name".to_string(),    camel_name(&self.name));
        data.insert("snake_name".to_string(),    snake_name(&self.name));
        data.insert("domain".to_string(),        self.serialize_domain());

        handlebars.render("argument", &data).expect("Failed to render argument template")
    }
}

#[derive(Debug, Deserialize)]
pub struct AMQPProperty {
    #[serde(rename="type")]
    pub amqp_type: AMQPType,
    pub name:      String,
}

impl Codegen for AMQPProperty {
    fn codegen(&self, handlebars: &Handlebars) -> String {
        let mut data = BTreeMap::new();

        data.insert("type".to_string(),       self.amqp_type.to_string());
        data.insert("rust_type".to_string(),  self.amqp_type.to_rust_type());
        data.insert("name".to_string(),       self.name.clone());
        data.insert("camel_name".to_string(), camel_name(&self.name));

        handlebars.render("property", &data).expect("Failed to render property template")
    }
}

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

    fn specs() -> AMQProtocolDefinition {
        AMQProtocolDefinition {
            name:          "AMQP".to_string(),
            major_version: 0,
            minor_version: 9,
            revision:      1,
            port:          5672,
            copyright:     vec!["Copyright 1\n".to_string(), "Copyright 2".to_string()],
            domains:       vec![AMQPDomain("domain1".to_string(), AMQPType::Octet)],
            constants:     vec![
                AMQPConstant {
                    name:  "constant1".to_string(),
                    value: 128,
                    klass: Some("class1".to_string()),
                }
            ],
            classes:       vec![
                AMQPClass {
                    id:         42,
                    methods:    vec![
                        AMQPMethod {
                            id:          64,
                            arguments:   vec![
                                AMQPArgument {
                                    amqp_type:     Some(AMQPType::LongStr),
                                    name:          "argument1".to_string(),
                                    default_value: Some(Value::String("value1".to_string())),
                                    domain:        Some("domain1".to_string()),
                                }
                            ],
                            name:        "method1".to_string(),
                            synchronous: Some(true),
                        }
                    ],
                    name:       "class1".to_string(),
                    properties: Some(vec![
                        AMQPProperty {
                            amqp_type: AMQPType::LongStr,
                            name:      "property1".to_string(),
                        }
                    ]),
                }
            ],
        }
    }

    fn templates() -> AMQPTemplates {
        AMQPTemplates {
            main:     r#"
{{name}} - {{major_version}}.{{minor_version}}.{{revision}}
{{copyright}}
port {{port}}
{{domains}}
{{constants}}
{{classes}}
"#.to_string(),
            domain:   "{{name}}: {{type}}".to_string(),
            constant: "{{name}}({{class}}) = {{value}}".to_string(),
            klass:    r#"
{{id}} - {{name}}
{{properties}}
{{methods}}
"#.to_string(),
            method:   r#"
{{id}} - {{name}}
synchronous: {{synchronous}}
{{arguments}}
"#.to_string(),
            argument: "{{name}}({{domain}}): {{type}} = {{default_value_method}}".to_string(),
            property: "{{name}}: {{type}}".to_string(),
        }
    }

    #[test]
    fn main_template() {
        assert_eq!(specs().codegen(&templates()), r#"
AMQP - 0.9.1
Copyright 1
Copyright 2
port 5672
domain1: octet
constant1(class1) = 128

42 - class1
property1: longstr

64 - method1
synchronous: true
argument1(Some("domain1".to_string())): longstr = pub fn default_value() -> Option<String> { Some("value1".to_string()) }

"#);
    }
}
<|endoftext|>


<commit_before>
use collections::treemap::TreeMap;
use serialize::json::{JsonObject};

use valico::Builder as ValicoBuilder;

use request::Request;
use response::Response;
use middleware::{Handler, HandleResult, Error, NotMatchError};

pub use self::endpoint::{Endpoint, EndpointBuilder};
pub use self::client::Client;
pub use self::namespace::{Namespace, NS, ApiHandlers};

mod endpoint;
mod namespace;
mod client;

pub type ValicoBuildHandler<'a> = |&mut ValicoBuilder|:'a;

#[deriving(Show)]
pub struct QueryStringDecodeError;

impl Error for QueryStringDecodeError {
    fn name(&self) -> &'static str {
        return "QueryStringDecodeError";
    }
}

#[deriving(Show)]
pub struct ValidationError {
    reason: JsonObject
}

impl Error for ValidationError {
    fn name(&self) -> &'static str {
        return "ValidationError";
    }
}

#[deriving(Show)]
pub struct BodyDecodeError {
    reason: String
}

impl BodyDecodeError {
    pub fn new(reason: String) -> BodyDecodeError {
        return BodyDecodeError {
            reason: reason
        }
    }
}

impl Error for BodyDecodeError {
    fn name(&self) -> &'static str {
        return "BodyDecodeError";
    }

    fn description(&self) -> Option<&str> {
        return Some(self.reason.as_slice())
    }
}

pub trait ApiHandler {
    fn api_call(&self, &str, &mut JsonObject, &mut Request) -> HandleResult<Response>;
}

pub enum Versioning {
    PathVersioning,
    AcceptHeaderVersioning(String),
    AcceptVersionHeaderVersioning,
    ParamVersioning
}

#[deriving(Send)]
pub struct Api {
    pub version: Option<String>,
    pub versioning: Option<Versioning>,
    pub prefix: String,
    handlers: ApiHandlers
}

impl Api {

    pub fn new() -> Api {
        Api {
            version: None,
            versioning: None,
            prefix: "".to_string(),
            handlers: vec![]
        }
    }

    pub fn build(builder: |&mut Api|) -> Api {
        let mut api = Api::new();
        builder(&mut api);

        return api;
    }

    pub fn version(&mut self, version: &str, versioning: Versioning) {
        self.version = Some(version.to_string());
        self.versioning = Some(versioning);
    }

    pub fn prefix(&mut self, prefix: &str) {
        self.prefix = prefix.to_string();
    }

}

impl NS for Api {
    fn handlers<'a>(&'a self) -> &'a ApiHandlers { &self.handlers }
    fn handlers_mut<'a>(&'a mut self) -> &'a mut ApiHandlers { &mut self.handlers }
}

impl ApiHandler for Api {
    fn api_call(&self, rest_path: &str, params: &mut JsonObject, req: &mut Request) -> HandleResult<Response> {
        self.call(rest_path, req)
    }
}

impl Handler for Api {
    fn call(&self, rest_path: &str, req: &mut Request) -> HandleResult<Response> {

        // Check prefix
        let mut rest_path = if self.prefix.len() > 0 {
            if rest_path.slice_from(1).starts_with(self.prefix.as_slice()) {
                rest_path.slice_from(self.prefix.len() + 1)
            } else {
               return Err(NotMatchError.abstract())
            }
        } else {
            rest_path
        };

        // Check version
        if self.version.is_some() {
            let version = self.version.as_ref().unwrap();
            let versioning = self.versioning.as_ref().unwrap();

            match versioning {
                &PathVersioning => {
                    if rest_path.slice_from(1).starts_with(version.as_slice()) {
                        rest_path = rest_path.slice_from(version.len() + 1)
                    } else {
                       return Err(NotMatchError.abstract())
                    }
                },
                _ => ()
            }
        }

        println!("Prefix {} {}", rest_path, self.prefix);

        self.call_handlers(rest_path, &mut TreeMap::new(), req)
    }
}<commit_msg>feat: implement ParamVersioning in Api<commit_after>
use collections::treemap::TreeMap;
use serialize::json::{JsonObject};

use valico::Builder as ValicoBuilder;

use request::Request;
use response::Response;
use middleware::{Handler, HandleResult, Error, NotMatchError};

pub use self::endpoint::{Endpoint, EndpointBuilder};
pub use self::client::Client;
pub use self::namespace::{Namespace, NS, ApiHandlers};

mod endpoint;
mod namespace;
mod client;

pub type ValicoBuildHandler<'a> = |&mut ValicoBuilder|:'a;

#[deriving(Show)]
pub struct QueryStringDecodeError;

impl Error for QueryStringDecodeError {
    fn name(&self) -> &'static str {
        return "QueryStringDecodeError";
    }
}

#[deriving(Show)]
pub struct ValidationError {
    reason: JsonObject
}

impl Error for ValidationError {
    fn name(&self) -> &'static str {
        return "ValidationError";
    }
}

#[deriving(Show)]
pub struct BodyDecodeError {
    reason: String
}

impl BodyDecodeError {
    pub fn new(reason: String) -> BodyDecodeError {
        return BodyDecodeError {
            reason: reason
        }
    }
}

impl Error for BodyDecodeError {
    fn name(&self) -> &'static str {
        return "BodyDecodeError";
    }

    fn description(&self) -> Option<&str> {
        return Some(self.reason.as_slice())
    }
}

pub trait ApiHandler {
    fn api_call(&self, &str, &mut JsonObject, &mut Request) -> HandleResult<Response>;
}

pub enum Versioning {
    PathVersioning,
    AcceptHeaderVersioning(&'static str),
    AcceptVersionHeaderVersioning,
    ParamVersioning(&'static str)
}

#[deriving(Send)]
pub struct Api {
    pub version: Option<String>,
    pub versioning: Option<Versioning>,
    pub prefix: String,
    handlers: ApiHandlers
}

impl Api {

    pub fn new() -> Api {
        Api {
            version: None,
            versioning: None,
            prefix: "".to_string(),
            handlers: vec![]
        }
    }

    pub fn build(builder: |&mut Api|) -> Api {
        let mut api = Api::new();
        builder(&mut api);

        return api;
    }

    pub fn version(&mut self, version: &str, versioning: Versioning) {
        self.version = Some(version.to_string());
        self.versioning = Some(versioning);
    }

    pub fn prefix(&mut self, prefix: &str) {
        self.prefix = prefix.to_string();
    }

}

impl NS for Api {
    fn handlers<'a>(&'a self) -> &'a ApiHandlers { &self.handlers }
    fn handlers_mut<'a>(&'a mut self) -> &'a mut ApiHandlers { &mut self.handlers }
}

impl ApiHandler for Api {
    fn api_call(&self, rest_path: &str, params: &mut JsonObject, req: &mut Request) -> HandleResult<Response> {
        self.call(rest_path, req)
    }
}

impl Handler for Api {
    fn call(&self, rest_path: &str, req: &mut Request) -> HandleResult<Response> {

        // Check prefix
        let mut rest_path = if self.prefix.len() > 0 {
            if rest_path.slice_from(1).starts_with(self.prefix.as_slice()) {
                rest_path.slice_from(self.prefix.len() + 1)
            } else {
               return Err(NotMatchError.abstract())
            }
        } else {
            rest_path
        };

        // Check version
        if self.version.is_some() {
            let version = self.version.as_ref().unwrap();
            let versioning = self.versioning.as_ref().unwrap();

            match versioning {
                &PathVersioning => {
                    if rest_path.slice_from(1).starts_with(version.as_slice()) {
                        rest_path = rest_path.slice_from(version.len() + 1)
                    } else {
                       return Err(NotMatchError.abstract())
                    }
                },
                &ParamVersioning(ref param_name) => {
                    match req.url.query_pairs() {
                        Some(query_pairs) => {
                            if !query_pairs.iter().any(|&(ref key, ref val)| key.as_slice() == *param_name && val == version) {
                                return Err(NotMatchError.abstract())
                            }
                        },
                        None => return Err(NotMatchError.abstract())
                    }
                }
                _ => ()
            }
        }

        println!("Prefix {} {}", rest_path, self.prefix);

        self.call_handlers(rest_path, &mut TreeMap::new(), req)
    }
}<|endoftext|>


<commit_before>/* Implements a systemd password agent as per
http://www.freedesktop.org/wiki/Software/systemd/PasswordAgents/ */

use inotify::INotify;
use inotify::ffi::*;
use std::ffi::{CString, OsStr};
use std::fs::{self, File};
use std::io::{self, Read, Error, ErrorKind};
use std::path::Path;
use std::thread::sleep_ms;
use regex::Regex;

use socket;
use yubikey::*;

const SYSTEMD_ASK_PATH: &'static str = "/run/systemd/ask-password";
const SYSTEMD_ASK_MSG: &'static str = "Please enter passphrase for disk";

/// Check for existing ask requests before starting the watch loop
pub fn check_existing_asks() -> io::Result<()> {
    let ask_dir = Path::new(SYSTEMD_ASK_PATH);
    //    if ask_dir.is_dir() {} // This isn't stable yet
    for entry in try!(fs::read_dir(ask_dir)) {
        let entry = try!(entry);
        let entry_path =  entry.path();
        if handle_existing(entry_path.as_path()) {
            return Ok(())
        }
    }

    return Err(Error::new(ErrorKind::Other, "No existing asks found"));
}

/// Checks whether an existing file has the correct prefix before dispatching.
/// Return value indicates whether an event was handled or not.
fn handle_existing(filepath: &Path) -> bool {
    let ask_name = OsStr::new("ask");
    let stem = filepath.file_stem();

    match stem {
        Some(s) if s == ask_name => handle_ask(filepath),
        _ => false
    }
}

/// Watch systemd's ask password folder for incoming requests.
#[allow(unused_must_use)]
pub fn watch_ask_loop(mut timeout: u8) {
    let mut ino = INotify::init().unwrap();
    ino.add_watch(Path::new(SYSTEMD_ASK_PATH), IN_CLOSE_WRITE | IN_MOVED_TO).unwrap();

    'outer: loop {
        if timeout <= 0 {
            // Why do these types mismatch in inotify-rs?
            ino.rm_watch((IN_CLOSE_WRITE | IN_MOVED_TO) as i32);
            break;
        }

        let events = ino.available_events().unwrap();
        for event in events.iter() {
            if event.name.starts_with("ask.") {
                let full_path = format!("{}/{}", SYSTEMD_ASK_PATH, event.name);
                let filepath = Path::new(&full_path);
                if handle_ask(&filepath) {
                    break 'outer;
                }
            }
        }

        timeout -= 1;
        sleep_ms(1000);
    }
}

fn get_challenge() -> &'static [u8] {
    b"iCaiyoosashohm5yeiRi"
}

/// Handles an incoming inotify event. The return value does not indicate
/// success or failure, rather it indicates whether or not the event was a disk
/// decryption password request.
fn handle_ask(filepath: &Path) -> bool {
    let mut is_pw_ask = false;

    match fs::File::open(filepath) {
        Err(e)   => println!("{}", e),
        Ok(mut file) => match parse_ask(&mut file, &mut is_pw_ask) {
            Ok(_)  => println!("Responded successfully"),
            Err(e) => println!("{}", e)
        }
    }

    is_pw_ask
}

fn parse_ask(file: &mut File, is_pw_ask: &mut bool) -> io::Result<()> {
    let challenge = get_challenge();

    let mut file_content = String::new();
    try!(file.read_to_string(&mut file_content));

    if file_content.contains(SYSTEMD_ASK_MSG) {
        *is_pw_ask = true;
        match capture_socket(&file_content) {
            None => Ok(()),
            Some(socket) => handle_respond(challenge, &socket)
        }
    } else {
        Ok(())
    }

    // NYI: kill(PID, 0) -> if ESRCH ignore file (this is in the spec)
    // Currently the only kill() in Rust is deprecated so I'm gonna wait
}

/// Extracts Socket address from an ask file using regexes. An unsafe
/// function is being used because the safe alternative is not marked as stable
/// yet.
/// Rust ensures that content is not mutable in the context of this function.
fn capture_socket(content: &str) -> Option<String> {
    let sock_reg = Regex::new(r"(?m)^Socket=(.*)$").unwrap();
    match sock_reg.find(&content) {
        None => None,
        Some((start, end)) => {
            let socket =
                unsafe { content.slice_unchecked(start + 7, end).to_string() };
            Some(socket)
        }
    }
}

fn handle_respond(challenge: &[u8], socket_path: &str) -> io::Result<()> {
    match get_response(challenge) {
        Err(e) => panic!(e),
        Ok(yk_response) => {
            let response = format!("+{}", yk_response);
            let path_cstr = CString::new(socket_path).unwrap();
            let mut socket = try!(socket::UnixSocket::new());
            socket.sendto(&response.as_bytes(), &path_cstr)
        }
    }
}

fn get_response(challenge: &[u8]) -> Result<String, YubikeyError> {
    let yk = try!(Yubikey::get_yubikey());
    yk.challenge_response(2, &challenge, false)
}
<commit_msg>[askpass] Read challenge from file<commit_after>/* Implements a systemd password agent as per
http://www.freedesktop.org/wiki/Software/systemd/PasswordAgents/ */

use inotify::INotify;
use inotify::ffi::*;
use std::ffi::{CString, OsStr};
use std::fs::{self, File};
use std::io::{self, Read, Error, ErrorKind};
use std::path::Path;
use std::thread::sleep_ms;
use regex::Regex;

use socket;
use yubikey::*;

const SYSTEMD_ASK_PATH: &'static str = "/run/systemd/ask-password";
const SYSTEMD_ASK_MSG: &'static str = "Please enter passphrase for disk";

/// Check for existing ask requests before starting the watch loop
pub fn check_existing_asks() -> io::Result<()> {
    let ask_dir = Path::new(SYSTEMD_ASK_PATH);
    //    if ask_dir.is_dir() {} // This isn't stable yet
    for entry in try!(fs::read_dir(ask_dir)) {
        let entry = try!(entry);
        let entry_path =  entry.path();
        if handle_existing(entry_path.as_path()) {
            return Ok(())
        }
    }

    return Err(Error::new(ErrorKind::Other, "No existing asks found"));
}

/// Checks whether an existing file has the correct prefix before dispatching.
/// Return value indicates whether an event was handled or not.
fn handle_existing(filepath: &Path) -> bool {
    let ask_name = OsStr::new("ask");
    let stem = filepath.file_stem();

    match stem {
        Some(s) if s == ask_name => handle_ask(filepath),
        _ => false
    }
}

/// Watch systemd's ask password folder for incoming requests.
#[allow(unused_must_use)]
pub fn watch_ask_loop(mut timeout: u8) {
    let mut ino = INotify::init().unwrap();
    ino.add_watch(Path::new(SYSTEMD_ASK_PATH), IN_CLOSE_WRITE | IN_MOVED_TO).unwrap();

    'outer: loop {
        if timeout <= 0 {
            // Why do these types mismatch in inotify-rs?
            ino.rm_watch((IN_CLOSE_WRITE | IN_MOVED_TO) as i32);
            break;
        }

        let events = ino.available_events().unwrap();
        for event in events.iter() {
            if event.name.starts_with("ask.") {
                let full_path = format!("{}/{}", SYSTEMD_ASK_PATH, event.name);
                let filepath = Path::new(&full_path);
                if handle_ask(&filepath) {
                    break 'outer;
                }
            }
        }

        timeout -= 1;
        sleep_ms(1000);
    }
}

/// Loads the challenge for a Yubikey from the associated file in
/// /etc/ykfde.d/challenge-${yubikey_serial}.
/// This function will panic if the file is not found or the challenge is not
/// exactly 64 bytes in length, because those errors are not recoverable.
fn get_challenge(yk_serial: u32) -> String {
    let challenge_filepath = format!("/etc/ykfde.d/challenge-{}", yk_serial);
    let mut challenge = String::new();
    println!("Reading challenge from {}", challenge_filepath);
    match fs::File::open(Path::new(&challenge_filepath)) {
        Err(e) => panic!("No challenge found for Yubikey {}. Please enroll!",
                         yk_serial),
        Ok(mut file) => {
            let challenge_len = file.read_to_string(&mut challenge).unwrap();
            // Yubikey max challenge length is 64 bytes. It should also never be less.
            if challenge_len != 64 {
                panic!("Challenge must be exactly 64 bytes, was {}", challenge_len)
            } else {
                challenge
            }
        }
    }
}

/// Handles an incoming inotify event. The return value does not indicate
/// success or failure, rather it indicates whether or not the event was a disk
/// decryption password request.
fn handle_ask(filepath: &Path) -> bool {
    let mut is_pw_ask = false;

    match fs::File::open(filepath) {
        Err(e)   => println!("{}", e),
        Ok(mut file) => match parse_ask(&mut file, &mut is_pw_ask) {
            Ok(_)  => println!("Responded successfully"),
            Err(e) => println!("{}", e)
        }
    }

    is_pw_ask
}

fn parse_ask(file: &mut File, is_pw_ask: &mut bool) -> io::Result<()> {
    let mut file_content = String::new();
    try!(file.read_to_string(&mut file_content));

    if file_content.contains(SYSTEMD_ASK_MSG) {
        *is_pw_ask = true;
        match capture_socket(&file_content) {
            None => Ok(()),
            Some(socket) => handle_respond(&socket)
        }
    } else {
        Ok(())
    }

    // NYI: kill(PID, 0) -> if ESRCH ignore file (this is in the spec)
    // Currently the only kill() in Rust is deprecated so I'm gonna wait
}

/// Extracts Socket address from an ask file using regexes. An unsafe
/// function is being used because the safe alternative is not marked as stable
/// yet.
/// Rust ensures that content is not mutable in the context of this function.
fn capture_socket(content: &str) -> Option<String> {
    let sock_reg = Regex::new(r"(?m)^Socket=(.*)$").unwrap();
    match sock_reg.find(&content) {
        None => None,
        Some((start, end)) => {
            let socket =
                unsafe { content.slice_unchecked(start + 7, end).to_string() };
            Some(socket)
        }
    }
}

fn handle_respond(socket_path: &str) -> io::Result<()> {
    let yk = Yubikey::get_yubikey().unwrap(); // no Yubikey -> Panic!
    let yk_serial = yk.get_serial().unwrap(); // same thing
    let challenge = get_challenge(yk_serial);
    match yk.challenge_response(2, &challenge.as_bytes(), false) {
        Err(e) => panic!("{}", e),
        Ok(yk_response) => {
            let response = format!("+{}", yk_response);
            let path_cstr = CString::new(socket_path).unwrap();
            let mut socket = try!(socket::UnixSocket::new());
            socket.sendto(&response.as_bytes(), &path_cstr)
        }
    }
}

fn get_response(challenge: &[u8]) -> Result<String, YubikeyError> {
    let yk = try!(Yubikey::get_yubikey());
    yk.challenge_response(2, &challenge, false)
}
<|endoftext|>


<commit_before>/// This module provides default futures that can be used for
/// sending request to other microservices by proxy engine.
///

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

use futures::future::{Future};
use futures::Stream;
use json::parse as json_parse;
use lapin_futures_rustls::lapin::channel::{
    BasicConsumeOptions, BasicProperties, BasicPublishOptions, QueueBindOptions,
    QueueDeclareOptions, QueueDeleteOptions, QueueUnbindOptions,
};
use lapin_futures_rustls::lapin::types::{AMQPValue, FieldTable};
use log::error;

use crate::error::PathfinderError;
use crate::rabbitmq::{RabbitMQContext};
use crate::engine::MessageSender;
use crate::engine::options::RpcOptions;
use crate::engine::serializer::Serializer;

/// Simple future that sends a RPC request to the certain microservice,
/// consumes from a response from a separate queue and then returns a
/// response to the caller via transmitter.
pub fn rpc_request_future(
    transmitter: MessageSender,
    rabbitmq_context: Arc<RabbitMQContext>,
    options: Arc<RpcOptions>,
    headers: HashMap<String, String>
) -> Box<Future<Item=(), Error=PathfinderError> + Send + Sync + 'static> {
    let rabbitmq_context_local = rabbitmq_context.clone();
    let publish_channel = rabbitmq_context_local.get_publish_channel();
    let consume_channel = rabbitmq_context_local.get_consume_channel();

    let queue_name = options.get_queue_name().unwrap().clone();
    let queue_declare_options = QueueDeclareOptions {
        passive: false,
        durable: true,
        exclusive: true,
        auto_delete: false,
        ..Default::default()
    };

    Box::new(
        // 1. Declare a response queue
        consume_channel
            .queue_declare(&queue_name, queue_declare_options, FieldTable::new())
            .map(move |queue| (publish_channel, consume_channel, queue, options))
        // 2. Link the response queue the exchange
        .and_then(move |(publish_channel, consume_channel, queue, options)| {
            let queue_name = options.get_queue_name().unwrap().clone();
            let endpoint = options.get_endpoint().unwrap().clone();
            let routing_key = options.get_queue_name().unwrap().clone();

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

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

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

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

            consume_channel
                .basic_ack(message.delivery_tag, false)
                .map(move |_confirmation| (_publish_channel, consume_channel, queue, options))
        })
        // 6. Unbind the response queue from the exchange point
        .and_then(move |(publish_channel, consume_channel, _queue, options)| {
            let queue_name = options.get_queue_name().unwrap().clone();
            let routing_key = options.get_queue_name().unwrap().clone();
            let endpoint = options.get_endpoint().unwrap().clone();

            consume_channel
                .queue_unbind(
                    &queue_name,
                    &endpoint.get_response_exchange(),
                    &routing_key,
                    QueueUnbindOptions::default(),
                    FieldTable::new(),
                )
                .map(move |_| (publish_channel, consume_channel, options))
        })
        // 7. Delete the response queue
        .and_then(move |(_publish_channel, consume_channel, options)| {
            let queue_delete_options = QueueDeleteOptions {
                if_unused: false,
                if_empty: false,
                ..Default::default()
            };
            let queue_name = options.get_queue_name().unwrap().clone();

            consume_channel
                .queue_delete(&queue_name, queue_delete_options)
                .map(move |_| ())
        })
        // 8. Returns the result to the caller as future
        .then(move |result| match result {
            Ok(_) => Ok(()),
            Err(err) => {
                error!("Error in RabbitMQ client. Reason: {}", err);
                let message = String::from("The request wasn't processed. Please, try once again.");
                Err(PathfinderError::MessageBrokerError(message))
            }
        })
    )
}
<commit_msg>Fixed names of unused variables<commit_after>/// This module provides default futures that can be used for
/// sending request to other microservices by proxy engine.
///

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

use futures::future::{Future};
use futures::Stream;
use json::parse as json_parse;
use lapin_futures_rustls::lapin::channel::{
    BasicConsumeOptions, BasicProperties, BasicPublishOptions, QueueBindOptions,
    QueueDeclareOptions, QueueDeleteOptions, QueueUnbindOptions,
};
use lapin_futures_rustls::lapin::types::{AMQPValue, FieldTable};
use log::error;

use crate::error::PathfinderError;
use crate::rabbitmq::{RabbitMQContext};
use crate::engine::MessageSender;
use crate::engine::options::RpcOptions;
use crate::engine::serializer::Serializer;

/// Simple future that sends a RPC request to the certain microservice,
/// consumes from a response from a separate queue and then returns a
/// response to the caller via transmitter.
pub fn rpc_request_future(
    transmitter: MessageSender,
    rabbitmq_context: Arc<RabbitMQContext>,
    options: Arc<RpcOptions>,
    headers: HashMap<String, String>
) -> Box<Future<Item=(), Error=PathfinderError> + Send + Sync + 'static> {
    let rabbitmq_context_local = rabbitmq_context.clone();
    let publish_channel = rabbitmq_context_local.get_publish_channel();
    let consume_channel = rabbitmq_context_local.get_consume_channel();

    let queue_name = options.get_queue_name().unwrap().clone();
    let queue_declare_options = QueueDeclareOptions {
        passive: false,
        durable: true,
        exclusive: true,
        auto_delete: false,
        ..Default::default()
    };

    Box::new(
        // 1. Declare a response queue
        consume_channel
            .queue_declare(&queue_name, queue_declare_options, FieldTable::new())
            .map(move |queue| (publish_channel, consume_channel, queue, options))
        // 2. Link the response queue the exchange
        .and_then(move |(publish_channel, consume_channel, queue, options)| {
            let queue_name = options.get_queue_name().unwrap().clone();
            let endpoint = options.get_endpoint().unwrap().clone();
            let routing_key = options.get_queue_name().unwrap().clone();

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

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

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

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

            consume_channel
                .basic_ack(message.delivery_tag, false)
                .map(move |_confirmation| (publish_channel, consume_channel, queue, options))
        })
        // 6. Unbind the response queue from the exchange point
        .and_then(move |(publish_channel, consume_channel, _queue, options)| {
            let queue_name = options.get_queue_name().unwrap().clone();
            let routing_key = options.get_queue_name().unwrap().clone();
            let endpoint = options.get_endpoint().unwrap().clone();

            consume_channel
                .queue_unbind(
                    &queue_name,
                    &endpoint.get_response_exchange(),
                    &routing_key,
                    QueueUnbindOptions::default(),
                    FieldTable::new(),
                )
                .map(move |_| (publish_channel, consume_channel, options))
        })
        // 7. Delete the response queue
        .and_then(move |(_publish_channel, consume_channel, options)| {
            let queue_delete_options = QueueDeleteOptions {
                if_unused: false,
                if_empty: false,
                ..Default::default()
            };
            let queue_name = options.get_queue_name().unwrap().clone();

            consume_channel
                .queue_delete(&queue_name, queue_delete_options)
                .map(move |_| ())
        })
        // 8. Returns the result to the caller as future
        .then(move |result| match result {
            Ok(_) => Ok(()),
            Err(err) => {
                error!("Error in RabbitMQ client. Reason: {}", err);
                let message = String::from("The request wasn't processed. Please, try once again.");
                Err(PathfinderError::MessageBrokerError(message))
            }
        })
    )
}
<|endoftext|>


<commit_before>//! Logging scopes for slog-rs
//!
//! Logging scopes are convinience functionality for slog-rs to free user from manually passing
//! `Logger` objects around.
//!
//! Set of macros is also provided as an alternative to original `slog` crate macros, for logging
//! directly to `Logger` of the current logging scope.
//!
//! Note: Part of a `slog` logging philosophy is ability to freely express logging contexts
//! according to logical structure, rather than code structure. By using logging scopes the logging
//! context is tied to code flow again, which is less expressive.
//!
//! It is generally advised **NOT** to use `slog_scope` in libraries. Read more in
//! [slog-rs FAQ](https://github.com/slog-rs/slog/wiki/FAQ#do-i-have-to-pass-logger-around)
//!
//! ```
//! #[macro_use(o, slog_info, slog_log, slog_record, slog_record_static, slog_b)]
//! extern crate slog;
//! #[macro_use]
//! extern crate slog_scope;
//! extern crate slog_term;
//!
//! use slog::Drain;
//!
//! fn foo() {
//!     slog_info!(slog_scope::logger(), "foo");
//!     info!("foo"); // Same as above, but more ergonomic.
//! }
//!
//! fn main() {
//!     let plain = slog_term::PlainSyncDecorator::new(std::io::stdout());
//!     let log = slog::Logger::root(
//!         slog_term::FullFormat::new(plain)
//!         .build().fuse(), o!()
//!     );
//!
//!     slog_scope::set_global_logger(log);
//!     slog_scope::scope(slog_scope::logger().new(o!("scope" => "1")),
//!         || foo()
//!     );
//! }

#![warn(missing_docs)]

#[macro_use(o)]
extern crate slog;
#[macro_use]
extern crate lazy_static;
extern crate crossbeam;

use slog::*;

use std::sync::Arc;
use std::cell::RefCell;
use crossbeam::sync::ArcCell;

/// Log a critical level message using current scope logger
#[macro_export] macro_rules! crit( ($($args:tt)+) => { slog_crit![$crate::logger(), $($args)+]; };);
/// Log a error level message using current scope logger
#[macro_export] macro_rules! error( ($($args:tt)+) => { slog_error![$crate::logger(), $($args)+]; };);
/// Log a warning level message using current scope logger
#[macro_export] macro_rules! warn( ($($args:tt)+) => { slog_warn![$crate::logger(), $($args)+]; };);
/// Log a info level message using current scope logger
#[macro_export] macro_rules! info( ($($args:tt)+) => { slog_info![$crate::logger(), $($args)+]; };);
/// Log a debug level message using current scope logger
#[macro_export] macro_rules! debug( ($($args:tt)+) => { slog_debug![$crate::logger(), $($args)+]; };);
/// Log a trace level message using current scope logger
#[macro_export] macro_rules! trace( ($($args:tt)+) => { slog_trace![$crate::logger(), $($args)+]; };);

thread_local! {
    static TL_SCOPES: RefCell<Vec<slog::Logger>> = RefCell::new(Vec::with_capacity(8))
}

lazy_static! {
    static ref GLOBAL_LOGGER : ArcCell<slog::Logger> = ArcCell::new(
        Arc::new(
            slog::Logger::root(slog::Discard, o!())
        )
    );
}

/// Set global `Logger` that is returned by calls like `logger()` outside of any logging scope.
pub fn set_global_logger(l: slog::Logger) {
    let _ = GLOBAL_LOGGER.set(Arc::new(l));
}

struct ScopeGuard;

impl ScopeGuard {
    fn new(logger: slog::Logger) -> Self {
        TL_SCOPES.with(|s| {
            s.borrow_mut().push(logger);
        });

        ScopeGuard
    }
}

impl Drop for ScopeGuard {
    fn drop(&mut self) {
        TL_SCOPES.with(|s| {
            s.borrow_mut().pop().expect("TL_SCOPES should contain a logger");
        })
    }
}

/// Access the `Logger` for the current logging scope
pub fn logger() -> Logger {
    TL_SCOPES.with(|s| {
        let s = s.borrow();
        match s.last() {
          Some(logger) => logger.clone(),
          None => (*GLOBAL_LOGGER.get()).clone(),
        }
    })
}

/// Execute code in a logging scope
///
/// Logging scopes allow using a `slog::Logger` without explicitly
/// passing it in the code.
///
/// At any time current active `Logger` for a given thread can be retrived
/// with `logger()` call.
///
/// Logging scopes can be nested and are panic safe.
///
/// `logger` is the `Logger` to use during the duration of `f`.
/// `with_current_logger` can be used to build it as a child of currently active
/// logger.
///
/// `f` is a code to be executed in the logging scope.
///
/// Note: Thread scopes are thread-local. Each newly spawned thread starts
/// with a global logger, as a current logger.
pub fn scope<SF, R>(logger: slog::Logger, f: SF) -> R
    where SF: FnOnce() -> R
{
    let _guard = ScopeGuard::new(logger);
    f()
}
<commit_msg>Round of `rustfmt`<commit_after>//! Logging scopes for slog-rs
//!
//! Logging scopes are convinience functionality for slog-rs to free user from manually passing
//! `Logger` objects around.
//!
//! Set of macros is also provided as an alternative to original `slog` crate macros, for logging
//! directly to `Logger` of the current logging scope.
//!
//! Note: Part of a `slog` logging philosophy is ability to freely express logging contexts
//! according to logical structure, rather than code structure. By using logging scopes the logging
//! context is tied to code flow again, which is less expressive.
//!
//! It is generally advised **NOT** to use `slog_scope` in libraries. Read more in
//! [slog-rs FAQ](https://github.com/slog-rs/slog/wiki/FAQ#do-i-have-to-pass-logger-around)
//!
//! ```
//! #[macro_use(o, slog_info, slog_log, slog_record, slog_record_static, slog_b)]
//! extern crate slog;
//! #[macro_use]
//! extern crate slog_scope;
//! extern crate slog_term;
//!
//! use slog::Drain;
//!
//! fn foo() {
//!     slog_info!(slog_scope::logger(), "foo");
//!     info!("foo"); // Same as above, but more ergonomic.
//! }
//!
//! fn main() {
//!     let plain = slog_term::PlainSyncDecorator::new(std::io::stdout());
//!     let log = slog::Logger::root(
//!         slog_term::FullFormat::new(plain)
//!         .build().fuse(), o!()
//!     );
//!
//!     slog_scope::set_global_logger(log);
//!     slog_scope::scope(slog_scope::logger().new(o!("scope" => "1")),
//!         || foo()
//!     );
//! }

#![warn(missing_docs)]

#[macro_use(o)]
extern crate slog;
#[macro_use]
extern crate lazy_static;
extern crate crossbeam;

use slog::*;

use std::sync::Arc;
use std::cell::RefCell;
use crossbeam::sync::ArcCell;

/// Log a critical level message using current scope logger
#[macro_export]
macro_rules! crit( ($($args:tt)+) => { slog_crit![$crate::logger(), $($args)+]; };);
/// Log a error level message using current scope logger
#[macro_export]
macro_rules! error( ($($args:tt)+) => { slog_error![$crate::logger(), $($args)+]; };);
/// Log a warning level message using current scope logger
#[macro_export]
macro_rules! warn( ($($args:tt)+) => { slog_warn![$crate::logger(), $($args)+]; };);
/// Log a info level message using current scope logger
#[macro_export]
macro_rules! info( ($($args:tt)+) => { slog_info![$crate::logger(), $($args)+]; };);
/// Log a debug level message using current scope logger
#[macro_export]
macro_rules! debug( ($($args:tt)+) => { slog_debug![$crate::logger(), $($args)+]; };);
/// Log a trace level message using current scope logger
#[macro_export]
macro_rules! trace( ($($args:tt)+) => { slog_trace![$crate::logger(), $($args)+]; };);

thread_local! {
    static TL_SCOPES: RefCell<Vec<slog::Logger>> = RefCell::new(Vec::with_capacity(8))
}

lazy_static! {
    static ref GLOBAL_LOGGER : ArcCell<slog::Logger> = ArcCell::new(
        Arc::new(
            slog::Logger::root(slog::Discard, o!())
        )
    );
}

/// Set global `Logger` that is returned by calls like `logger()` outside of any logging scope.
pub fn set_global_logger(l: slog::Logger) {
    let _ = GLOBAL_LOGGER.set(Arc::new(l));
}

struct ScopeGuard;

impl ScopeGuard {
    fn new(logger: slog::Logger) -> Self {
        TL_SCOPES.with(|s| { s.borrow_mut().push(logger); });

        ScopeGuard
    }
}

impl Drop for ScopeGuard {
    fn drop(&mut self) {
        TL_SCOPES.with(|s| { s.borrow_mut().pop().expect("TL_SCOPES should contain a logger"); })
    }
}

/// Access the `Logger` for the current logging scope
pub fn logger() -> Logger {
    TL_SCOPES.with(|s| {
        let s = s.borrow();
        match s.last() {
            Some(logger) => logger.clone(),
            None => (*GLOBAL_LOGGER.get()).clone(),
        }
    })
}

/// Execute code in a logging scope
///
/// Logging scopes allow using a `slog::Logger` without explicitly
/// passing it in the code.
///
/// At any time current active `Logger` for a given thread can be retrived
/// with `logger()` call.
///
/// Logging scopes can be nested and are panic safe.
///
/// `logger` is the `Logger` to use during the duration of `f`.
/// `with_current_logger` can be used to build it as a child of currently active
/// logger.
///
/// `f` is a code to be executed in the logging scope.
///
/// Note: Thread scopes are thread-local. Each newly spawned thread starts
/// with a global logger, as a current logger.
pub fn scope<SF, R>(logger: slog::Logger, f: SF) -> R
    where SF: FnOnce() -> R
{
    let _guard = ScopeGuard::new(logger);
    f()
}
<|endoftext|>