7#ifndef BOOST_REDIS_ADAPTER_ADAPTERS_HPP
8#define BOOST_REDIS_ADAPTER_ADAPTERS_HPP
10#include <boost/redis/error.hpp>
11#include <boost/redis/resp3/type.hpp>
12#include <boost/redis/resp3/serialization.hpp>
13#include <boost/redis/resp3/node.hpp>
14#include <boost/redis/adapter/result.hpp>
15#include <boost/assert.hpp>
19#include <unordered_set>
20#include <forward_list>
21#include <system_error>
23#include <unordered_map>
37namespace boost::redis::adapter::detail
40template <
class>
struct is_integral : std::false_type {};
42template <>
struct is_integral<long long int > : std::true_type {};
43template <>
struct is_integral<unsigned long long int> : std::true_type {};
44template <>
struct is_integral<int > : std::true_type {};
46template<class T, bool = is_integral<T>::value>
50struct converter<T, true> {
51 template <
class String>
55 resp3::basic_node<String>
const& node,
56 system::error_code& ec)
59 std::from_chars(
node.value.data(),
node.value.data() +
node.value.size(), i);
60 if (res.ec != std::errc())
66struct converter<bool, false> {
67 template <
class String>
71 resp3::basic_node<String>
const& node,
72 system::error_code& ec)
74 t = *
node.value.data() ==
't';
79struct converter<double, false> {
80 template <
class String>
84 resp3::basic_node<String>
const& node,
85 system::error_code& ec)
91 std::string
const tmp{
node.value.data(),
node.value.data() +
node.value.size()};
93 d = std::strtod(tmp.data(), &end);
94 if (d == HUGE_VAL || d == 0)
97 auto const res = std::from_chars(
node.value.data(),
node.value.data() +
node.value.size(), d);
98 if (res.ec != std::errc())
104template <
class CharT,
class Traits,
class Allocator>
105struct converter<std::basic_string<CharT, Traits, Allocator>, false> {
106 template <
class String>
109 std::basic_string<CharT, Traits, Allocator>& s,
110 resp3::basic_node<String>
const& node,
113 s.append(
node.value.data(),
node.value.size());
118struct from_bulk_impl {
119 template <
class String>
123 resp3::basic_node<String>
const& node,
124 system::error_code& ec)
126 converter<T>::apply(t, node, ec);
131struct from_bulk_impl<std::optional<T>> {
132 template <
class String>
135 std::optional<T>& op,
136 resp3::basic_node<String>
const& node,
137 system::error_code& ec)
141 converter<T>::apply(op.value(), node, ec);
146template <
class T,
class String>
148boost_redis_from_bulk(
150 resp3::basic_node<String>
const& node,
151 system::error_code& ec)
153 from_bulk_impl<T>::apply(t, node, ec);
158template <
class Result>
159class general_aggregate {
164 explicit general_aggregate(Result* c =
nullptr): result_(c) {}
165 template <
class String>
166 void operator()(resp3::basic_node<String>
const& nd, system::error_code&)
168 BOOST_ASSERT_MSG(!!result_,
"Unexpected null pointer");
169 switch (nd.data_type) {
172 *result_ =
error{nd.data_type, std::string{std::cbegin(nd.value), std::cend(nd.value)}};
175 result_->value().push_back({nd.data_type, nd.aggregate_size, nd.depth, std::string{std::cbegin(nd.value), std::cend(nd.value)}});
181class general_simple {
186 explicit general_simple(Node* t =
nullptr) : result_(t) {}
188 template <
class String>
189 void operator()(resp3::basic_node<String>
const& nd, system::error_code&)
191 BOOST_ASSERT_MSG(!!result_,
"Unexpected null pointer");
192 switch (nd.data_type) {
195 *result_ =
error{nd.data_type, std::string{std::cbegin(nd.value), std::cend(nd.value)}};
198 result_->value().data_type = nd.data_type;
199 result_->value().aggregate_size = nd.aggregate_size;
200 result_->value().depth = nd.depth;
201 result_->value().value.assign(nd.value.data(), nd.value.size());
206template <
class Result>
209 void on_value_available(Result&) {}
211 template <
class String>
212 void operator()(Result&
result, resp3::basic_node<String>
const& node, system::error_code& ec)
214 if (is_aggregate(
node.data_type)) {
219 boost_redis_from_bulk(
result, node, ec);
223template <
class Result>
226 typename Result::iterator hint_;
229 void on_value_available(Result&
result)
230 { hint_ = std::end(
result); }
232 template <
class String>
233 void operator()(Result&
result, resp3::basic_node<String>
const& nd, system::error_code& ec)
235 if (is_aggregate(nd.data_type)) {
241 BOOST_ASSERT(nd.aggregate_size == 1);
248 typename Result::key_type obj;
249 boost_redis_from_bulk(obj, nd, ec);
250 hint_ =
result.insert(hint_, std::move(obj));
254template <
class Result>
257 typename Result::iterator current_;
261 void on_value_available(Result&
result)
262 { current_ = std::end(
result); }
264 template <
class String>
265 void operator()(Result&
result, resp3::basic_node<String>
const& nd, system::error_code& ec)
267 if (is_aggregate(nd.data_type)) {
268 if (element_multiplicity(nd.data_type) != 2)
273 BOOST_ASSERT(nd.aggregate_size == 1);
281 typename Result::key_type obj;
282 boost_redis_from_bulk(obj, nd, ec);
283 current_ =
result.insert(current_, {std::move(obj), {}});
285 typename Result::mapped_type obj;
286 boost_redis_from_bulk(obj, nd, ec);
287 current_->second = std::move(obj);
294template <
class Result>
297 void on_value_available(Result& ) { }
299 template <
class String>
300 void operator()(Result&
result, resp3::basic_node<String>
const& nd, system::error_code& ec)
302 if (is_aggregate(nd.data_type)) {
303 auto const m = element_multiplicity(nd.data_type);
307 boost_redis_from_bulk(
result.back(), nd, ec);
312template <
class Result>
318 void on_value_available(Result& ) { }
320 template <
class String>
321 void operator()(Result&
result, resp3::basic_node<String>
const& nd, system::error_code& ec)
323 if (is_aggregate(nd.data_type)) {
329 if (
result.size() != nd.aggregate_size * element_multiplicity(nd.data_type)) {
339 BOOST_ASSERT(nd.aggregate_size == 1);
340 boost_redis_from_bulk(
result.at(i_), nd, ec);
347template <
class Result>
350 void on_value_available(Result& ) { }
352 template <
class String>
353 void operator()(Result&
result, resp3::basic_node<String>
const& nd, system::error_code& ec)
355 if (!is_aggregate(nd.data_type)) {
356 BOOST_ASSERT(nd.aggregate_size == 1);
363 boost_redis_from_bulk(
result.back(), nd, ec);
371struct impl_map {
using type = simple_impl<T>; };
373template <
class Key,
class Compare,
class Allocator>
374struct impl_map<std::
set<Key, Compare, Allocator>> {
using type = set_impl<std::set<Key, Compare, Allocator>>; };
376template <
class Key,
class Compare,
class Allocator>
377struct impl_map<std::multiset<Key, Compare, Allocator>> {
using type = set_impl<std::multiset<Key, Compare, Allocator>>; };
379template <
class Key,
class Hash,
class KeyEqual,
class Allocator>
380struct impl_map<std::unordered_set<Key, Hash, KeyEqual, Allocator>> {
using type = set_impl<std::unordered_set<Key, Hash, KeyEqual, Allocator>>; };
382template <
class Key,
class Hash,
class KeyEqual,
class Allocator>
383struct impl_map<std::unordered_multiset<Key, Hash, KeyEqual, Allocator>> {
using type = set_impl<std::unordered_multiset<Key, Hash, KeyEqual, Allocator>>; };
385template <
class Key,
class T,
class Compare,
class Allocator>
386struct impl_map<std::
map<Key, T, Compare, Allocator>> {
using type = map_impl<std::map<Key, T, Compare, Allocator>>; };
388template <
class Key,
class T,
class Compare,
class Allocator>
389struct impl_map<std::multimap<Key, T, Compare, Allocator>> {
using type = map_impl<std::multimap<Key, T, Compare, Allocator>>; };
391template <
class Key,
class Hash,
class KeyEqual,
class Allocator>
392struct impl_map<std::unordered_map<Key, Hash, KeyEqual, Allocator>> {
using type = map_impl<std::unordered_map<Key, Hash, KeyEqual, Allocator>>; };
394template <
class Key,
class Hash,
class KeyEqual,
class Allocator>
395struct impl_map<std::unordered_multimap<Key, Hash, KeyEqual, Allocator>> {
using type = map_impl<std::unordered_multimap<Key, Hash, KeyEqual, Allocator>>; };
397template <
class T,
class Allocator>
398struct impl_map<std::vector<T, Allocator>> {
using type = vector_impl<std::vector<T, Allocator>>; };
400template <
class T, std::
size_t N>
401struct impl_map<std::
array<T, N>> {
using type = array_impl<std::array<T, N>>; };
403template <
class T,
class Allocator>
404struct impl_map<std::list<T, Allocator>> {
using type = list_impl<std::list<T, Allocator>>; };
406template <
class T,
class Allocator>
407struct impl_map<std::deque<T, Allocator>> {
using type = list_impl<std::deque<T, Allocator>>; };
419 response_type* result_;
420 typename impl_map<T>::type impl_;
421 bool called_once_ =
false;
423 template <
class String>
424 bool set_if_resp3_error(resp3::basic_node<String>
const& nd)
noexcept
426 switch (nd.data_type) {
430 *result_ =
error{nd.data_type, {std::cbegin(nd.value), std::cend(nd.value)}};
438 explicit wrapper(response_type* t =
nullptr) : result_(t)
441 result_->value() = T{};
442 impl_.on_value_available(result_->value());
446 template <
class String>
447 void operator()(resp3::basic_node<String>
const& nd, system::error_code& ec)
449 BOOST_ASSERT_MSG(!!result_,
"Unexpected null pointer");
451 if (result_->has_error())
454 if (!std::exchange(called_once_,
true) && set_if_resp3_error(nd))
457 BOOST_ASSERT(result_);
458 impl_(result_->value(), nd, ec);
463class wrapper<
result<std::optional<T>>> {
468 response_type* result_;
469 typename impl_map<T>::type impl_{};
470 bool called_once_ =
false;
472 template <
class String>
473 bool set_if_resp3_error(resp3::basic_node<String>
const& nd)
noexcept
475 switch (nd.data_type) {
478 *result_ =
error{nd.data_type, {std::cbegin(nd.value), std::cend(nd.value)}};
486 explicit wrapper(response_type* o =
nullptr) : result_(o) {}
488 template <
class String>
491 resp3::basic_node<String>
const& nd,
492 system::error_code& ec)
494 BOOST_ASSERT_MSG(!!result_,
"Unexpected null pointer");
496 if (result_->has_error())
499 if (set_if_resp3_error(nd))
505 if (!result_->value().has_value()) {
506 result_->value() = T{};
507 impl_.on_value_available(result_->value().value());
510 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.