result_traits.hpp 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. /* Copyright (c) 2018-2023 Marcelo Zimbres Silva (mzimbres@gmail.com)
  2. *
  3. * Distributed under the Boost Software License, Version 1.0. (See
  4. * accompanying file LICENSE.txt)
  5. */
  6. #ifndef BOOST_REDIS_ADAPTER_RESPONSE_TRAITS_HPP
  7. #define BOOST_REDIS_ADAPTER_RESPONSE_TRAITS_HPP
  8. #include <boost/redis/error.hpp>
  9. #include <boost/redis/resp3/type.hpp>
  10. #include <boost/redis/ignore.hpp>
  11. #include <boost/redis/adapter/detail/adapters.hpp>
  12. #include <boost/redis/adapter/result.hpp>
  13. #include <boost/redis/adapter/ignore.hpp>
  14. #include <boost/mp11.hpp>
  15. #include <vector>
  16. #include <tuple>
  17. #include <string_view>
  18. #include <variant>
  19. namespace boost::redis::adapter::detail
  20. {
  21. /* Traits class for response objects.
  22. *
  23. * Provides traits for all supported response types i.e. all STL
  24. * containers and C++ buil-in types.
  25. */
  26. template <class Result>
  27. struct result_traits {
  28. using adapter_type = adapter::detail::wrapper<typename std::decay<Result>::type>;
  29. static auto adapt(Result& r) noexcept { return adapter_type{&r}; }
  30. };
  31. template <>
  32. struct result_traits<result<ignore_t>> {
  33. using response_type = result<ignore_t>;
  34. using adapter_type = ignore;
  35. static auto adapt(response_type) noexcept { return adapter_type{}; }
  36. };
  37. template <>
  38. struct result_traits<ignore_t> {
  39. using response_type = ignore_t;
  40. using adapter_type = ignore;
  41. static auto adapt(response_type) noexcept { return adapter_type{}; }
  42. };
  43. template <class T>
  44. struct result_traits<result<resp3::basic_node<T>>> {
  45. using response_type = result<resp3::basic_node<T>>;
  46. using adapter_type = adapter::detail::general_simple<response_type>;
  47. static auto adapt(response_type& v) noexcept { return adapter_type{&v}; }
  48. };
  49. template <class String, class Allocator>
  50. struct result_traits<result<std::vector<resp3::basic_node<String>, Allocator>>> {
  51. using response_type = result<std::vector<resp3::basic_node<String>, Allocator>>;
  52. using adapter_type = adapter::detail::general_aggregate<response_type>;
  53. static auto adapt(response_type& v) noexcept { return adapter_type{&v}; }
  54. };
  55. template <class T>
  56. using adapter_t = typename result_traits<std::decay_t<T>>::adapter_type;
  57. template<class T>
  58. auto internal_adapt(T& t) noexcept
  59. { return result_traits<std::decay_t<T>>::adapt(t); }
  60. template <std::size_t N>
  61. struct assigner {
  62. template <class T1, class T2>
  63. static void assign(T1& dest, T2& from)
  64. {
  65. dest[N].template emplace<N>(internal_adapt(std::get<N>(from)));
  66. assigner<N - 1>::assign(dest, from);
  67. }
  68. };
  69. template <>
  70. struct assigner<0> {
  71. template <class T1, class T2>
  72. static void assign(T1& dest, T2& from)
  73. {
  74. dest[0].template emplace<0>(internal_adapt(std::get<0>(from)));
  75. }
  76. };
  77. template <class Tuple>
  78. class static_aggregate_adapter;
  79. template <class Tuple>
  80. class static_aggregate_adapter<result<Tuple>> {
  81. private:
  82. using adapters_array_type =
  83. std::array<
  84. mp11::mp_rename<
  85. mp11::mp_transform<
  86. adapter_t, Tuple>,
  87. std::variant>,
  88. std::tuple_size<Tuple>::value>;
  89. std::size_t i_ = 0;
  90. std::size_t aggregate_size_ = 0;
  91. adapters_array_type adapters_;
  92. result<Tuple>* res_ = nullptr;
  93. public:
  94. explicit static_aggregate_adapter(result<Tuple>* r = nullptr)
  95. {
  96. if (r) {
  97. res_ = r;
  98. detail::assigner<std::tuple_size<Tuple>::value - 1>::assign(adapters_, r->value());
  99. }
  100. }
  101. template <class String>
  102. void count(resp3::basic_node<String> const& nd)
  103. {
  104. if (nd.depth == 1) {
  105. if (is_aggregate(nd.data_type))
  106. aggregate_size_ = element_multiplicity(nd.data_type) * nd.aggregate_size;
  107. else
  108. ++i_;
  109. return;
  110. }
  111. if (--aggregate_size_ == 0)
  112. ++i_;
  113. }
  114. template <class String>
  115. void operator()(resp3::basic_node<String> const& nd, system::error_code& ec)
  116. {
  117. using std::visit;
  118. if (nd.depth == 0) {
  119. auto const real_aggr_size = nd.aggregate_size * element_multiplicity(nd.data_type);
  120. if (real_aggr_size != std::tuple_size<Tuple>::value)
  121. ec = redis::error::incompatible_size;
  122. return;
  123. }
  124. visit([&](auto& arg){arg(nd, ec);}, adapters_[i_]);
  125. count(nd);
  126. }
  127. };
  128. template <class... Ts>
  129. struct result_traits<result<std::tuple<Ts...>>>
  130. {
  131. using response_type = result<std::tuple<Ts...>>;
  132. using adapter_type = static_aggregate_adapter<response_type>;
  133. static auto adapt(response_type& r) noexcept { return adapter_type{&r}; }
  134. };
  135. } // boost::redis::adapter::detail
  136. #endif // BOOST_REDIS_ADAPTER_RESPONSE_TRAITS_HPP