5 #ifndef PACKIO_NL_JSON_RPC_RPC_H 
    6 #define PACKIO_NL_JSON_RPC_RPC_H 
   11 #include <nlohmann/json.hpp> 
   14 #include "../args_specs.h" 
   15 #include "../internal/config.h" 
   16 #include "../internal/expected.h" 
   17 #include "../internal/log.h" 
   18 #include "../internal/rpc.h" 
   19 #include "incremental_buffers.h" 
   22 namespace nl_json_rpc {
 
   25 template <
typename... Args>
 
   26 constexpr 
bool positional_args_v = (!is_arg_v<Args> && ...);
 
   28 template <
typename... Args>
 
   29 constexpr 
bool named_args_v = 
sizeof...(Args) > 0 && (is_arg_v<Args> && ...);
 
   31 using id_type = nlohmann::json;
 
   32 using native_type = nlohmann::json;
 
   33 using packio::internal::expected;
 
   34 using packio::internal::unexpected;
 
   54     expected<request, std::string> get_request()
 
   58             return unexpected{
"no request parsed"};
 
   60         auto object = std::move(*parsed_);
 
   62         return parse_request(std::move(
object));
 
   65     expected<response, std::string> get_response()
 
   69             return unexpected{
"no response parsed"};
 
   71         auto object = std::move(*parsed_);
 
   73         return parse_response(std::move(
object));
 
   78         return incremental_buffers_.in_place_buffer();
 
   81     std::size_t buffer_capacity()
 const 
   83         return incremental_buffers_.in_place_buffer_capacity();
 
   86     void buffer_consumed(std::size_t bytes)
 
   88         incremental_buffers_.in_place_buffer_consumed(bytes);
 
   91     void reserve_buffer(std::size_t bytes)
 
   93         incremental_buffers_.reserve_in_place_buffer(bytes);
 
   97     void try_parse_object()
 
  102         auto buffer = incremental_buffers_.get_parsed_buffer();
 
  104             parsed_ = nlohmann::json::parse(*buffer);
 
  108     static expected<response, std::string> parse_response(nlohmann::json&& res)
 
  110         auto id_it = res.find(
"id");
 
  111         auto result_it = res.find(
"result");
 
  112         auto error_it = res.find(
"error");
 
  114         if (id_it == end(res)) {
 
  115             return unexpected{
"missing id field"};
 
  117         if (result_it == end(res) && error_it == end(res)) {
 
  118             return unexpected{
"missing error and result field"};
 
  122         parsed.id = std::move(*id_it);
 
  123         if (error_it != end(res)) {
 
  124             parsed.error = std::move(*error_it);
 
  126         if (result_it != end(res)) {
 
  127             parsed.result = std::move(*result_it);
 
  129         return {std::move(parsed)};
 
  132     static expected<request, std::string> parse_request(nlohmann::json&& req)
 
  134         auto id_it = req.find(
"id");
 
  135         auto method_it = req.find(
"method");
 
  136         auto params_it = req.find(
"params");
 
  138         if (method_it == end(req)) {
 
  139             return unexpected{
"missing method field"};
 
  141         if (!method_it->is_string()) {
 
  142             return unexpected{
"method field is not a string"};
 
  146         parsed.method = method_it->get<std::string>();
 
  147         if (params_it == end(req) || params_it->is_null()) {
 
  148             parsed.args = nlohmann::json::array();
 
  150         else if (!params_it->is_array() && !params_it->is_object()) {
 
  151             return unexpected{
"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);
 
  164         return {std::move(parsed)};
 
  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 F>
 
  323     static internal::expected<T, std::string> extract_args(
 
  324         const nlohmann::json& args,
 
  325         const args_specs<F>& specs)
 
  328             if (args.is_array()) {
 
  329                 return convert_positional_args<T>(args, specs);
 
  331             else if (args.is_object()) {
 
  332                 return convert_named_args<T>(args, specs);
 
  335                 throw std::runtime_error{
"arguments are not a structured type"};
 
  338         catch (
const std::exception& exc) {
 
  339             return internal::unexpected{
 
  340                 std::string{
"cannot convert arguments: "} + exc.what()};
 
  345     template <
typename T, 
typename F>
 
  346     static constexpr T convert_positional_args(
 
  347         const nlohmann::json& array,
 
  348         const args_specs<F>& specs)
 
  350         return convert_positional_args<T>(
 
  351             array, specs, std::make_index_sequence<args_specs<F>::size()>());
 
  354     template <
typename T, 
typename F, std::size_t... Idxs>
 
  355     static constexpr T convert_positional_args(
 
  356         const nlohmann::json& array,
 
  357         const args_specs<F>& specs,
 
  358         std::index_sequence<Idxs...>)
 
  360         if (!specs.options().allow_extra_arguments
 
  361             && array.size() > std::tuple_size_v<T>) {
 
  362             throw std::runtime_error{
"too many arguments"};
 
  365             if (Idxs < array.size()) {
 
  367                     return array.at(Idxs).get<std::tuple_element_t<Idxs, T>>();
 
  369                 catch (const ::nlohmann::json::type_error&) {
 
  370                     throw std::runtime_error{
 
  371                         "invalid type for argument " 
  372                         + specs.template get<Idxs>().name()};
 
  375             if (
const auto& value = specs.template get<Idxs>().default_value()) {
 
  378             throw std::runtime_error{
 
  379                 "no value for argument " + specs.template get<Idxs>().name()};
 
  383     template <
typename T, 
typename F>
 
  384     static T convert_named_args(
const nlohmann::json& args, 
const args_specs<F>& specs)
 
  386         return convert_named_args<T>(
 
  387             args, specs, std::make_index_sequence<args_specs<F>::size()>{});
 
  390     template <
typename T, 
typename F, std::size_t... Idxs>
 
  391     static T convert_named_args(
 
  392         const nlohmann::json& args,
 
  393         const args_specs<F>& specs,
 
  394         std::index_sequence<Idxs...>)
 
  396         if (!specs.options().allow_extra_arguments) {
 
  397             const std::array<
const std::string*, 
sizeof...(Idxs)>
 
  398                 available_arguments = {&specs.template get<Idxs>().name()...};
 
  399             for (
auto it = args.begin(); it != args.end(); ++it) {
 
  400                 auto arg_it = std::find_if(
 
  401                     available_arguments.begin(),
 
  402                     available_arguments.end(),
 
  403                     [&](
const std::string* arg) { return *arg == it.key(); });
 
  404                 if (arg_it == available_arguments.end()) {
 
  405                     throw std::runtime_error{
"unexpected argument " + it.key()};
 
  411             auto it = args.find(specs.template get<Idxs>().name());
 
  412             if (it != args.end()) {
 
  414                     return it->template get<std::tuple_element_t<Idxs, T>>();
 
  416                 catch (const ::nlohmann::json::type_error&) {
 
  417                     throw std::runtime_error{
 
  418                         "invalid type for argument " 
  419                         + specs.template get<Idxs>().name()};
 
  422             if (
const auto& value = specs.template get<Idxs>().default_value()) {
 
  425             throw std::runtime_error{
 
  426                 "no value for argument " + specs.template get<Idxs>().name()};
 
The incremental parser for JSON-RPC objects.
Definition: rpc.h:52
 
The JSON-RPC protocol implementation.
Definition: rpc.h:174
 
internal::native_type native_type
The native type of the serialization library.
Definition: rpc.h:180
 
internal::id_type id_type
Type of the call ID.
Definition: rpc.h:177
 
The packio namespace.
Definition: arg.h:14
 
The object representing a client request.
Definition: rpc.h:37
 
The object representing the response to a call.
Definition: rpc.h:45