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>
38namespace boost::redis::adapter::detail
44auto boost_redis_from_bulk(T& i, std::string_view sv, system::error_code& ec) ->
typename std::enable_if<std::is_integral<T>::value,
void>::type
46 auto const res = std::from_chars(sv.data(), sv.data() + std::size(sv), i);
47 if (res.ec != std::errc())
52void boost_redis_from_bulk(
bool& t, std::string_view sv, system::error_code&)
54 t = *sv.data() ==
't';
58void boost_redis_from_bulk(
double& d, std::string_view sv, system::error_code& ec)
64 std::string
const tmp{sv.data(), sv.data() + std::size(sv)};
66 d = std::strtod(tmp.data(), &end);
67 if (d == HUGE_VAL || d == 0)
70 auto const res = std::from_chars(sv.data(), sv.data() + std::size(sv), d);
71 if (res.ec != std::errc())
76template <
class CharT,
class Traits,
class Allocator>
79 std::basic_string<CharT, Traits, Allocator>& s,
83 s.append(sv.data(), sv.size());
88template <
class Result>
89class general_aggregate {
94 explicit general_aggregate(Result* c =
nullptr): result_(c) {}
95 template <
class String>
96 void operator()(resp3::basic_node<String>
const& nd, system::error_code&)
98 BOOST_ASSERT_MSG(!!result_,
"Unexpected null pointer");
99 switch (nd.data_type) {
102 *result_ =
error{nd.data_type, std::string{std::cbegin(nd.value), std::cend(nd.value)}};
105 result_->value().push_back({nd.data_type, nd.aggregate_size, nd.depth, std::string{std::cbegin(nd.value), std::cend(nd.value)}});
111class general_simple {
116 explicit general_simple(Node* t =
nullptr) : result_(t) {}
118 template <
class String>
119 void operator()(resp3::basic_node<String>
const& nd, system::error_code&)
121 BOOST_ASSERT_MSG(!!result_,
"Unexpected null pointer");
122 switch (nd.data_type) {
125 *result_ =
error{nd.data_type, std::string{std::cbegin(nd.value), std::cend(nd.value)}};
128 result_->value().data_type = nd.data_type;
129 result_->value().aggregate_size = nd.aggregate_size;
130 result_->value().depth = nd.depth;
131 result_->value().value.assign(nd.value.data(), nd.value.size());
136template <
class Result>
139 void on_value_available(Result&) {}
141 template <
class String>
142 void operator()(Result&
result, resp3::basic_node<String>
const& n, system::error_code& ec)
144 if (is_aggregate(n.data_type)) {
149 boost_redis_from_bulk(
result, n.value, ec);
153template <
class Result>
156 typename Result::iterator hint_;
159 void on_value_available(Result&
result)
160 { hint_ = std::end(
result); }
162 template <
class String>
163 void operator()(Result&
result, resp3::basic_node<String>
const& nd, system::error_code& ec)
165 if (is_aggregate(nd.data_type)) {
171 BOOST_ASSERT(nd.aggregate_size == 1);
178 typename Result::key_type obj;
179 boost_redis_from_bulk(obj, nd.value, ec);
180 hint_ =
result.insert(hint_, std::move(obj));
184template <
class Result>
187 typename Result::iterator current_;
191 void on_value_available(Result&
result)
192 { current_ = std::end(
result); }
194 template <
class String>
195 void operator()(Result&
result, resp3::basic_node<String>
const& nd, system::error_code& ec)
197 if (is_aggregate(nd.data_type)) {
198 if (element_multiplicity(nd.data_type) != 2)
203 BOOST_ASSERT(nd.aggregate_size == 1);
211 typename Result::key_type obj;
212 boost_redis_from_bulk(obj, nd.value, ec);
213 current_ =
result.insert(current_, {std::move(obj), {}});
215 typename Result::mapped_type obj;
216 boost_redis_from_bulk(obj, nd.value, ec);
217 current_->second = std::move(obj);
224template <
class Result>
227 void on_value_available(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)) {
233 auto const m = element_multiplicity(nd.data_type);
237 boost_redis_from_bulk(
result.back(), nd.value, ec);
242template <
class Result>
248 void on_value_available(Result& ) { }
250 template <
class String>
251 void operator()(Result&
result, resp3::basic_node<String>
const& nd, system::error_code& ec)
253 if (is_aggregate(nd.data_type)) {
259 if (
result.size() != nd.aggregate_size * element_multiplicity(nd.data_type)) {
269 BOOST_ASSERT(nd.aggregate_size == 1);
270 boost_redis_from_bulk(
result.at(i_), nd.value, ec);
277template <
class Result>
280 void on_value_available(Result& ) { }
282 template <
class String>
283 void operator()(Result&
result, resp3::basic_node<String>
const& nd, system::error_code& ec)
285 if (!is_aggregate(nd.data_type)) {
286 BOOST_ASSERT(nd.aggregate_size == 1);
293 boost_redis_from_bulk(
result.back(), nd.value, ec);
301struct impl_map {
using type = simple_impl<T>; };
303template <
class Key,
class Compare,
class Allocator>
304struct impl_map<std::
set<Key, Compare, Allocator>> {
using type = set_impl<std::set<Key, Compare, Allocator>>; };
306template <
class Key,
class Compare,
class Allocator>
307struct impl_map<std::multiset<Key, Compare, Allocator>> {
using type = set_impl<std::multiset<Key, Compare, Allocator>>; };
309template <
class Key,
class Hash,
class KeyEqual,
class Allocator>
310struct impl_map<std::unordered_set<Key, Hash, KeyEqual, Allocator>> {
using type = set_impl<std::unordered_set<Key, Hash, KeyEqual, Allocator>>; };
312template <
class Key,
class Hash,
class KeyEqual,
class Allocator>
313struct impl_map<std::unordered_multiset<Key, Hash, KeyEqual, Allocator>> {
using type = set_impl<std::unordered_multiset<Key, Hash, KeyEqual, Allocator>>; };
315template <
class Key,
class T,
class Compare,
class Allocator>
316struct impl_map<std::
map<Key, T, Compare, Allocator>> {
using type = map_impl<std::map<Key, T, Compare, Allocator>>; };
318template <
class Key,
class T,
class Compare,
class Allocator>
319struct impl_map<std::multimap<Key, T, Compare, Allocator>> {
using type = map_impl<std::multimap<Key, T, Compare, Allocator>>; };
321template <
class Key,
class Hash,
class KeyEqual,
class Allocator>
322struct impl_map<std::unordered_map<Key, Hash, KeyEqual, Allocator>> {
using type = map_impl<std::unordered_map<Key, Hash, KeyEqual, Allocator>>; };
324template <
class Key,
class Hash,
class KeyEqual,
class Allocator>
325struct impl_map<std::unordered_multimap<Key, Hash, KeyEqual, Allocator>> {
using type = map_impl<std::unordered_multimap<Key, Hash, KeyEqual, Allocator>>; };
327template <
class T,
class Allocator>
328struct impl_map<std::vector<T, Allocator>> {
using type = vector_impl<std::vector<T, Allocator>>; };
330template <
class T, std::
size_t N>
331struct impl_map<std::
array<T, N>> {
using type = array_impl<std::array<T, N>>; };
333template <
class T,
class Allocator>
334struct impl_map<std::list<T, Allocator>> {
using type = list_impl<std::list<T, Allocator>>; };
336template <
class T,
class Allocator>
337struct impl_map<std::deque<T, Allocator>> {
using type = list_impl<std::deque<T, Allocator>>; };
344template <
class Result>
345class wrapper<
result<Result>> {
347 using response_type = result<Result>;
349 response_type* result_;
350 typename impl_map<Result>::type impl_;
352 template <
class String>
353 bool set_if_resp3_error(resp3::basic_node<String>
const& nd)
noexcept
355 switch (nd.data_type) {
359 *result_ =
error{nd.data_type, {std::cbegin(nd.value), std::cend(nd.value)}};
367 explicit wrapper(response_type* t =
nullptr) : result_(t)
370 result_->value() = Result{};
371 impl_.on_value_available(result_->value());
375 template <
class String>
376 void operator()(resp3::basic_node<String>
const& nd, system::error_code& ec)
378 BOOST_ASSERT_MSG(!!result_,
"Unexpected null pointer");
380 if (result_->has_error())
383 if (set_if_resp3_error(nd))
386 BOOST_ASSERT(result_);
387 impl_(result_->value(), nd, ec);
392class wrapper<
result<std::optional<T>>> {
394 using response_type = result<std::optional<T>>;
397 response_type* result_;
398 typename impl_map<T>::type impl_{};
400 template <
class String>
401 bool set_if_resp3_error(resp3::basic_node<String>
const& nd)
noexcept
403 switch (nd.data_type) {
406 *result_ =
error{nd.data_type, {std::cbegin(nd.value), std::cend(nd.value)}};
414 explicit wrapper(response_type* o =
nullptr) : result_(o) {}
416 template <
class String>
419 resp3::basic_node<String>
const& nd,
420 system::error_code& ec)
422 BOOST_ASSERT_MSG(!!result_,
"Unexpected null pointer");
424 if (result_->has_error())
427 if (set_if_resp3_error(nd))
433 if (!result_->value().has_value()) {
434 result_->value() = T{};
435 impl_.on_value_available(result_->value().value());
438 impl_(result_->value().value(), nd, ec);
system::result< Value, error > result
Stores response to individual Redis commands.
@ 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.