/* Copyright (c) 2018-2024 Marcelo Zimbres Silva (mzimbres@gmail.com) * * Distributed under the Boost Software License, Version 1.0. (See * accompanying file LICENSE.txt) */ #ifndef BOOST_REDIS_ADAPTER_RESPONSE_TRAITS_HPP #define BOOST_REDIS_ADAPTER_RESPONSE_TRAITS_HPP #include #include #include #include #include #include #include #include #include #include #include namespace boost::redis::adapter::detail { /* Traits class for response objects. * * Provides traits for all supported response types i.e. all STL * containers and C++ buil-in types. */ template struct result_traits { using adapter_type = wrapper::type>; static auto adapt(Result& r) noexcept { return adapter_type{&r}; } }; template <> struct result_traits> { using response_type = result; using adapter_type = ignore; static auto adapt(response_type) noexcept { return adapter_type{}; } }; template <> struct result_traits { using response_type = ignore_t; using adapter_type = ignore; static auto adapt(response_type) noexcept { return adapter_type{}; } }; template struct result_traits>> { using response_type = result>; using adapter_type = adapter::detail::general_simple; static auto adapt(response_type& v) noexcept { return adapter_type{&v}; } }; template struct result_traits, Allocator>>> { using response_type = result, Allocator>>; using adapter_type = adapter::detail::general_aggregate; static auto adapt(response_type& v) noexcept { return adapter_type{&v}; } }; template using adapter_t = typename result_traits>::adapter_type; template auto internal_adapt(T& t) noexcept { return result_traits>::adapt(t); } template struct assigner { template static void assign(T1& dest, T2& from) { dest[N].template emplace(internal_adapt(std::get(from))); assigner::assign(dest, from); } }; template <> struct assigner<0> { template static void assign(T1& dest, T2& from) { dest[0].template emplace<0>(internal_adapt(std::get<0>(from))); } }; template class static_aggregate_adapter; template class static_aggregate_adapter> { private: using adapters_array_type = std::array< mp11::mp_rename, std::variant>, std::tuple_size::value>; // Tuple element we are currently on. std::size_t i_ = 0; // Nested aggregate element counter. std::size_t aggregate_size_ = 0; adapters_array_type adapters_; result* res_ = nullptr; public: explicit static_aggregate_adapter(result* r = nullptr) { if (r) { res_ = r; detail::assigner::value - 1>::assign(adapters_, r->value()); } } template void count(resp3::basic_node const& elem) { if (elem.depth == 1 && is_aggregate(elem.data_type)) { aggregate_size_ = element_multiplicity(elem.data_type) * elem.aggregate_size; } if (aggregate_size_ == 0) { i_ += 1; } else { aggregate_size_ -= 1; } } void on_init() { using std::visit; for (auto& adapter : adapters_) { visit( [&](auto& arg) { arg.on_init(); }, adapter); } } void on_done() { using std::visit; for (auto& adapter : adapters_) { visit( [&](auto& arg) { arg.on_done(); }, adapter); } } template void on_node(resp3::basic_node const& elem, system::error_code& ec) { using std::visit; if (elem.depth == 0) { auto const multiplicity = element_multiplicity(elem.data_type); auto const real_aggr_size = elem.aggregate_size * multiplicity; if (real_aggr_size != std::tuple_size::value) ec = redis::error::incompatible_size; return; } visit( [&](auto& arg) { arg.on_node(elem, ec); }, adapters_.at(i_)); count(elem); } }; template struct result_traits>> { using response_type = result>; using adapter_type = static_aggregate_adapter; static auto adapt(response_type& r) noexcept { return adapter_type{&r}; } }; } // namespace boost::redis::adapter::detail #endif // BOOST_REDIS_ADAPTER_RESPONSE_TRAITS_HPP