1 #pragma once
2 
3 #ifndef RESPONSE_H_MVRZEKPX
4 #define RESPONSE_H_MVRZEKPX
5 
6 #include "rpc/detail/log.h"
7 #include "rpc/detail/make_unique.h"
8 #include "rpc/msgpack.hpp"
9 #include "rpc/config.h"
10 
11 namespace rpc {
12 namespace detail {
13 
14 //! \brief Represents a response and creates a msgpack to be sent back
15 //! as per the msgpack-rpc spec.
16 class response {
17 public:
18     //! \brief Creates a response that represents a normal return value.
19     //! \param id The sequence id (as per protocol).
20     //! \param result The return value to store in the response.
21     //! \tparam T Any msgpack-able type.
22     //! \note If there is both an error and result in the response,
23     //! the result will be discarded while packing the data.
24     template <typename T> static response make_result(uint32_t id, T &&result);
25 
26     //! \brief Creates a response that represents an error.
27     //! \param id The sequence id (as per protocol).
28     //! \param error The error value to store in the response.
29     //! \tparam T Any msgpack-able type.
30     template <typename T> static response make_error(uint32_t id, T &&error);
31 
32 
33     //! \brief Constructs a response from RPCLIB_MSGPACK::object (useful when
34     //! reading a response from a stream).
35     response(RPCLIB_MSGPACK::object_handle o);
36 
37     //! \brief Gets the response data as a RPCLIB_MSGPACK::sbuffer.
38     RPCLIB_MSGPACK::sbuffer get_data() const;
39 
40     //! \brief Moves the specified object_handle into the response
41     //! as a result.
42     //! \param r The result to capture.
43     void capture_result(RPCLIB_MSGPACK::object_handle &r);
44 
45     //! \brief Moves the specified object_handle into the response as an error.
46     //! \param e The error to capture.
47     void capture_error(RPCLIB_MSGPACK::object_handle &e);
48 
49     //! \brief Returns the call id/index used to identify which call
50     //! this response corresponds to.
51     uint32_t get_id() const;
52 
53     //! \brief Returns the error object stored in the response. Can
54     //! be empty.
55     std::shared_ptr<RPCLIB_MSGPACK::object_handle> get_error() const;
56 
57     //! \brief Returns the result stored in the response. Can be empty.
58     std::shared_ptr<RPCLIB_MSGPACK::object_handle> get_result() const;
59 
60     //! \brief Gets an empty response which means "no response" (not to be
61     //! confused with void return, i.e. this means literally
62     //! "don't write the response to the socket")
63     static response empty();
64 
65     //! \brief If true, this response is empty (\see empty())
66     bool is_empty() const;
67 
68     //! \brief The type of a response, according to the msgpack-rpc spec
69     using response_type =
70         std::tuple<uint32_t, uint32_t, RPCLIB_MSGPACK::object, RPCLIB_MSGPACK::object>;
71 
72 private:
73     //! \brief Default constructor for responses.
74     response();
75 
76     uint32_t id_;
77     // I really wish to avoid shared_ptr here but at this point asio does not
78     // work with move-only handlers in post() and I need to capture responses
79     // in lambdas.
80     std::shared_ptr<RPCLIB_MSGPACK::object_handle> error_;
81     std::shared_ptr<RPCLIB_MSGPACK::object_handle> result_;
82     bool empty_;
83     RPCLIB_CREATE_LOG_CHANNEL(response)
84 };
85 
86 template <typename T>
make_result(uint32_t id,T && result)87 inline response response::make_result(uint32_t id, T &&result) {
88     auto z = rpc::detail::make_unique<RPCLIB_MSGPACK::zone>();
89     RPCLIB_MSGPACK::object o(std::forward<T>(result), *z);
90     response inst;
91     inst.id_ = id;
92     inst.result_ = std::make_shared<RPCLIB_MSGPACK::object_handle>(o, std::move(z));
93     return inst;
94 }
95 
96 template <>
97 inline response
make_result(uint32_t id,std::unique_ptr<RPCLIB_MSGPACK::object_handle> && r)98 response::make_result(uint32_t id, std::unique_ptr<RPCLIB_MSGPACK::object_handle> &&r) {
99     response inst;
100     inst.id_ = id;
101     inst.result_ = std::move(r);
102     return inst;
103 }
104 
105 template <typename T>
make_error(uint32_t id,T && error)106 inline response response::make_error(uint32_t id, T &&error) {
107     auto z = rpc::detail::make_unique<RPCLIB_MSGPACK::zone>();
108     RPCLIB_MSGPACK::object o(std::forward<T>(error), *z);
109     response inst;
110     inst.id_ = id;
111     inst.error_ = std::make_shared<RPCLIB_MSGPACK::object_handle>(o, std::move(z));
112     return inst;
113 }
114 
115 } /* detail */
116 
117 } /* rpc  */
118 
119 #endif /* end of include guard: RESPONSE_H_MVRZEKPX */
120