5 #ifndef PACKIO_NL_JSON_RPC_RPC_H
6 #define PACKIO_NL_JSON_RPC_RPC_H
10 #include <nlohmann/json.hpp>
13 #include "../internal/config.h"
14 #include "../internal/log.h"
15 #include "../internal/rpc.h"
16 #include "incremental_buffers.h"
19 namespace nl_json_rpc {
22 template <
typename... Args>
23 constexpr
bool positional_args_v = (!is_arg_v<Args> && ...);
25 template <
typename... Args>
26 constexpr
bool named_args_v =
sizeof...(Args) > 0 && (is_arg_v<Args> && ...);
28 using id_type = nlohmann::json;
29 using native_type = nlohmann::json;
49 std::optional<request> get_request()
55 auto object = std::move(*parsed_);
57 return parse_request(std::move(
object));
60 std::optional<response> get_response()
66 auto object = std::move(*parsed_);
68 return parse_response(std::move(
object));
73 return incremental_buffers_.in_place_buffer();
76 std::size_t buffer_capacity()
const
78 return incremental_buffers_.in_place_buffer_capacity();
81 void buffer_consumed(std::size_t bytes)
83 incremental_buffers_.in_place_buffer_consumed(bytes);
86 void reserve_buffer(std::size_t bytes)
88 incremental_buffers_.reserve_in_place_buffer(bytes);
92 void try_parse_object()
97 auto buffer = incremental_buffers_.get_parsed_buffer();
99 parsed_ = nlohmann::json::parse(*buffer);
103 static std::optional<response> parse_response(nlohmann::json&& res)
105 auto id_it = res.find(
"id");
106 auto result_it = res.find(
"result");
107 auto error_it = res.find(
"error");
109 if (id_it == end(res)) {
110 PACKIO_ERROR(
"missing id field");
113 if (result_it == end(res) && error_it == end(res)) {
114 PACKIO_ERROR(
"missing error and result field");
118 std::optional<response> parsed{std::in_place};
119 parsed->id = std::move(*id_it);
120 if (error_it != end(res)) {
121 parsed->error = std::move(*error_it);
123 if (result_it != end(res)) {
124 parsed->result = std::move(*result_it);
129 static std::optional<request> parse_request(nlohmann::json&& req)
131 auto id_it = req.find(
"id");
132 auto method_it = req.find(
"method");
133 auto params_it = req.find(
"params");
135 if (method_it == end(req)) {
136 PACKIO_ERROR(
"missing method field");
139 if (!method_it->is_string()) {
140 PACKIO_ERROR(
"method field is not a string");
144 std::optional<request> parsed{std::in_place};
145 parsed->method = method_it->get<std::string>();
146 if (params_it == end(req) || params_it->is_null()) {
147 parsed->args = nlohmann::json::array();
149 else if (!params_it->is_array() && !params_it->is_object()) {
150 PACKIO_ERROR(
"non-structured arguments are not supported");
154 parsed->args = std::move(*params_it);
157 if (id_it == end(req) || id_it->is_null()) {
158 parsed->type = call_type::notification;
161 parsed->type = call_type::request;
162 parsed->id = std::move(*id_it);
167 std::optional<nlohmann::json> parsed_;
168 incremental_buffers incremental_buffers_;
191 static std::string format_id(
const id_type&
id)
196 template <
typename... Args>
197 static auto serialize_notification(std::string_view method, Args&&... args)
198 -> std::enable_if_t<internal::positional_args_v<Args...>, std::string>
200 return nlohmann::json({
204 nlohmann::json::array({nlohmann::json(
205 std::forward<Args>(args))...})},
210 template <
typename... Args>
211 static auto serialize_notification(std::string_view method, Args&&... args)
212 -> std::enable_if_t<internal::named_args_v<Args...>, std::string>
214 return nlohmann::json({
217 {
"params", {{args.name, args.value}...}},
222 template <
typename... Args>
223 static auto serialize_notification(std::string_view, Args&&...) -> std::enable_if_t<
224 !internal::positional_args_v<Args...> && !internal::named_args_v<Args...>,
228 internal::positional_args_v<Args...> || internal::named_args_v<Args...>,
229 "JSON-RPC does not support mixed named and unnamed arguments");
232 template <
typename... Args>
233 static auto serialize_request(
235 std::string_view method,
237 -> std::enable_if_t<internal::positional_args_v<Args...>, std::string>
239 return nlohmann::json({
243 nlohmann::json::array({nlohmann::json(
244 std::forward<Args>(args))...})},
250 template <
typename... Args>
251 static auto serialize_request(
253 std::string_view method,
255 -> std::enable_if_t<internal::named_args_v<Args...>, std::string>
257 return nlohmann::json({
260 {
"params", {{args.name, args.value}...}},
266 template <
typename... Args>
267 static auto serialize_request(
const id_type&, std::string_view, Args&&...)
269 !internal::positional_args_v<Args...> && !internal::named_args_v<Args...>,
273 internal::positional_args_v<Args...> || internal::named_args_v<Args...>,
274 "JSON-RPC does not support mixed named and unnamed arguments");
277 static std::string serialize_response(
const id_type&
id)
279 return serialize_response(
id, nlohmann::json{});
282 template <
typename T>
283 static std::string serialize_response(
const id_type&
id, T&& value)
285 return nlohmann::json{
288 {
"result", std::forward<T>(value)},
293 template <
typename T>
294 static std::string serialize_error_response(
const id_type&
id, T&& value)
296 return nlohmann::json{
301 nlohmann::json error = {
303 {
"data", std::forward<T>(value)},
305 if (error[
"data"].is_string()) {
306 error[
"message"] = error[
"data"];
309 error[
"message"] =
"Unknown error";
317 static net::const_buffer buffer(
const std::string& buf)
319 return net::const_buffer(buf.data(), buf.size());
322 template <
typename T,
typename NamesContainer>
323 static std::optional<T> extract_args(
324 const nlohmann::json& args,
325 const NamesContainer& names)
328 if (args.is_array()) {
329 if (args.size() != std::tuple_size_v<T>) {
333 "cannot convert args: wrong number of arguments");
337 return args.get<T>();
339 else if (args.is_object()) {
340 return convert_named_args<T>(args, names);
343 PACKIO_ERROR(
"arguments are not a structured type");
347 catch (
const std::exception& exc) {
348 PACKIO_WARN(
"cannot convert args: {}", exc.what());
355 template <
typename T,
typename NamesContainer>
356 static T convert_named_args(
const nlohmann::json& args,
const NamesContainer& names)
358 return convert_named_args<T>(
359 args, names, std::make_index_sequence<std::tuple_size_v<T>>{});
362 template <
typename T,
typename NamesContainer, std::size_t... Idxs>
363 static T convert_named_args(
364 const nlohmann::json& args,
365 const NamesContainer& names,
366 std::index_sequence<Idxs...>)
368 return T{(args.at(names.at(Idxs))
369 .template get<std::tuple_element_t<Idxs, T>>())...};
376 #endif // PACKIO_NL_JSON_RPC_RPC_H