result_traits.hpp 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. /* Copyright (c) 2018-2024 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/adapter/detail/adapters.hpp>
  9. #include <boost/redis/adapter/ignore.hpp>
  10. #include <boost/redis/adapter/result.hpp>
  11. #include <boost/redis/error.hpp>
  12. #include <boost/redis/ignore.hpp>
  13. #include <boost/redis/resp3/type.hpp>
  14. #include <boost/mp11.hpp>
  15. #include <string_view>
  16. #include <tuple>
  17. #include <variant>
  18. #include <vector>
  19. namespace boost::redis::adapter::detail {
  20. /* Traits class for response objects.
  21. *
  22. * Provides traits for all supported response types i.e. all STL
  23. * containers and C++ buil-in types.
  24. */
  25. template <class Result>
  26. struct result_traits {
  27. using adapter_type = wrapper<typename std::decay<Result>::type>;
  28. static auto adapt(Result& r) noexcept { return adapter_type{&r}; }
  29. };
  30. template <>
  31. struct result_traits<result<ignore_t>> {
  32. using response_type = result<ignore_t>;
  33. using adapter_type = ignore;
  34. static auto adapt(response_type) noexcept { return adapter_type{}; }
  35. };
  36. template <>
  37. struct result_traits<ignore_t> {
  38. using response_type = ignore_t;
  39. using adapter_type = ignore;
  40. static auto adapt(response_type) noexcept { return adapter_type{}; }
  41. };
  42. template <class T>
  43. struct result_traits<result<resp3::basic_node<T>>> {
  44. using response_type = result<resp3::basic_node<T>>;
  45. using adapter_type = adapter::detail::general_simple<response_type>;
  46. static auto adapt(response_type& v) noexcept { return adapter_type{&v}; }
  47. };
  48. template <class String, class Allocator>
  49. struct result_traits<result<std::vector<resp3::basic_node<String>, Allocator>>> {
  50. using response_type = result<std::vector<resp3::basic_node<String>, Allocator>>;
  51. using adapter_type = adapter::detail::general_aggregate<response_type>;
  52. static auto adapt(response_type& v) noexcept { return adapter_type{&v}; }
  53. };
  54. template <class T>
  55. using adapter_t = typename result_traits<std::decay_t<T>>::adapter_type;
  56. template <class T>
  57. auto internal_adapt(T& t) noexcept
  58. {
  59. return result_traits<std::decay_t<T>>::adapt(t);
  60. }
  61. template <std::size_t N>
  62. struct assigner {
  63. template <class T1, class T2>
  64. static void assign(T1& dest, T2& from)
  65. {
  66. dest[N].template emplace<N>(internal_adapt(std::get<N>(from)));
  67. assigner<N - 1>::assign(dest, from);
  68. }
  69. };
  70. template <>
  71. struct assigner<0> {
  72. template <class T1, class T2>
  73. static void assign(T1& dest, T2& from)
  74. {
  75. dest[0].template emplace<0>(internal_adapt(std::get<0>(from)));
  76. }
  77. };
  78. template <class Tuple>
  79. class static_aggregate_adapter;
  80. template <class Tuple>
  81. class static_aggregate_adapter<result<Tuple>> {
  82. private:
  83. using adapters_array_type = std::array<
  84. mp11::mp_rename<mp11::mp_transform<adapter_t, Tuple>, std::variant>,
  85. std::tuple_size<Tuple>::value>;
  86. // Tuple element we are currently on.
  87. std::size_t i_ = 0;
  88. // Nested aggregate element counter.
  89. std::size_t aggregate_size_ = 0;
  90. adapters_array_type adapters_;
  91. result<Tuple>* res_ = nullptr;
  92. public:
  93. explicit static_aggregate_adapter(result<Tuple>* r = nullptr)
  94. {
  95. if (r) {
  96. res_ = r;
  97. detail::assigner<std::tuple_size<Tuple>::value - 1>::assign(adapters_, r->value());
  98. }
  99. }
  100. template <class String>
  101. void count(resp3::basic_node<String> const& elem)
  102. {
  103. if (elem.depth == 1 && is_aggregate(elem.data_type)) {
  104. aggregate_size_ = element_multiplicity(elem.data_type) * elem.aggregate_size;
  105. }
  106. if (aggregate_size_ == 0) {
  107. i_ += 1;
  108. } else {
  109. aggregate_size_ -= 1;
  110. }
  111. }
  112. void on_init()
  113. {
  114. using std::visit;
  115. for (auto& adapter : adapters_) {
  116. visit(
  117. [&](auto& arg) {
  118. arg.on_init();
  119. },
  120. adapter);
  121. }
  122. }
  123. void on_done()
  124. {
  125. using std::visit;
  126. for (auto& adapter : adapters_) {
  127. visit(
  128. [&](auto& arg) {
  129. arg.on_done();
  130. },
  131. adapter);
  132. }
  133. }
  134. template <class String>
  135. void on_node(resp3::basic_node<String> const& elem, system::error_code& ec)
  136. {
  137. using std::visit;
  138. if (elem.depth == 0) {
  139. auto const multiplicity = element_multiplicity(elem.data_type);
  140. auto const real_aggr_size = elem.aggregate_size * multiplicity;
  141. if (real_aggr_size != std::tuple_size<Tuple>::value)
  142. ec = redis::error::incompatible_size;
  143. return;
  144. }
  145. visit(
  146. [&](auto& arg) {
  147. arg.on_node(elem, ec);
  148. },
  149. adapters_.at(i_));
  150. count(elem);
  151. }
  152. };
  153. template <class... Ts>
  154. struct result_traits<result<std::tuple<Ts...>>> {
  155. using response_type = result<std::tuple<Ts...>>;
  156. using adapter_type = static_aggregate_adapter<response_type>;
  157. static auto adapt(response_type& r) noexcept { return adapter_type{&r}; }
  158. };
  159. } // namespace boost::redis::adapter::detail
  160. #endif // BOOST_REDIS_ADAPTER_RESPONSE_TRAITS_HPP