/* Copyright (c) 2018-2023 Marcelo Zimbres Silva (mzimbres@gmail.com) * * Distributed under the Boost Software License, Version 1.0. (See * accompanying file LICENSE.txt) */ #ifndef BOOST_REDIS_ADAPTER_ADAPTERS_HPP #define BOOST_REDIS_ADAPTER_ADAPTERS_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // See https://stackoverflow.com/a/31658120/1077832 #include #ifdef _LIBCPP_VERSION #else #include #endif namespace boost::redis::adapter::detail { // Serialization. template auto boost_redis_from_bulk(T& i, std::string_view sv, system::error_code& ec) -> typename std::enable_if::value, void>::type { auto const res = std::from_chars(sv.data(), sv.data() + std::size(sv), i); if (res.ec != std::errc()) ec = redis::error::not_a_number; } inline void boost_redis_from_bulk(bool& t, std::string_view sv, system::error_code&) { t = *sv.data() == 't'; } inline void boost_redis_from_bulk(double& d, std::string_view sv, system::error_code& ec) { #ifdef _LIBCPP_VERSION // The string in sv is not null terminated and we also don't know // if there is enough space at the end for a null char. The easiest // thing to do is to create a temporary. std::string const tmp{sv.data(), sv.data() + std::size(sv)}; char* end{}; d = std::strtod(tmp.data(), &end); if (d == HUGE_VAL || d == 0) ec = redis::error::not_a_double; #else auto const res = std::from_chars(sv.data(), sv.data() + std::size(sv), d); if (res.ec != std::errc()) ec = redis::error::not_a_double; #endif // _LIBCPP_VERSION } template void boost_redis_from_bulk( std::basic_string& s, std::string_view sv, system::error_code&) { s.append(sv.data(), sv.size()); } //================================================ template class general_aggregate { private: Result* result_; public: explicit general_aggregate(Result* c = nullptr): result_(c) {} template void operator()(resp3::basic_node const& nd, system::error_code&) { BOOST_ASSERT_MSG(!!result_, "Unexpected null pointer"); switch (nd.data_type) { case resp3::type::blob_error: case resp3::type::simple_error: *result_ = error{nd.data_type, std::string{std::cbegin(nd.value), std::cend(nd.value)}}; break; default: result_->value().push_back({nd.data_type, nd.aggregate_size, nd.depth, std::string{std::cbegin(nd.value), std::cend(nd.value)}}); } } }; template class general_simple { private: Node* result_; public: explicit general_simple(Node* t = nullptr) : result_(t) {} template void operator()(resp3::basic_node const& nd, system::error_code&) { BOOST_ASSERT_MSG(!!result_, "Unexpected null pointer"); switch (nd.data_type) { case resp3::type::blob_error: case resp3::type::simple_error: *result_ = error{nd.data_type, std::string{std::cbegin(nd.value), std::cend(nd.value)}}; break; default: result_->value().data_type = nd.data_type; result_->value().aggregate_size = nd.aggregate_size; result_->value().depth = nd.depth; result_->value().value.assign(nd.value.data(), nd.value.size()); } } }; template class simple_impl { public: void on_value_available(Result&) {} template void operator()(Result& result, resp3::basic_node const& n, system::error_code& ec) { if (is_aggregate(n.data_type)) { ec = redis::error::expects_resp3_simple_type; return; } boost_redis_from_bulk(result, n.value, ec); } }; template class set_impl { private: typename Result::iterator hint_; public: void on_value_available(Result& result) { hint_ = std::end(result); } template void operator()(Result& result, resp3::basic_node const& nd, system::error_code& ec) { if (is_aggregate(nd.data_type)) { if (nd.data_type != resp3::type::set) ec = redis::error::expects_resp3_set; return; } BOOST_ASSERT(nd.aggregate_size == 1); if (nd.depth < 1) { ec = redis::error::expects_resp3_set; return; } typename Result::key_type obj; boost_redis_from_bulk(obj, nd.value, ec); hint_ = result.insert(hint_, std::move(obj)); } }; template class map_impl { private: typename Result::iterator current_; bool on_key_ = true; public: void on_value_available(Result& result) { current_ = std::end(result); } template void operator()(Result& result, resp3::basic_node const& nd, system::error_code& ec) { if (is_aggregate(nd.data_type)) { if (element_multiplicity(nd.data_type) != 2) ec = redis::error::expects_resp3_map; return; } BOOST_ASSERT(nd.aggregate_size == 1); if (nd.depth < 1) { ec = redis::error::expects_resp3_map; return; } if (on_key_) { typename Result::key_type obj; boost_redis_from_bulk(obj, nd.value, ec); current_ = result.insert(current_, {std::move(obj), {}}); } else { typename Result::mapped_type obj; boost_redis_from_bulk(obj, nd.value, ec); current_->second = std::move(obj); } on_key_ = !on_key_; } }; template class vector_impl { public: void on_value_available(Result& ) { } template void operator()(Result& result, resp3::basic_node const& nd, system::error_code& ec) { if (is_aggregate(nd.data_type)) { auto const m = element_multiplicity(nd.data_type); result.reserve(result.size() + m * nd.aggregate_size); } else { result.push_back({}); boost_redis_from_bulk(result.back(), nd.value, ec); } } }; template class array_impl { private: int i_ = -1; public: void on_value_available(Result& ) { } template void operator()(Result& result, resp3::basic_node const& nd, system::error_code& ec) { if (is_aggregate(nd.data_type)) { if (i_ != -1) { ec = redis::error::nested_aggregate_not_supported; return; } if (result.size() != nd.aggregate_size * element_multiplicity(nd.data_type)) { ec = redis::error::incompatible_size; return; } } else { if (i_ == -1) { ec = redis::error::expects_resp3_aggregate; return; } BOOST_ASSERT(nd.aggregate_size == 1); boost_redis_from_bulk(result.at(i_), nd.value, ec); } ++i_; } }; template struct list_impl { void on_value_available(Result& ) { } template void operator()(Result& result, resp3::basic_node const& nd, system::error_code& ec) { if (!is_aggregate(nd.data_type)) { BOOST_ASSERT(nd.aggregate_size == 1); if (nd.depth < 1) { ec = redis::error::expects_resp3_aggregate; return; } result.push_back({}); boost_redis_from_bulk(result.back(), nd.value, ec); } } }; //--------------------------------------------------- template struct impl_map { using type = simple_impl; }; template struct impl_map> { using type = set_impl>; }; template struct impl_map> { using type = set_impl>; }; template struct impl_map> { using type = set_impl>; }; template struct impl_map> { using type = set_impl>; }; template struct impl_map> { using type = map_impl>; }; template struct impl_map> { using type = map_impl>; }; template struct impl_map> { using type = map_impl>; }; template struct impl_map> { using type = map_impl>; }; template struct impl_map> { using type = vector_impl>; }; template struct impl_map> { using type = array_impl>; }; template struct impl_map> { using type = list_impl>; }; template struct impl_map> { using type = list_impl>; }; //--------------------------------------------------- template class wrapper; template class wrapper> { public: using response_type = result; private: response_type* result_; typename impl_map::type impl_; template bool set_if_resp3_error(resp3::basic_node const& nd) noexcept { switch (nd.data_type) { case resp3::type::null: case resp3::type::simple_error: case resp3::type::blob_error: *result_ = error{nd.data_type, {std::cbegin(nd.value), std::cend(nd.value)}}; return true; default: return false; } } public: explicit wrapper(response_type* t = nullptr) : result_(t) { if (result_) { result_->value() = Result{}; impl_.on_value_available(result_->value()); } } template void operator()(resp3::basic_node const& nd, system::error_code& ec) { BOOST_ASSERT_MSG(!!result_, "Unexpected null pointer"); if (result_->has_error()) return; if (set_if_resp3_error(nd)) return; BOOST_ASSERT(result_); impl_(result_->value(), nd, ec); } }; template class wrapper>> { public: using response_type = result>; private: response_type* result_; typename impl_map::type impl_{}; template bool set_if_resp3_error(resp3::basic_node const& nd) noexcept { switch (nd.data_type) { case resp3::type::blob_error: case resp3::type::simple_error: *result_ = error{nd.data_type, {std::cbegin(nd.value), std::cend(nd.value)}}; return true; default: return false; } } public: explicit wrapper(response_type* o = nullptr) : result_(o) {} template void operator()( resp3::basic_node const& nd, system::error_code& ec) { BOOST_ASSERT_MSG(!!result_, "Unexpected null pointer"); if (result_->has_error()) return; if (set_if_resp3_error(nd)) return; if (nd.data_type == resp3::type::null) return; if (!result_->value().has_value()) { result_->value() = T{}; impl_.on_value_available(result_->value().value()); } impl_(result_->value().value(), nd, ec); } }; } // boost::redis::adapter::detail #endif // BOOST_REDIS_ADAPTER_ADAPTERS_HPP