7#ifndef BOOST_REDIS_ADAPTER_ADAPTERS_HPP
8#define BOOST_REDIS_ADAPTER_ADAPTERS_HPP
10#include <boost/redis/adapter/result.hpp>
11#include <boost/redis/error.hpp>
12#include <boost/redis/resp3/node.hpp>
13#include <boost/redis/resp3/serialization.hpp>
14#include <boost/redis/resp3/type.hpp>
16#include <boost/assert.hpp>
21#include <forward_list>
27#include <system_error>
29#include <unordered_map>
30#include <unordered_set>
39namespace boost::redis::adapter::detail {
42template <
class T>
struct is_integral_number : std::is_integral<T> { };
43template <>
struct is_integral_number<bool> : std::false_type { };
44template <>
struct is_integral_number<char> : std::false_type { };
45template <>
struct is_integral_number<char16_t> : std::false_type { };
46template <>
struct is_integral_number<char32_t> : std::false_type { };
47template <>
struct is_integral_number<wchar_t> : std::false_type { };
49template <>
struct is_integral_number<char8_t> : std::false_type { };
52template <class T, bool = is_integral_number<T>::value>
56struct converter<T, true> {
57 template <
class String>
58 static void apply(T& i, resp3::basic_node<String>
const& node, system::error_code& ec)
60 auto const res = std::from_chars(
node.value.data(),
node.value.data() +
node.value.size(), i);
61 if (res.ec != std::errc())
67struct converter<bool, false> {
68 template <
class String>
69 static void apply(
bool& t, resp3::basic_node<String>
const& node, system::error_code&)
71 t = *
node.value.data() ==
't';
76struct converter<double, false> {
77 template <
class String>
78 static void apply(
double& d, resp3::basic_node<String>
const& node, system::error_code& ec)
84 std::string
const tmp{
node.value.data(),
node.value.data() +
node.value.size()};
86 d = std::strtod(tmp.data(), &end);
87 if (d == HUGE_VAL || d == 0)
90 auto const res = std::from_chars(
node.value.data(),
node.value.data() +
node.value.size(), d);
91 if (res.ec != std::errc())
97template <
class CharT,
class Traits,
class Allocator>
98struct converter<std::basic_string<CharT, Traits, Allocator>, false> {
99 template <
class String>
101 std::basic_string<CharT, Traits, Allocator>& s,
102 resp3::basic_node<String>
const& node,
105 s.append(
node.value.data(),
node.value.size());
110struct from_bulk_impl {
111 template <
class String>
112 static void apply(T& t, resp3::basic_node<String>
const& node, system::error_code& ec)
114 converter<T>::apply(t, node, ec);
119struct from_bulk_impl<std::optional<T>> {
120 template <
class String>
122 std::optional<T>& op,
123 resp3::basic_node<String>
const& node,
124 system::error_code& ec)
128 converter<T>::apply(op.value(), node, ec);
133template <
class T,
class String>
134void boost_redis_from_bulk(T& t, resp3::basic_node<String>
const& node, system::error_code& ec)
136 from_bulk_impl<T>::apply(t, node, ec);
141template <
class Result>
142class general_aggregate {
147 explicit general_aggregate(Result* c =
nullptr)
150 template <
class String>
151 void operator()(resp3::basic_node<String>
const& nd, system::error_code&)
153 BOOST_ASSERT_MSG(!!result_,
"Unexpected null pointer");
154 switch (nd.data_type) {
159 std::string{std::cbegin(nd.value), std::cend(nd.value)}
163 result_->value().push_back({
167 std::string{std::cbegin(nd.value), std::cend(nd.value)}
174class general_simple {
179 explicit general_simple(Node* t =
nullptr)
183 template <
class String>
184 void operator()(resp3::basic_node<String>
const& nd, system::error_code&)
186 BOOST_ASSERT_MSG(!!result_,
"Unexpected null pointer");
187 switch (nd.data_type) {
192 std::string{std::cbegin(nd.value), std::cend(nd.value)}
196 result_->value().data_type = nd.data_type;
197 result_->value().aggregate_size = nd.aggregate_size;
198 result_->value().depth = nd.depth;
199 result_->value().value.assign(nd.value.data(), nd.value.size());
204template <
class Result>
207 void on_value_available(Result&) { }
209 template <
class String>
210 void operator()(Result&
result, resp3::basic_node<String>
const& node, system::error_code& ec)
212 if (is_aggregate(
node.data_type)) {
217 boost_redis_from_bulk(
result, node, ec);
221template <
class Result>
224 typename Result::iterator hint_;
227 void on_value_available(Result&
result) { hint_ = std::end(
result); }
229 template <
class String>
230 void operator()(Result&
result, resp3::basic_node<String>
const& nd, system::error_code& ec)
232 if (is_aggregate(nd.data_type)) {
238 BOOST_ASSERT(nd.aggregate_size == 1);
245 typename Result::key_type obj;
246 boost_redis_from_bulk(obj, nd, ec);
247 hint_ =
result.insert(hint_, std::move(obj));
251template <
class Result>
254 typename Result::iterator current_;
258 void on_value_available(Result&
result) { current_ = std::end(
result); }
260 template <
class String>
261 void operator()(Result&
result, resp3::basic_node<String>
const& nd, system::error_code& ec)
263 if (is_aggregate(nd.data_type)) {
264 if (element_multiplicity(nd.data_type) != 2)
269 BOOST_ASSERT(nd.aggregate_size == 1);
277 typename Result::key_type obj;
278 boost_redis_from_bulk(obj, nd, ec);
279 current_ =
result.insert(current_, {std::move(obj), {}});
281 typename Result::mapped_type obj;
282 boost_redis_from_bulk(obj, nd, ec);
283 current_->second = std::move(obj);
290template <
class Result>
293 void on_value_available(Result&) { }
295 template <
class String>
296 void operator()(Result&
result, resp3::basic_node<String>
const& nd, system::error_code& ec)
298 if (is_aggregate(nd.data_type)) {
299 auto const m = element_multiplicity(nd.data_type);
303 boost_redis_from_bulk(
result.back(), nd, ec);
308template <
class Result>
314 void on_value_available(Result&) { }
316 template <
class String>
317 void operator()(Result&
result, resp3::basic_node<String>
const& nd, system::error_code& ec)
319 if (is_aggregate(nd.data_type)) {
325 if (
result.size() != nd.aggregate_size * element_multiplicity(nd.data_type)) {
335 BOOST_ASSERT(nd.aggregate_size == 1);
336 boost_redis_from_bulk(
result.at(i_), nd, ec);
343template <
class Result>
345 void on_value_available(Result&) { }
347 template <
class String>
348 void operator()(Result&
result, resp3::basic_node<String>
const& nd, system::error_code& ec)
350 if (!is_aggregate(nd.data_type)) {
351 BOOST_ASSERT(nd.aggregate_size == 1);
358 boost_redis_from_bulk(
result.back(), nd, ec);
367 using type = simple_impl<T>;
370template <
class Key,
class Compare,
class Allocator>
371struct impl_map<std::
set<Key, Compare, Allocator>> {
372 using type = set_impl<std::set<Key, Compare, Allocator>>;
375template <
class Key,
class Compare,
class Allocator>
376struct impl_map<std::multiset<Key, Compare, Allocator>> {
377 using type = set_impl<std::multiset<Key, Compare, Allocator>>;
380template <
class Key,
class Hash,
class KeyEqual,
class Allocator>
381struct impl_map<std::unordered_set<Key, Hash, KeyEqual, Allocator>> {
382 using type = set_impl<std::unordered_set<Key, Hash, KeyEqual, Allocator>>;
385template <
class Key,
class Hash,
class KeyEqual,
class Allocator>
386struct impl_map<std::unordered_multiset<Key, Hash, KeyEqual, Allocator>> {
387 using type = set_impl<std::unordered_multiset<Key, Hash, KeyEqual, Allocator>>;
390template <
class Key,
class T,
class Compare,
class Allocator>
391struct impl_map<std::
map<Key, T, Compare, Allocator>> {
392 using type = map_impl<std::map<Key, T, Compare, Allocator>>;
395template <
class Key,
class T,
class Compare,
class Allocator>
396struct impl_map<std::multimap<Key, T, Compare, Allocator>> {
397 using type = map_impl<std::multimap<Key, T, Compare, Allocator>>;
400template <
class Key,
class Hash,
class KeyEqual,
class Allocator>
401struct impl_map<std::unordered_map<Key, Hash, KeyEqual, Allocator>> {
402 using type = map_impl<std::unordered_map<Key, Hash, KeyEqual, Allocator>>;
405template <
class Key,
class Hash,
class KeyEqual,
class Allocator>
406struct impl_map<std::unordered_multimap<Key, Hash, KeyEqual, Allocator>> {
407 using type = map_impl<std::unordered_multimap<Key, Hash, KeyEqual, Allocator>>;
410template <
class T,
class Allocator>
411struct impl_map<std::vector<T, Allocator>> {
412 using type = vector_impl<std::vector<T, Allocator>>;
415template <
class T, std::
size_t N>
416struct impl_map<std::
array<T, N>> {
417 using type = array_impl<std::array<T, N>>;
420template <
class T,
class Allocator>
421struct impl_map<std::list<T, Allocator>> {
422 using type = list_impl<std::list<T, Allocator>>;
425template <
class T,
class Allocator>
426struct impl_map<std::deque<T, Allocator>> {
427 using type = list_impl<std::deque<T, Allocator>>;
441 response_type* result_;
442 typename impl_map<T>::type impl_;
443 bool called_once_ =
false;
445 template <
class String>
446 bool set_if_resp3_error(resp3::basic_node<String>
const& nd)
noexcept
448 switch (nd.data_type) {
454 {std::cbegin(nd.value), std::cend(nd.value)}
457 default:
return false;
462 explicit wrapper(response_type* t =
nullptr)
466 result_->value() = T{};
467 impl_.on_value_available(result_->value());
471 template <
class String>
472 void operator()(resp3::basic_node<String>
const& nd, system::error_code& ec)
474 BOOST_ASSERT_MSG(!!result_,
"Unexpected null pointer");
476 if (result_->has_error())
479 if (!std::exchange(called_once_,
true) && set_if_resp3_error(nd))
482 BOOST_ASSERT(result_);
483 impl_(result_->value(), nd, ec);
488class wrapper<
result<std::optional<T>>> {
493 response_type* result_;
494 typename impl_map<T>::type impl_{};
495 bool called_once_ =
false;
497 template <
class String>
498 bool set_if_resp3_error(resp3::basic_node<String>
const& nd)
noexcept
500 switch (nd.data_type) {
505 {std::cbegin(nd.value), std::cend(nd.value)}
508 default:
return false;
513 explicit wrapper(response_type* o =
nullptr)
517 template <
class String>
518 void operator()(resp3::basic_node<String>
const& nd, system::error_code& ec)
520 BOOST_ASSERT_MSG(!!result_,
"Unexpected null pointer");
522 if (result_->has_error())
525 if (set_if_resp3_error(nd))
531 if (!result_->value().has_value()) {
532 result_->value() = T{};
533 impl_.on_value_available(result_->value().value());
536 impl_(result_->value().value(), nd, ec);
system::result< Value, error > result
Stores response to individual Redis commands.
basic_node< std::string > node
A node in the response tree that owns its data.
@ expects_resp3_set
Expects a set aggregate but got something else.
@ incompatible_size
Aggregate container has incompatible size.
@ not_a_number
Can't parse the string as a number.
@ nested_aggregate_not_supported
Nested response not supported.
@ expects_resp3_map
Expects a map but got other aggregate.
@ not_a_double
Not a double.
@ expects_resp3_aggregate
Expects aggregate.
@ expects_resp3_simple_type
Expects a simple RESP3 type but got an aggregate.