//! Macros managing requests and responses.
//!
//! Our protocol uses the Result(T) type to communicate rich errors to
//! the client.  These macros deal with sending a request using the
//! asynchronous I/O framework Tokio, and decoding the result.
//!
//! This implementation uses macros instead of functions to interface
//! with the code stubs generated by the RPC compiler.

// Sends request and decodes result.
//
// Sends the given request and decodes the result.
macro_rules! make_request {
    ( $core: expr, $request: expr ) => {{
        use node::result::Which;

        let r: std::result::Result<Result<_>, capnp::Error> = $core.run(
            $request.send().promise
                .and_then(|response| -> Promise<Result<_>, capnp::Error> {
                    let r = pry!(pry!(pry!(response.get()).get_result()).which());
                    let r = match r {
                        /* The Result.  */
                        Which::Ok(Ok(x)) => Ok(x),
                        Which::Err(Ok(e)) => Err(failure::Error::from(e)),
                        /* Protocol violations.  */
                        Which::Ok(Err(e)) => Err(failure::Error::from(e)),
                        Which::Err(Err(e)) => Err(failure::Error::from(e)),
                    };
                    Promise::ok(r)
                }));
        r?
    }}
}

macro_rules! make_request_map {
    ( $core: expr, $request: expr, $map: expr ) => {{
        use node::result::Which;

        let r: std::result::Result<Result<_>, capnp::Error> = $core.run(
            $request.send().promise
                .and_then(|response| -> Promise<Result<_>, capnp::Error> {
                    let r = pry!(pry!(pry!(response.get()).get_result()).which());
                    let r = match r {
                        /* The Result.  */
                        Which::Ok(Ok(x)) => $map(x),
                        Which::Err(Ok(e)) => Err(failure::Error::from(e)),
                        /* Protocol violations.  */
                        Which::Ok(Err(e)) => Err(failure::Error::from(e)),
                        Which::Err(Err(e)) => Err(failure::Error::from(e)),
                    };
                    Promise::ok(r)
                }));
        r?
    }}
}

/// These macros are for server functions.  Because they use the
/// 'results' parameter, they must be bound explicitly at the
/// beginning of the function.
macro_rules! bind_results {
    ( $results: ident ) => {
        #[allow(unused)]
        const DEBUG_BACKEND_ERRORS: bool = false;

        /// Behaves like `return Err(_)` for server functions.
        #[allow(unused_macros)]
        macro_rules! fail {
            ( $expr:expr ) => {{
                if DEBUG_BACKEND_ERRORS {
                    eprintln!("{}:{}: {:?}", file!(), line!(), $expr);
                }
                pry!($results.get().get_result()).set_err($expr);
                return Promise::ok(());
            }};
        }

        /// Behaves like `try!` for server functions.
        ///
        /// If the given expression evaluates to Err(_), the error is
        /// stored in the result and the function terminates.
        #[allow(unused_macros)]
        macro_rules! sry {
            ( $expr:expr ) => {{
                match $expr {
                    Ok(x) => x,
                    Err(x) => {
                        if DEBUG_BACKEND_ERRORS {
                            eprintln!("{}:{}: {:?}", file!(), line!(), x);
                        }
                        pry!($results.get().get_result()).set_err(x.into());
                        return Promise::ok(());
                    },
                }
            }};
        }
    };
}
