mirror of
https://github.com/boostorg/redis.git
synced 2026-01-19 04:42:09 +00:00
Uses system::result to implement per request error handling.
This commit is contained in:
@@ -275,6 +275,15 @@ if (MSVC)
|
||||
target_compile_definitions(test_conn_exec_cancel PRIVATE _WIN32_WINNT=0x0601)
|
||||
endif()
|
||||
|
||||
add_executable(test_conn_exec_error tests/conn_exec_error.cpp)
|
||||
target_compile_features(test_conn_exec_error PUBLIC cxx_std_17)
|
||||
target_link_libraries(test_conn_exec_error common)
|
||||
add_test(test_conn_exec_error test_conn_exec_error)
|
||||
if (MSVC)
|
||||
target_compile_options(test_conn_exec_error PRIVATE /bigobj)
|
||||
target_compile_definitions(test_conn_exec_error PRIVATE _WIN32_WINNT=0x0601)
|
||||
endif()
|
||||
|
||||
add_executable(test_conn_echo_stress tests/conn_echo_stress.cpp)
|
||||
target_compile_features(test_conn_echo_stress PUBLIC cxx_std_20)
|
||||
target_link_libraries(test_conn_echo_stress common)
|
||||
|
||||
@@ -870,6 +870,10 @@ Acknowledgement to people that helped shape Boost.Redis
|
||||
* Adds new typedef `boost::redis::generic_response` that should be used instead of `std::vector<resp3::node<std::string>>`.
|
||||
* Renames `redis::ignore` to `redis::ignore_t`.
|
||||
* Changes the signature from `async_exec` to receive a `redis::response` instead of an adapter.
|
||||
* Adds `boost::redis::adapter::result` to store responses to commands
|
||||
including possible resp3 errors without losing the error diagnostic
|
||||
part. Basicaly instead of accessing values as `std::get<N>(resp)`
|
||||
users have to type `std::get<N>(resp).value()`
|
||||
|
||||
### v1.4.0-1
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ namespace redis = boost::redis;
|
||||
using redis::operation;
|
||||
using redis::request;
|
||||
using redis::response;
|
||||
using redis::ignore_t;
|
||||
|
||||
void log(boost::system::error_code const& ec, char const* prefix)
|
||||
{
|
||||
@@ -38,7 +39,7 @@ auto main(int argc, char * argv[]) -> int
|
||||
req.push("QUIT");
|
||||
|
||||
// The response.
|
||||
response<redis::ignore_t, std::string, redis::ignore_t> resp;
|
||||
response<ignore_t, std::string, ignore_t> resp;
|
||||
|
||||
net::io_context ioc;
|
||||
|
||||
@@ -64,7 +65,7 @@ auto main(int argc, char * argv[]) -> int
|
||||
return log(ec, "on_exec: ");
|
||||
}
|
||||
|
||||
std::cout << "PING: " << std::get<1>(resp) << std::endl;
|
||||
std::cout << "PING: " << std::get<1>(resp).value() << std::endl;
|
||||
};
|
||||
|
||||
// Connect callback.
|
||||
|
||||
@@ -15,10 +15,10 @@
|
||||
#include <boost/redis/src.hpp>
|
||||
|
||||
namespace net = boost::asio;
|
||||
namespace redis = boost::redis;
|
||||
using connection = redis::connection;
|
||||
using connection = boost::redis::connection;
|
||||
using boost::redis::request;
|
||||
using boost::redis::response;
|
||||
using boost::redis::ignore_t;
|
||||
|
||||
template <class Response>
|
||||
auto exec(std::shared_ptr<connection> conn, request const& req, Response& resp)
|
||||
@@ -66,12 +66,12 @@ auto main(int argc, char * argv[]) -> int
|
||||
req.push("PING");
|
||||
req.push("QUIT");
|
||||
|
||||
response<boost::redis::ignore_t, std::string, boost::redis::ignore_t> resp;
|
||||
response<ignore_t, std::string, ignore_t> resp;
|
||||
|
||||
// Executes commands synchronously.
|
||||
exec(conn, req, resp);
|
||||
|
||||
std::cout << "Response: " << std::get<1>(resp) << std::endl;
|
||||
std::cout << "Response: " << std::get<1>(resp).value() << std::endl;
|
||||
|
||||
t.join();
|
||||
} catch (std::exception const& e) {
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
#include <boost/redis/src.hpp>
|
||||
|
||||
namespace net = boost::asio;
|
||||
namespace redis = boost::redis;
|
||||
namespace resp3 = redis::resp3;
|
||||
using redis::adapter::adapt2;
|
||||
namespace resp3 = boost::redis::resp3;
|
||||
using boost::redis::adapter::adapt2;
|
||||
using boost::redis::request;
|
||||
using boost::redis::adapter::result;
|
||||
|
||||
auto main(int argc, char * argv[]) -> int
|
||||
{
|
||||
@@ -41,8 +41,8 @@ auto main(int argc, char * argv[]) -> int
|
||||
req.push("QUIT");
|
||||
resp3::write(socket, req);
|
||||
|
||||
// Responses
|
||||
std::string buffer, resp;
|
||||
std::string buffer;
|
||||
result<std::string> resp;
|
||||
|
||||
// Reads the responses to all commands in the request.
|
||||
auto dbuffer = net::dynamic_buffer(buffer);
|
||||
@@ -50,7 +50,7 @@ auto main(int argc, char * argv[]) -> int
|
||||
resp3::read(socket, dbuffer, adapt2(resp));
|
||||
resp3::read(socket, dbuffer);
|
||||
|
||||
std::cout << "Ping: " << resp << std::endl;
|
||||
std::cout << "Ping: " << resp.value() << std::endl;
|
||||
|
||||
} catch (std::exception const& e) {
|
||||
std::cerr << e.what() << std::endl;
|
||||
|
||||
@@ -29,8 +29,8 @@ auto receiver(std::shared_ptr<connection> conn) -> net::awaitable<void>
|
||||
{
|
||||
for (generic_response resp;;) {
|
||||
co_await conn->async_receive(resp);
|
||||
std::cout << resp.at(1).value << " " << resp.at(2).value << " " << resp.at(3).value << std::endl;
|
||||
resp.clear();
|
||||
std::cout << resp.value().at(1).value << " " << resp.value().at(2).value << " " << resp.value().at(3).value << std::endl;
|
||||
resp.value().clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ namespace redis = boost::redis;
|
||||
using namespace net::experimental::awaitable_operators;
|
||||
using boost::redis::request;
|
||||
using boost::redis::response;
|
||||
using boost::redis::ignore_t;
|
||||
|
||||
void print(std::map<std::string, std::string> const& cont)
|
||||
{
|
||||
@@ -62,12 +63,12 @@ auto hgetall(std::shared_ptr<connection> conn) -> net::awaitable<void>
|
||||
req.push("HGETALL", "hset-key");
|
||||
|
||||
// Responses as tuple elements.
|
||||
response<redis::ignore_t, std::map<std::string, std::string>> resp;
|
||||
response<ignore_t, std::map<std::string, std::string>> resp;
|
||||
|
||||
// Executes the request and reads the response.
|
||||
co_await conn->async_exec(req, resp);
|
||||
|
||||
print(std::get<1>(resp));
|
||||
print(std::get<1>(resp).value());
|
||||
}
|
||||
|
||||
// Retrieves in a transaction.
|
||||
@@ -81,17 +82,17 @@ auto transaction(std::shared_ptr<connection> conn) -> net::awaitable<void>
|
||||
req.push("EXEC");
|
||||
|
||||
response<
|
||||
redis::ignore_t, // hello
|
||||
redis::ignore_t, // multi
|
||||
redis::ignore_t, // lrange
|
||||
redis::ignore_t, // hgetall
|
||||
ignore_t, // hello
|
||||
ignore_t, // multi
|
||||
ignore_t, // lrange
|
||||
ignore_t, // hgetall
|
||||
response<std::optional<std::vector<int>>, std::optional<std::map<std::string, std::string>>> // exec
|
||||
> resp;
|
||||
|
||||
co_await conn->async_exec(req, resp);
|
||||
|
||||
print(std::get<0>(std::get<4>(resp)).value());
|
||||
print(std::get<1>(std::get<4>(resp)).value());
|
||||
print(std::get<0>(std::get<4>(resp).value()).value().value());
|
||||
print(std::get<1>(std::get<4>(resp).value()).value().value());
|
||||
}
|
||||
|
||||
auto quit(std::shared_ptr<connection> conn) -> net::awaitable<void>
|
||||
|
||||
@@ -27,8 +27,8 @@ auto echo_server_session(tcp_socket socket, std::shared_ptr<connection> conn) ->
|
||||
auto n = co_await net::async_read_until(socket, net::dynamic_buffer(buffer, 1024), "\n");
|
||||
req.push("PING", buffer);
|
||||
co_await conn->async_exec(req, resp);
|
||||
co_await net::async_write(socket, net::buffer(std::get<0>(resp)));
|
||||
std::get<0>(resp).clear();
|
||||
co_await net::async_write(socket, net::buffer(std::get<0>(resp).value()));
|
||||
std::get<0>(resp).value().clear();
|
||||
req.clear();
|
||||
buffer.erase(0, n);
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ auto ping(std::shared_ptr<connection> conn) -> net::awaitable<void>
|
||||
response<std::string> resp;
|
||||
co_await conn->async_exec(req, resp);
|
||||
|
||||
std::cout << "PING: " << std::get<0>(resp) << std::endl;
|
||||
std::cout << "PING: " << std::get<0>(resp).value() << std::endl;
|
||||
}
|
||||
|
||||
auto quit(std::shared_ptr<connection> conn) -> net::awaitable<void>
|
||||
|
||||
@@ -14,22 +14,27 @@ namespace net = boost::asio;
|
||||
using namespace net::experimental::awaitable_operators;
|
||||
using boost::redis::request;
|
||||
using boost::redis::response;
|
||||
using boost::redis::ignore_t;
|
||||
|
||||
// Called from the main function (see main.cpp)
|
||||
auto co_main(std::string host, std::string port) -> net::awaitable<void>
|
||||
{
|
||||
request req;
|
||||
req.push("HELLO", 3);
|
||||
req.push("PING", "Hello world");
|
||||
req.push("QUIT");
|
||||
try {
|
||||
request req;
|
||||
req.push("HELLO", 3);
|
||||
req.push("PING", "Hello world");
|
||||
req.push("QUIT");
|
||||
|
||||
response<boost::redis::ignore_t, std::string, boost::redis::ignore_t> resp;
|
||||
response<ignore_t, std::string, ignore_t> resp;
|
||||
|
||||
auto conn = std::make_shared<connection>(co_await net::this_coro::executor);
|
||||
co_await connect(conn, host, port);
|
||||
co_await (conn->async_run() || conn->async_exec(req, resp));
|
||||
auto conn = std::make_shared<connection>(co_await net::this_coro::executor);
|
||||
co_await connect(conn, host, port);
|
||||
co_await (conn->async_run() || conn->async_exec(req, resp));
|
||||
|
||||
std::cout << "PING: " << std::get<1>(resp) << std::endl;
|
||||
std::cout << "PING: " << std::get<1>(resp).value() << std::endl;
|
||||
} catch (std::exception const& e) {
|
||||
std::cout << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // defined(BOOST_ASIO_HAS_CO_AWAIT)
|
||||
|
||||
@@ -23,6 +23,7 @@ using resolver = net::use_awaitable_t<>::as_default_on_t<net::ip::tcp::resolver>
|
||||
using connection = net::use_awaitable_t<>::as_default_on_t<redis::ssl::connection>;
|
||||
using boost::redis::request;
|
||||
using boost::redis::response;
|
||||
using boost::redis::ignore_t;
|
||||
|
||||
auto verify_certificate(bool, net::ssl::verify_context&) -> bool
|
||||
{
|
||||
@@ -37,7 +38,7 @@ net::awaitable<void> co_main(std::string, std::string)
|
||||
req.push("PING");
|
||||
req.push("QUIT");
|
||||
|
||||
response<redis::ignore_t, std::string, redis::ignore_t> resp;
|
||||
response<ignore_t, std::string, ignore_t> resp;
|
||||
|
||||
// Resolve
|
||||
auto ex = co_await net::this_coro::executor;
|
||||
@@ -53,7 +54,7 @@ net::awaitable<void> co_main(std::string, std::string)
|
||||
co_await conn.next_layer().async_handshake(net::ssl::stream_base::client);
|
||||
co_await (conn.async_run() || conn.async_exec(req, resp));
|
||||
|
||||
std::cout << "Response: " << std::get<1>(resp) << std::endl;
|
||||
std::cout << "Response: " << std::get<1>(resp).value() << std::endl;
|
||||
}
|
||||
|
||||
#endif // defined(BOOST_ASIO_HAS_CO_AWAIT)
|
||||
|
||||
@@ -17,6 +17,7 @@ using tcp_socket = net::use_awaitable_t<>::as_default_on_t<net::ip::tcp::socket>
|
||||
using boost::redis::adapter::adapt2;
|
||||
using net::ip::tcp;
|
||||
using boost::redis::request;
|
||||
using boost::redis::adapter::result;
|
||||
|
||||
auto co_main(std::string host, std::string port) -> net::awaitable<void>
|
||||
{
|
||||
@@ -35,7 +36,8 @@ auto co_main(std::string host, std::string port) -> net::awaitable<void>
|
||||
co_await resp3::async_write(socket, req);
|
||||
|
||||
// Responses
|
||||
std::string buffer, resp;
|
||||
std::string buffer;
|
||||
result<std::string> resp;
|
||||
|
||||
// Reads the responses to all commands in the request.
|
||||
auto dbuffer = net::dynamic_buffer(buffer);
|
||||
@@ -43,7 +45,7 @@ auto co_main(std::string host, std::string port) -> net::awaitable<void>
|
||||
co_await resp3::async_read(socket, dbuffer, adapt2(resp));
|
||||
co_await resp3::async_read(socket, dbuffer);
|
||||
|
||||
std::cout << "Ping: " << resp << std::endl;
|
||||
std::cout << "Ping: " << resp.value() << std::endl;
|
||||
}
|
||||
|
||||
#endif // defined(BOOST_ASIO_HAS_CO_AWAIT)
|
||||
|
||||
@@ -16,6 +16,7 @@ using namespace net::experimental::awaitable_operators;
|
||||
using endpoints = net::ip::tcp::resolver::results_type;
|
||||
using boost::redis::request;
|
||||
using boost::redis::response;
|
||||
using boost::redis::ignore_t;
|
||||
|
||||
auto redir(boost::system::error_code& ec)
|
||||
{ return net::redirect_error(net::use_awaitable, ec); }
|
||||
@@ -36,14 +37,14 @@ auto resolve_master_address(std::vector<address> const& endpoints) -> net::await
|
||||
|
||||
auto conn = std::make_shared<connection>(co_await net::this_coro::executor);
|
||||
|
||||
response<std::optional<std::array<std::string, 2>>, boost::redis::ignore_t> addr;
|
||||
response<std::optional<std::array<std::string, 2>>, ignore_t> addr;
|
||||
for (auto ep : endpoints) {
|
||||
boost::system::error_code ec;
|
||||
co_await connect(conn, ep.host, ep.port);
|
||||
co_await (conn->async_run() && conn->async_exec(req, addr, redir(ec)));
|
||||
conn->reset_stream();
|
||||
if (std::get<0>(addr))
|
||||
co_return address{std::get<0>(addr).value().at(0), std::get<0>(addr).value().at(1)};
|
||||
co_return address{std::get<0>(addr).value().value().at(0), std::get<0>(addr).value().value().at(1)};
|
||||
}
|
||||
|
||||
co_return address{};
|
||||
|
||||
@@ -28,6 +28,7 @@ using namespace net::experimental::awaitable_operators;
|
||||
using namespace boost::json;
|
||||
using boost::redis::request;
|
||||
using boost::redis::response;
|
||||
using boost::redis::ignore_t;
|
||||
|
||||
struct user {
|
||||
std::string name;
|
||||
@@ -98,14 +99,14 @@ net::awaitable<void> co_main(std::string host, std::string port)
|
||||
req.push("SMEMBERS", "sadd-key"); // Retrieves
|
||||
req.push("QUIT");
|
||||
|
||||
response<redis::ignore_t, int, std::set<user>, std::string> resp;
|
||||
response<ignore_t, int, std::set<user>, std::string> resp;
|
||||
|
||||
auto conn = std::make_shared<connection>(co_await net::this_coro::executor);
|
||||
|
||||
co_await connect(conn, host, port);
|
||||
co_await (conn->async_run() || conn->async_exec(req, resp));
|
||||
|
||||
for (auto const& e: std::get<2>(resp))
|
||||
for (auto const& e: std::get<2>(resp).value())
|
||||
std::cout << e << "\n";
|
||||
}
|
||||
|
||||
|
||||
@@ -38,8 +38,8 @@ auto receiver(std::shared_ptr<connection> conn) -> net::awaitable<void>
|
||||
{
|
||||
for (generic_response resp;;) {
|
||||
co_await conn->async_receive(resp);
|
||||
std::cout << resp.at(1).value << " " << resp.at(2).value << " " << resp.at(3).value << std::endl;
|
||||
resp.clear();
|
||||
std::cout << resp.value().at(1).value << " " << resp.value().at(2).value << " " << resp.value().at(3).value << std::endl;
|
||||
resp.value().clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,39 +9,14 @@
|
||||
|
||||
#include <boost/redis/adapter/detail/response_traits.hpp>
|
||||
|
||||
namespace boost::redis::adapter {
|
||||
namespace boost::redis::adapter
|
||||
{
|
||||
|
||||
template <class T>
|
||||
using adapter_t = typename detail::adapter_t<T>;
|
||||
|
||||
/** \brief Creates a dummy response adapter.
|
||||
\ingroup low-level-api
|
||||
|
||||
The adapter returned by this function ignores responses. It is
|
||||
useful to avoid wasting time with responses which are not needed.
|
||||
|
||||
Example:
|
||||
|
||||
@code
|
||||
// Pushes and writes some commands to the server.
|
||||
sr.push(command::hello, 3);
|
||||
sr.push(command::ping);
|
||||
sr.push(command::quit);
|
||||
net::write(socket, net::buffer(request));
|
||||
|
||||
// Ignores all responses except for the response to ping.
|
||||
std::string buffer;
|
||||
resp3::read(socket, dynamic_buffer(buffer), adapt2()); // hello
|
||||
resp3::read(socket, dynamic_buffer(buffer), adapt2(resp)); // ping
|
||||
resp3::read(socket, dynamic_buffer(buffer, adapt2())); // quit
|
||||
@endcode
|
||||
*/
|
||||
inline
|
||||
auto adapt2() noexcept
|
||||
{ return detail::response_traits<void>::adapt(); }
|
||||
|
||||
/** \brief Adapts user data to read operations.
|
||||
* \ingroup low-level-api
|
||||
/** @brief Adapts user data to read operations.
|
||||
* @ingroup low-level-api
|
||||
*
|
||||
* STL containers, \c resp3::response and built-in types are supported and
|
||||
* can be used in conjunction with \c std::optional<T>.
|
||||
@@ -72,7 +47,7 @@ auto adapt2() noexcept
|
||||
* @endcode
|
||||
*/
|
||||
template<class T>
|
||||
auto adapt2(T& t) noexcept
|
||||
auto adapt2(T& t = redis::ignore) noexcept
|
||||
{ return detail::response_traits<T>::adapt(t); }
|
||||
|
||||
} // boost::redis::adapter
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
#include <boost/redis/resp3/serialization.hpp>
|
||||
#include <boost/redis/resp3/detail/parser.hpp>
|
||||
#include <boost/redis/resp3/node.hpp>
|
||||
#include <boost/redis/adapter/result.hpp>
|
||||
#include <boost/redis/adapter/ignore.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
#include <set>
|
||||
@@ -28,7 +30,8 @@
|
||||
#include <string_view>
|
||||
#include <charconv>
|
||||
|
||||
namespace boost::redis::adapter::detail {
|
||||
namespace boost::redis::adapter::detail
|
||||
{
|
||||
|
||||
// Serialization.
|
||||
|
||||
@@ -37,7 +40,7 @@ auto boost_redis_from_bulk(T& i, std::string_view sv, system::error_code& ec) ->
|
||||
{
|
||||
auto const res = std::from_chars(sv.data(), sv.data() + std::size(sv), i);
|
||||
if (res.ec != std::errc())
|
||||
ec = error::not_a_number;
|
||||
ec = redis::error::not_a_number;
|
||||
}
|
||||
|
||||
inline
|
||||
@@ -51,7 +54,7 @@ void boost_redis_from_bulk(double& d, std::string_view sv, system::error_code& e
|
||||
{
|
||||
auto const res = std::from_chars(sv.data(), sv.data() + std::size(sv), d);
|
||||
if (res.ec != std::errc())
|
||||
ec = error::not_a_double;
|
||||
ec = redis::error::not_a_double;
|
||||
}
|
||||
|
||||
template <class CharT, class Traits, class Allocator>
|
||||
@@ -66,17 +69,6 @@ boost_redis_from_bulk(
|
||||
|
||||
//================================================
|
||||
|
||||
inline
|
||||
void set_on_resp3_error(resp3::type t, system::error_code& ec)
|
||||
{
|
||||
switch (t) {
|
||||
case resp3::type::simple_error: ec = error::resp3_simple_error; return;
|
||||
case resp3::type::blob_error: ec = error::resp3_blob_error; return;
|
||||
case resp3::type::null: ec = error::resp3_null; return;
|
||||
default: return;
|
||||
}
|
||||
}
|
||||
|
||||
template <class Result>
|
||||
class general_aggregate {
|
||||
private:
|
||||
@@ -84,9 +76,17 @@ private:
|
||||
|
||||
public:
|
||||
explicit general_aggregate(Result* c = nullptr): result_(c) {}
|
||||
void operator()(resp3::node<std::string_view> const& n, system::error_code&)
|
||||
void operator()(resp3::node<std::string_view> const& nd, system::error_code&)
|
||||
{
|
||||
result_->push_back({n.data_type, n.aggregate_size, n.depth, std::string{std::cbegin(n.value), std::cend(n.value)}});
|
||||
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)}});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -98,13 +98,20 @@ private:
|
||||
public:
|
||||
explicit general_simple(Node* t = nullptr) : result_(t) {}
|
||||
|
||||
void operator()(resp3::node<std::string_view> const& n, system::error_code& ec)
|
||||
void operator()(resp3::node<std::string_view> const& nd, system::error_code&)
|
||||
{
|
||||
result_->data_type = n.data_type;
|
||||
result_->aggregate_size = n.aggregate_size;
|
||||
result_->depth = n.depth;
|
||||
result_->value.assign(n.value.data(), n.value.size());
|
||||
set_on_resp3_error(n.data_type, ec);
|
||||
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());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -119,12 +126,8 @@ public:
|
||||
resp3::node<std::string_view> const& n,
|
||||
system::error_code& ec)
|
||||
{
|
||||
set_on_resp3_error(n.data_type, ec);
|
||||
if (ec)
|
||||
return;
|
||||
|
||||
if (is_aggregate(n.data_type)) {
|
||||
ec = error::expects_resp3_simple_type;
|
||||
ec = redis::error::expects_resp3_simple_type;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -147,20 +150,16 @@ public:
|
||||
resp3::node<std::string_view> const& nd,
|
||||
system::error_code& ec)
|
||||
{
|
||||
set_on_resp3_error(nd.data_type, ec);
|
||||
if (ec)
|
||||
return;
|
||||
|
||||
if (is_aggregate(nd.data_type)) {
|
||||
if (nd.data_type != resp3::type::set)
|
||||
ec = error::expects_resp3_set;
|
||||
ec = redis::error::expects_resp3_set;
|
||||
return;
|
||||
}
|
||||
|
||||
BOOST_ASSERT(nd.aggregate_size == 1);
|
||||
|
||||
if (nd.depth < 1) {
|
||||
ec = error::expects_resp3_set;
|
||||
ec = redis::error::expects_resp3_set;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -186,20 +185,16 @@ public:
|
||||
resp3::node<std::string_view> const& nd,
|
||||
system::error_code& ec)
|
||||
{
|
||||
set_on_resp3_error(nd.data_type, ec);
|
||||
if (ec)
|
||||
return;
|
||||
|
||||
if (is_aggregate(nd.data_type)) {
|
||||
if (element_multiplicity(nd.data_type) != 2)
|
||||
ec = error::expects_resp3_map;
|
||||
ec = redis::error::expects_resp3_map;
|
||||
return;
|
||||
}
|
||||
|
||||
BOOST_ASSERT(nd.aggregate_size == 1);
|
||||
|
||||
if (nd.depth < 1) {
|
||||
ec = error::expects_resp3_map;
|
||||
ec = redis::error::expects_resp3_map;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -228,10 +223,6 @@ public:
|
||||
resp3::node<std::string_view> const& nd,
|
||||
system::error_code& ec)
|
||||
{
|
||||
set_on_resp3_error(nd.data_type, ec);
|
||||
if (ec)
|
||||
return;
|
||||
|
||||
if (is_aggregate(nd.data_type)) {
|
||||
auto const m = element_multiplicity(nd.data_type);
|
||||
result.reserve(result.size() + m * nd.aggregate_size);
|
||||
@@ -256,23 +247,19 @@ public:
|
||||
resp3::node<std::string_view> const& nd,
|
||||
system::error_code& ec)
|
||||
{
|
||||
set_on_resp3_error(nd.data_type, ec);
|
||||
if (ec)
|
||||
return;
|
||||
|
||||
if (is_aggregate(nd.data_type)) {
|
||||
if (i_ != -1) {
|
||||
ec = error::nested_aggregate_not_supported;
|
||||
ec = redis::error::nested_aggregate_not_supported;
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.size() != nd.aggregate_size * element_multiplicity(nd.data_type)) {
|
||||
ec = error::incompatible_size;
|
||||
ec = redis::error::incompatible_size;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (i_ == -1) {
|
||||
ec = error::expects_resp3_aggregate;
|
||||
ec = redis::error::expects_resp3_aggregate;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -295,14 +282,10 @@ struct list_impl {
|
||||
resp3::node<std::string_view> const& nd,
|
||||
system::error_code& ec)
|
||||
{
|
||||
set_on_resp3_error(nd.data_type, ec);
|
||||
if (ec)
|
||||
return;
|
||||
|
||||
if (!is_aggregate(nd.data_type)) {
|
||||
BOOST_ASSERT(nd.aggregate_size == 1);
|
||||
if (nd.depth < 1) {
|
||||
ec = error::expects_resp3_aggregate;
|
||||
ec = redis::error::expects_resp3_aggregate;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -355,49 +338,103 @@ struct impl_map<std::deque<T, Allocator>> { using type = list_impl<std::deque<T,
|
||||
|
||||
//---------------------------------------------------
|
||||
|
||||
template <class>
|
||||
class wrapper;
|
||||
|
||||
template <class Result>
|
||||
class wrapper {
|
||||
class wrapper<result<Result>> {
|
||||
public:
|
||||
using response_type = result<Result>;
|
||||
private:
|
||||
Result* result_;
|
||||
response_type* result_;
|
||||
typename impl_map<Result>::type impl_;
|
||||
|
||||
bool set_if_resp3_error(resp3::node<std::string_view> 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(Result* t = nullptr) : result_(t)
|
||||
{ impl_.on_value_available(*result_); }
|
||||
explicit wrapper(response_type* t = nullptr) : result_(t)
|
||||
{
|
||||
if (result_) {
|
||||
result_->value() = Result{};
|
||||
impl_.on_value_available(result_->value());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
operator()(
|
||||
resp3::node<std::string_view> 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_, nd, ec);
|
||||
impl_(result_->value(), nd, ec);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class wrapper<std::optional<T>> {
|
||||
class wrapper<result<std::optional<T>>> {
|
||||
public:
|
||||
using response_type = result<std::optional<T>>;
|
||||
|
||||
private:
|
||||
std::optional<T>* result_;
|
||||
response_type* result_;
|
||||
typename impl_map<T>::type impl_{};
|
||||
|
||||
bool set_if_resp3_error(resp3::node<std::string_view> 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(std::optional<T>* o = nullptr) : result_(o) {}
|
||||
explicit wrapper(response_type* o = nullptr) : result_(o) {}
|
||||
|
||||
void
|
||||
operator()(
|
||||
resp3::node<std::string_view> 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_->has_value()) {
|
||||
*result_ = T{};
|
||||
impl_.on_value_available(result_->value());
|
||||
if (!result_->value().has_value()) {
|
||||
result_->value() = T{};
|
||||
impl_.on_value_available(result_->value().value());
|
||||
}
|
||||
|
||||
impl_(result_->value(), nd, ec);
|
||||
impl_(result_->value().value(), nd, ec);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -9,8 +9,9 @@
|
||||
|
||||
#include <boost/redis/error.hpp>
|
||||
#include <boost/redis/resp3/type.hpp>
|
||||
#include <boost/redis/resp3/read.hpp>
|
||||
#include <boost/redis/ignore.hpp>
|
||||
#include <boost/redis/adapter/detail/adapters.hpp>
|
||||
#include <boost/redis/adapter/result.hpp>
|
||||
#include <boost/mp11.hpp>
|
||||
|
||||
#include <vector>
|
||||
@@ -18,53 +19,51 @@
|
||||
#include <string_view>
|
||||
#include <variant>
|
||||
|
||||
namespace boost::redis::adapter::detail {
|
||||
|
||||
using ignore_t = std::decay_t<decltype(std::ignore)>;
|
||||
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 <class ResponseType>
|
||||
template <class Response>
|
||||
struct response_traits {
|
||||
using adapter_type = adapter::detail::wrapper<typename std::decay<ResponseType>::type>;
|
||||
static auto adapt(ResponseType& r) noexcept { return adapter_type{&r}; }
|
||||
using adapter_type = adapter::detail::wrapper<typename std::decay<Response>::type>;
|
||||
static auto adapt(Response& r) noexcept { return adapter_type{&r}; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct response_traits<result<ignore_t>> {
|
||||
using response_type = result<ignore_t>;
|
||||
using adapter_type = ignore;
|
||||
static auto adapt(response_type) noexcept { return adapter_type{}; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct response_traits<ignore_t> {
|
||||
using response_type = ignore_t;
|
||||
using adapter_type = resp3::detail::ignore_response;
|
||||
using adapter_type = ignore;
|
||||
static auto adapt(response_type) noexcept { return adapter_type{}; }
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct response_traits<resp3::node<T>> {
|
||||
using response_type = resp3::node<T>;
|
||||
struct response_traits<result<resp3::node<T>>> {
|
||||
using response_type = result<resp3::node<T>>;
|
||||
using adapter_type = adapter::detail::general_simple<response_type>;
|
||||
static auto adapt(response_type& v) noexcept { return adapter_type{&v}; }
|
||||
};
|
||||
|
||||
template <class String, class Allocator>
|
||||
struct response_traits<std::vector<resp3::node<String>, Allocator>> {
|
||||
using response_type = std::vector<resp3::node<String>, Allocator>;
|
||||
struct response_traits<result<std::vector<resp3::node<String>, Allocator>>> {
|
||||
using response_type = result<std::vector<resp3::node<String>, Allocator>>;
|
||||
using adapter_type = adapter::detail::general_aggregate<response_type>;
|
||||
static auto adapt(response_type& v) noexcept { return adapter_type{&v}; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct response_traits<void> {
|
||||
using response_type = void;
|
||||
using adapter_type = resp3::detail::ignore_response;
|
||||
static auto adapt() noexcept { return adapter_type{}; }
|
||||
};
|
||||
|
||||
template <class T>
|
||||
using adapter_t = typename response_traits<std::decay_t<T>>::adapter_type;
|
||||
|
||||
// Duplicated here to avoid circular include dependency.
|
||||
template<class T>
|
||||
auto internal_adapt(T& t) noexcept
|
||||
{ return response_traits<std::decay_t<T>>::adapt(t); }
|
||||
@@ -89,7 +88,10 @@ struct assigner<0> {
|
||||
};
|
||||
|
||||
template <class Tuple>
|
||||
class static_aggregate_adapter {
|
||||
class static_aggregate_adapter;
|
||||
|
||||
template <class Tuple>
|
||||
class static_aggregate_adapter<result<Tuple>> {
|
||||
private:
|
||||
using adapters_array_type =
|
||||
std::array<
|
||||
@@ -102,11 +104,15 @@ private:
|
||||
std::size_t i_ = 0;
|
||||
std::size_t aggregate_size_ = 0;
|
||||
adapters_array_type adapters_;
|
||||
result<Tuple>* res_ = nullptr;
|
||||
|
||||
public:
|
||||
explicit static_aggregate_adapter(Tuple* r = nullptr)
|
||||
explicit static_aggregate_adapter(result<Tuple>* r = nullptr)
|
||||
{
|
||||
detail::assigner<std::tuple_size<Tuple>::value - 1>::assign(adapters_, *r);
|
||||
if (r) {
|
||||
res_ = r;
|
||||
detail::assigner<std::tuple_size<Tuple>::value - 1>::assign(adapters_, r->value());
|
||||
}
|
||||
}
|
||||
|
||||
void count(resp3::node<std::string_view> const& nd)
|
||||
@@ -124,17 +130,14 @@ public:
|
||||
++i_;
|
||||
}
|
||||
|
||||
void
|
||||
operator()(
|
||||
resp3::node<std::string_view> const& nd,
|
||||
system::error_code& ec)
|
||||
void operator()(resp3::node<std::string_view> const& nd, system::error_code& ec)
|
||||
{
|
||||
using std::visit;
|
||||
|
||||
if (nd.depth == 0) {
|
||||
auto const real_aggr_size = nd.aggregate_size * element_multiplicity(nd.data_type);
|
||||
if (real_aggr_size != std::tuple_size<Tuple>::value)
|
||||
ec = error::incompatible_size;
|
||||
ec = redis::error::incompatible_size;
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -145,9 +148,9 @@ public:
|
||||
};
|
||||
|
||||
template <class... Ts>
|
||||
struct response_traits<std::tuple<Ts...>>
|
||||
struct response_traits<result<std::tuple<Ts...>>>
|
||||
{
|
||||
using response_type = std::tuple<Ts...>;
|
||||
using response_type = result<std::tuple<Ts...>>;
|
||||
using adapter_type = static_aggregate_adapter<response_type>;
|
||||
static auto adapt(response_type& r) noexcept { return adapter_type{&r}; }
|
||||
};
|
||||
|
||||
31
include/boost/redis/adapter/ignore.hpp
Normal file
31
include/boost/redis/adapter/ignore.hpp
Normal file
@@ -0,0 +1,31 @@
|
||||
/* Copyright (c) 2018-2022 Marcelo Zimbres Silva (mzimbres@gmail.com)
|
||||
*
|
||||
* Distributed under the Boost Software License, Version 1.0. (See
|
||||
* accompanying file LICENSE.txt)
|
||||
*/
|
||||
|
||||
#ifndef BOOST_REDIS_ADAPTER_IGNORE_HPP
|
||||
#define BOOST_REDIS_ADAPTER_IGNORE_HPP
|
||||
|
||||
#include <boost/redis/resp3/node.hpp>
|
||||
#include <boost/system/error_code.hpp>
|
||||
#include <string>
|
||||
|
||||
namespace boost::redis::adapter
|
||||
{
|
||||
|
||||
struct ignore {
|
||||
void operator()(resp3::node<std::string_view> const& nd, system::error_code& ec)
|
||||
{
|
||||
switch (nd.data_type) {
|
||||
case resp3::type::simple_error: ec = redis::error::resp3_simple_error; break;
|
||||
case resp3::type::blob_error: ec = redis::error::resp3_blob_error; break;
|
||||
case resp3::type::null: ec = redis::error::resp3_null; break;
|
||||
default:;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // boost::redis::adapter
|
||||
|
||||
#endif // BOOST_REDIS_ADAPTER_IGNORE_HPP
|
||||
80
include/boost/redis/adapter/result.hpp
Normal file
80
include/boost/redis/adapter/result.hpp
Normal file
@@ -0,0 +1,80 @@
|
||||
|
||||
/* Copyright (c) 2018-2022 Marcelo Zimbres Silva (mzimbres@gmail.com)
|
||||
*
|
||||
* Distributed under the Boost Software License, Version 1.0. (See
|
||||
* accompanying file LICENSE.txt)
|
||||
*/
|
||||
|
||||
#ifndef BOOST_REDIS_ADAPTER_RESULT_HPP
|
||||
#define BOOST_REDIS_ADAPTER_RESULT_HPP
|
||||
|
||||
#include <boost/redis/resp3/type.hpp>
|
||||
#include <boost/system/result.hpp>
|
||||
#include <string>
|
||||
|
||||
namespace boost::redis::adapter
|
||||
{
|
||||
|
||||
/** @brief Stores any resp3 error
|
||||
* @ingroup high-level-api
|
||||
*/
|
||||
struct error {
|
||||
/// RESP3 error data type.
|
||||
resp3::type data_type = resp3::type::invalid;
|
||||
|
||||
/// Diagnostic error message sent by Redis.
|
||||
std::string diagnostic;
|
||||
};
|
||||
|
||||
/** @brief Compares two error objects for equality
|
||||
* @relates error
|
||||
*
|
||||
* @param a Left hand side error object.
|
||||
* @param b Right hand side error object.
|
||||
*/
|
||||
inline bool operator==(error const& a, error const& b)
|
||||
{
|
||||
return a.data_type == b.data_type && a.diagnostic == b.diagnostic;
|
||||
}
|
||||
|
||||
/** @brief Compares two error objects for difference
|
||||
* @relates error
|
||||
*
|
||||
* @param a Left hand side error object.
|
||||
* @param b Right hand side error object.
|
||||
*/
|
||||
inline bool operator!=(error const& a, error const& b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
/** @brief Stores response to individual Redis commands
|
||||
* @ingroup high-level-api
|
||||
*/
|
||||
template <class Value>
|
||||
using result = system::result<Value, error>;
|
||||
|
||||
BOOST_NORETURN inline void
|
||||
throw_exception_from_error(error const & e, boost::source_location const &)
|
||||
{
|
||||
system::error_code ec;
|
||||
switch (e.data_type) {
|
||||
case resp3::type::simple_error:
|
||||
ec = redis::error::resp3_simple_error;
|
||||
break;
|
||||
case resp3::type::blob_error:
|
||||
ec = redis::error::resp3_blob_error;
|
||||
break;
|
||||
case resp3::type::null:
|
||||
ec = redis::error::resp3_null;
|
||||
break;
|
||||
default:
|
||||
BOOST_ASSERT_MSG(false, "Unexpected data type.");
|
||||
}
|
||||
|
||||
throw system::system_error(ec, e.diagnostic);
|
||||
}
|
||||
|
||||
} // boost::redis::adapter
|
||||
|
||||
#endif // BOOST_REDIS_ADAPTER_RESULT_HPP
|
||||
@@ -25,26 +25,33 @@ namespace boost::redis::detail
|
||||
class ignore_adapter {
|
||||
public:
|
||||
void
|
||||
operator()(
|
||||
std::size_t, resp3::node<std::string_view> const&, system::error_code&) { }
|
||||
operator()(std::size_t, resp3::node<std::string_view> const& nd, system::error_code& ec)
|
||||
{
|
||||
switch (nd.data_type) {
|
||||
case resp3::type::simple_error: ec = redis::error::resp3_simple_error; break;
|
||||
case resp3::type::blob_error: ec = redis::error::resp3_blob_error; break;
|
||||
case resp3::type::null: ec = redis::error::resp3_null; break;
|
||||
default:;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
auto get_supported_response_size() const noexcept
|
||||
{ return static_cast<std::size_t>(-1);}
|
||||
};
|
||||
|
||||
template <class Tuple>
|
||||
template <class Response>
|
||||
class static_adapter {
|
||||
private:
|
||||
static constexpr auto size = std::tuple_size<Tuple>::value;
|
||||
using adapter_tuple = mp11::mp_transform<adapter::adapter_t, Tuple>;
|
||||
static constexpr auto size = std::tuple_size<Response>::value;
|
||||
using adapter_tuple = mp11::mp_transform<adapter::adapter_t, Response>;
|
||||
using variant_type = mp11::mp_rename<adapter_tuple, std::variant>;
|
||||
using adapters_array_type = std::array<variant_type, size>;
|
||||
|
||||
adapters_array_type adapters_;
|
||||
|
||||
public:
|
||||
explicit static_adapter(Tuple& r)
|
||||
explicit static_adapter(Response& r)
|
||||
{
|
||||
adapter::detail::assigner<size - 1>::assign(adapters_, r);
|
||||
}
|
||||
@@ -97,16 +104,25 @@ struct response_traits;
|
||||
|
||||
template <>
|
||||
struct response_traits<ignore_t> {
|
||||
using response_type = void;
|
||||
using response_type = ignore_t;
|
||||
using adapter_type = detail::ignore_adapter;
|
||||
|
||||
static auto adapt(ignore_t&) noexcept
|
||||
static auto adapt(response_type&) noexcept
|
||||
{ return detail::ignore_adapter{}; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct response_traits<adapter::result<ignore_t>> {
|
||||
using response_type = adapter::result<ignore_t>;
|
||||
using adapter_type = detail::ignore_adapter;
|
||||
|
||||
static auto adapt(response_type&) noexcept
|
||||
{ return detail::ignore_adapter{}; }
|
||||
};
|
||||
|
||||
template <class String, class Allocator>
|
||||
struct response_traits<std::vector<resp3::node<String>, Allocator>> {
|
||||
using response_type = std::vector<resp3::node<String>, Allocator>;
|
||||
struct response_traits<adapter::result<std::vector<resp3::node<String>, Allocator>>> {
|
||||
using response_type = adapter::result<std::vector<resp3::node<String>, Allocator>>;
|
||||
using adapter_type = vector_adapter<response_type>;
|
||||
|
||||
static auto adapt(response_type& v) noexcept
|
||||
@@ -159,7 +175,7 @@ auto make_adapter_wrapper(Adapter adapter)
|
||||
template<class T>
|
||||
auto boost_redis_adapt(T& t) noexcept
|
||||
{
|
||||
return detail::response_traits<T>::adapt(t);
|
||||
return response_traits<T>::adapt(t);
|
||||
}
|
||||
|
||||
} // boost::redis::detail
|
||||
|
||||
49
include/boost/redis/ignore.hpp
Normal file
49
include/boost/redis/ignore.hpp
Normal file
@@ -0,0 +1,49 @@
|
||||
|
||||
/* Copyright (c) 2018-2022 Marcelo Zimbres Silva (mzimbres@gmail.com)
|
||||
*
|
||||
* Distributed under the Boost Software License, Version 1.0. (See
|
||||
* accompanying file LICENSE.txt)
|
||||
*/
|
||||
|
||||
#ifndef BOOST_REDIS_IGNORE_HPP
|
||||
#define BOOST_REDIS_IGNORE_HPP
|
||||
|
||||
#include <boost/system/result.hpp>
|
||||
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
namespace boost::redis
|
||||
{
|
||||
|
||||
/** @brief Type used to ignore responses.
|
||||
* @ingroup high-level-api
|
||||
*
|
||||
* For example
|
||||
*
|
||||
* @code
|
||||
* response<ignore_t, std::string, ignore_t> resp;
|
||||
* @endcode
|
||||
*
|
||||
* will ignore the first and third responses. RESP3 errors won't be
|
||||
* ignore but will cause `async_exec` to complete with an error.
|
||||
*/
|
||||
using ignore_t = std::decay_t<decltype(std::ignore)>;
|
||||
|
||||
/** @brief Global ignore object.
|
||||
* @ingroup high-level-api
|
||||
*
|
||||
* Can be used to ignore responses to a request
|
||||
*
|
||||
* @code
|
||||
* conn->async_exec(req, ignore, ...);
|
||||
* @endcode
|
||||
*
|
||||
* RESP3 errors won't be ignore but will cause `async_exec` to
|
||||
* complete with an error.
|
||||
*/
|
||||
extern ignore_t ignore;
|
||||
|
||||
} // boost::redis
|
||||
|
||||
#endif // BOOST_REDIS_IGNORE_HPP
|
||||
@@ -4,7 +4,7 @@
|
||||
* accompanying file LICENSE.txt)
|
||||
*/
|
||||
|
||||
#include <boost/redis/response.hpp>
|
||||
#include <boost/redis/ignore.hpp>
|
||||
|
||||
namespace boost::redis
|
||||
{
|
||||
@@ -276,11 +276,6 @@ private:
|
||||
bool has_hello_priority_ = false;
|
||||
};
|
||||
|
||||
/** @brief The response to a request.
|
||||
*/
|
||||
template <class... Ts>
|
||||
using response = std::tuple<Ts...>;
|
||||
|
||||
} // boost::redis::resp3
|
||||
|
||||
#endif // BOOST_REDIS_REQUEST_HPP
|
||||
|
||||
@@ -41,17 +41,6 @@ auto is_cancelled(T const& self)
|
||||
|
||||
namespace boost::redis::resp3::detail {
|
||||
|
||||
struct ignore_response {
|
||||
void operator()(node<std::string_view> nd, system::error_code& ec)
|
||||
{
|
||||
switch (nd.data_type) {
|
||||
case resp3::type::simple_error: ec = error::resp3_simple_error; return;
|
||||
case resp3::type::blob_error: ec = error::resp3_blob_error; return;
|
||||
default: return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <
|
||||
class AsyncReadStream,
|
||||
class DynamicBuffer,
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <boost/redis/resp3/type.hpp>
|
||||
#include <boost/redis/resp3/detail/parser.hpp>
|
||||
#include <boost/redis/resp3/detail/read_ops.hpp>
|
||||
#include <boost/redis/adapter/ignore.hpp>
|
||||
#include <boost/asio/read.hpp>
|
||||
#include <boost/asio/compose.hpp>
|
||||
#include <boost/asio/async_result.hpp>
|
||||
@@ -47,8 +48,8 @@ namespace boost::redis::resp3 {
|
||||
* the bytes after it returns.
|
||||
*/
|
||||
template <
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
class ResponseAdapter
|
||||
>
|
||||
auto
|
||||
@@ -105,7 +106,7 @@ read(
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer,
|
||||
class ResponseAdapter = detail::ignore_response>
|
||||
class ResponseAdapter = adapter::ignore>
|
||||
auto
|
||||
read(
|
||||
SyncReadStream& stream,
|
||||
@@ -162,7 +163,7 @@ read(
|
||||
template <
|
||||
class AsyncReadStream,
|
||||
class DynamicBuffer,
|
||||
class ResponseAdapter = detail::ignore_response,
|
||||
class ResponseAdapter = adapter::ignore,
|
||||
class CompletionToken = asio::default_completion_token_t<typename AsyncReadStream::executor_type>
|
||||
>
|
||||
auto async_read(
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#define BOOST_REDIS_RESPONSE_HPP
|
||||
|
||||
#include <boost/redis/resp3/node.hpp>
|
||||
#include <boost/redis/adapter/adapt.hpp>
|
||||
#include <boost/redis/adapter/result.hpp>
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
@@ -16,46 +16,21 @@
|
||||
|
||||
namespace boost::redis {
|
||||
|
||||
/** @brief The response to a request.
|
||||
/** @brief Response with compile-time size.
|
||||
* @ingroup high-level-api
|
||||
*/
|
||||
template <class... Ts>
|
||||
using response = std::tuple<Ts...>;
|
||||
using response = std::tuple<adapter::result<Ts>...>;
|
||||
|
||||
/** @brief A generic response to a request
|
||||
* @ingroup high-level-api
|
||||
*
|
||||
* It contains the
|
||||
* This response type can store any type of RESP3 data structure. It
|
||||
* contains the
|
||||
* [pre-order](https://en.wikipedia.org/wiki/Tree_traversal#Pre-order,_NLR)
|
||||
* view of the response tree. Any Redis response can be received in
|
||||
* an array of nodes.
|
||||
* view of the response tree.
|
||||
*/
|
||||
using generic_response = std::vector<resp3::node<std::string>>;
|
||||
|
||||
/** @brief Type used to ignore responses.
|
||||
* @ingroup high-level-api
|
||||
*
|
||||
* For example
|
||||
*
|
||||
* @code
|
||||
* response<boost::redis::ignore_t, std::string, boost::redis::ignore_t> resp;
|
||||
* @endcode
|
||||
*
|
||||
* will cause only the second tuple type to be parsed, the others
|
||||
* will be ignored.
|
||||
*/
|
||||
using ignore_t = adapter::detail::ignore_t;
|
||||
|
||||
/** @brief Global ignore object.
|
||||
* @ingroup high-level-api
|
||||
*
|
||||
* Can be used to ignore responses to a request
|
||||
*
|
||||
* @code
|
||||
* conn->async_exec(req, ignore, ...);
|
||||
* @endcode
|
||||
*/
|
||||
extern ignore_t ignore;
|
||||
using generic_response = adapter::result<std::vector<resp3::node<std::string>>>;
|
||||
|
||||
} // boost::redis::resp3
|
||||
|
||||
|
||||
@@ -6,6 +6,6 @@
|
||||
|
||||
#include <boost/redis/impl/error.ipp>
|
||||
#include <boost/redis/impl/request.ipp>
|
||||
#include <boost/redis/impl/response.ipp>
|
||||
#include <boost/redis/impl/ignore.ipp>
|
||||
#include <boost/redis/resp3/impl/type.ipp>
|
||||
#include <boost/redis/resp3/detail/impl/parser.ipp>
|
||||
|
||||
@@ -21,6 +21,7 @@ using boost::redis::operation;
|
||||
using boost::redis::request;
|
||||
using boost::redis::response;
|
||||
using boost::redis::ignore;
|
||||
using boost::redis::ignore_t;
|
||||
|
||||
auto push_consumer(std::shared_ptr<connection> conn, int expected) -> net::awaitable<void>
|
||||
{
|
||||
@@ -42,7 +43,7 @@ auto echo_session(std::shared_ptr<connection> conn, std::string id, int n) -> ne
|
||||
auto ex = co_await net::this_coro::executor;
|
||||
|
||||
request req;
|
||||
response<boost::redis::ignore_t, std::string> resp;
|
||||
response<ignore_t, std::string> resp;
|
||||
|
||||
for (auto i = 0; i < n; ++i) {
|
||||
auto const msg = id + "/" + std::to_string(i);
|
||||
@@ -53,9 +54,9 @@ auto echo_session(std::shared_ptr<connection> conn, std::string id, int n) -> ne
|
||||
boost::system::error_code ec;
|
||||
co_await conn->async_exec(req, resp, redir(ec));
|
||||
BOOST_CHECK_EQUAL(ec, boost::system::error_code{});
|
||||
BOOST_CHECK_EQUAL(msg, std::get<1>(resp));
|
||||
BOOST_CHECK_EQUAL(msg, std::get<1>(resp).value());
|
||||
req.clear();
|
||||
std::get<1>(resp).clear();
|
||||
std::get<1>(resp).value().clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ using connection = boost::redis::connection;
|
||||
using boost::redis::request;
|
||||
using boost::redis::response;
|
||||
using boost::redis::ignore;
|
||||
using boost::redis::ignore_t;
|
||||
|
||||
BOOST_AUTO_TEST_CASE(hello_priority)
|
||||
{
|
||||
@@ -91,7 +92,7 @@ BOOST_AUTO_TEST_CASE(wrong_response_data_type)
|
||||
req.push("QUIT");
|
||||
|
||||
// Wrong data type.
|
||||
response<boost::redis::ignore_t, int> resp;
|
||||
response<ignore_t, int> resp;
|
||||
net::io_context ioc;
|
||||
|
||||
auto const endpoints = resolve();
|
||||
|
||||
@@ -27,16 +27,20 @@ using namespace net::experimental::awaitable_operators;
|
||||
using boost::redis::operation;
|
||||
using boost::redis::request;
|
||||
using boost::redis::response;
|
||||
using boost::redis::generic_response;
|
||||
using boost::redis::ignore;
|
||||
using boost::redis::ignore_t;
|
||||
|
||||
auto async_ignore_explicit_cancel_of_req_written() -> net::awaitable<void>
|
||||
{
|
||||
auto ex = co_await net::this_coro::executor;
|
||||
|
||||
generic_response gresp;
|
||||
auto conn = std::make_shared<connection>(ex);
|
||||
co_await connect(conn, "127.0.0.1", "6379");
|
||||
|
||||
conn->async_run([conn](auto ec) {
|
||||
std::cout << "async_run: " << ec.message() << std::endl;
|
||||
BOOST_TEST(!ec);
|
||||
});
|
||||
|
||||
@@ -47,14 +51,15 @@ auto async_ignore_explicit_cancel_of_req_written() -> net::awaitable<void>
|
||||
request req0;
|
||||
req0.get_config().coalesce = false;
|
||||
req0.push("HELLO", 3);
|
||||
std::ignore = co_await conn->async_exec(req0, ignore, net::use_awaitable);
|
||||
co_await conn->async_exec(req0, gresp, net::use_awaitable);
|
||||
|
||||
request req1;
|
||||
req1.get_config().coalesce = false;
|
||||
req1.push("BLPOP", "any", 3);
|
||||
|
||||
// Should not be canceled.
|
||||
conn->async_exec(req1, ignore, [](auto ec, auto){
|
||||
conn->async_exec(req1, gresp, [](auto ec, auto){
|
||||
std::cout << "async_exec (1): " << ec.message() << std::endl;
|
||||
BOOST_TEST(!ec);
|
||||
});
|
||||
|
||||
@@ -63,7 +68,8 @@ auto async_ignore_explicit_cancel_of_req_written() -> net::awaitable<void>
|
||||
req2.push("PING", "second");
|
||||
|
||||
// Should be canceled.
|
||||
conn->async_exec(req2, ignore, [](auto ec, auto){
|
||||
conn->async_exec(req2, gresp, [](auto ec, auto){
|
||||
std::cout << "async_exec (2): " << ec.message() << std::endl;
|
||||
BOOST_CHECK_EQUAL(ec, net::error::basic_errors::operation_aborted);
|
||||
});
|
||||
|
||||
@@ -79,7 +85,7 @@ auto async_ignore_explicit_cancel_of_req_written() -> net::awaitable<void>
|
||||
|
||||
// Test whether the connection remains usable after a call to
|
||||
// cancel(exec).
|
||||
co_await conn->async_exec(req3, ignore, net::redirect_error(net::use_awaitable, ec1));
|
||||
co_await conn->async_exec(req3, gresp, net::redirect_error(net::use_awaitable, ec1));
|
||||
|
||||
BOOST_TEST(!ec1);
|
||||
}
|
||||
|
||||
269
tests/conn_exec_error.cpp
Normal file
269
tests/conn_exec_error.cpp
Normal file
@@ -0,0 +1,269 @@
|
||||
/* Copyright (c) 2018-2022 Marcelo Zimbres Silva (mzimbres@gmail.com)
|
||||
*
|
||||
* Distributed under the Boost Software License, Version 1.0. (See
|
||||
* accompanying file LICENSE.txt)
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/system/errc.hpp>
|
||||
|
||||
#define BOOST_TEST_MODULE low level
|
||||
#include <boost/test/included/unit_test.hpp>
|
||||
|
||||
#include <boost/redis.hpp>
|
||||
#include <boost/redis/src.hpp>
|
||||
|
||||
#include "common.hpp"
|
||||
|
||||
namespace net = boost::asio;
|
||||
namespace redis = boost::redis;
|
||||
namespace resp3 = redis::resp3;
|
||||
using error_code = boost::system::error_code;
|
||||
using connection = boost::redis::connection;
|
||||
using boost::redis::request;
|
||||
using boost::redis::response;
|
||||
using boost::redis::generic_response;
|
||||
using boost::redis::ignore;
|
||||
using boost::redis::ignore_t;
|
||||
using boost::redis::error;
|
||||
|
||||
BOOST_AUTO_TEST_CASE(no_ignore_error)
|
||||
{
|
||||
request req;
|
||||
|
||||
// HELLO expects a number, by feeding a string we should get a simple error.
|
||||
req.push("HELLO", "not-a-number");
|
||||
|
||||
net::io_context ioc;
|
||||
|
||||
auto const endpoints = resolve();
|
||||
connection conn{ioc};
|
||||
net::connect(conn.next_layer(), endpoints);
|
||||
|
||||
conn.async_exec(req, ignore, [&](auto ec, auto){
|
||||
BOOST_CHECK_EQUAL(ec, error::resp3_simple_error);
|
||||
conn.cancel(redis::operation::run);
|
||||
});
|
||||
|
||||
conn.async_run([](auto ec){
|
||||
BOOST_CHECK_EQUAL(ec, boost::asio::error::basic_errors::operation_aborted);
|
||||
});
|
||||
|
||||
ioc.run();
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(has_diagnostic)
|
||||
{
|
||||
request req;
|
||||
|
||||
// HELLO expects a number, by feeding a string we should get a simple error.
|
||||
req.push("HELLO", "not-a-number");
|
||||
|
||||
// The second command should be also executed. Notice PING does not
|
||||
// require resp3.
|
||||
req.push("PING", "Barra do Una");
|
||||
|
||||
net::io_context ioc;
|
||||
|
||||
auto const endpoints = resolve();
|
||||
connection conn{ioc};
|
||||
net::connect(conn.next_layer(), endpoints);
|
||||
|
||||
response<std::string, std::string> resp;
|
||||
conn.async_exec(req, resp, [&](auto ec, auto){
|
||||
BOOST_TEST(!ec);
|
||||
|
||||
// HELLO
|
||||
BOOST_TEST(std::get<0>(resp).has_error());
|
||||
BOOST_CHECK_EQUAL(std::get<0>(resp).error().data_type, resp3::type::simple_error);
|
||||
auto const diag = std::get<0>(resp).error().diagnostic;
|
||||
BOOST_TEST(!std::empty(diag));
|
||||
std::cout << "has_diagnostic: " << diag << std::endl;
|
||||
|
||||
// PING
|
||||
BOOST_TEST(std::get<1>(resp).has_value());
|
||||
BOOST_CHECK_EQUAL(std::get<1>(resp).value(), "Barra do Una");
|
||||
|
||||
conn.cancel(redis::operation::run);
|
||||
});
|
||||
|
||||
conn.async_run([](auto ec){
|
||||
BOOST_CHECK_EQUAL(ec, boost::asio::error::basic_errors::operation_aborted);
|
||||
});
|
||||
|
||||
ioc.run();
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(resp3_error_in_cmd_pipeline)
|
||||
{
|
||||
request req1;
|
||||
req1.push("HELLO", "3");
|
||||
req1.push("PING", "req1-msg1");
|
||||
req1.push("PING", "req1-msg2", "extra arg"); // Error.
|
||||
req1.push("PING", "req1-msg3"); // Should run ok.
|
||||
|
||||
response<ignore_t, std::string, std::string, std::string> resp1;
|
||||
|
||||
request req2;
|
||||
req2.push("PING", "req2-msg1");
|
||||
|
||||
response<std::string> resp2;
|
||||
|
||||
net::io_context ioc;
|
||||
auto const endpoints = resolve();
|
||||
connection conn{ioc};
|
||||
net::connect(conn.next_layer(), endpoints);
|
||||
|
||||
auto c2 = [&](auto ec, auto)
|
||||
{
|
||||
BOOST_TEST(!ec);
|
||||
BOOST_TEST(std::get<0>(resp2).has_value());
|
||||
BOOST_CHECK_EQUAL(std::get<0>(resp2).value(), "req2-msg1");
|
||||
conn.cancel(redis::operation::run);
|
||||
};
|
||||
|
||||
auto c1 = [&](auto ec, auto)
|
||||
{
|
||||
BOOST_TEST(!ec);
|
||||
BOOST_TEST(std::get<2>(resp1).has_error());
|
||||
BOOST_CHECK_EQUAL(std::get<2>(resp1).error().data_type, resp3::type::simple_error);
|
||||
auto const diag = std::get<2>(resp1).error().diagnostic;
|
||||
BOOST_TEST(!std::empty(diag));
|
||||
std::cout << "resp3_error_in_cmd_pipeline: " << diag << std::endl;
|
||||
|
||||
BOOST_TEST(std::get<3>(resp1).has_value());
|
||||
BOOST_CHECK_EQUAL(std::get<3>(resp1).value(), "req1-msg3");
|
||||
|
||||
conn.async_exec(req2, resp2, c2);
|
||||
};
|
||||
|
||||
conn.async_exec(req1, resp1, c1);
|
||||
|
||||
conn.async_run([](auto ec){
|
||||
BOOST_CHECK_EQUAL(ec, boost::asio::error::basic_errors::operation_aborted);
|
||||
});
|
||||
|
||||
ioc.run();
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(error_in_transaction)
|
||||
{
|
||||
request req;
|
||||
req.push("HELLO", 3);
|
||||
req.push("MULTI");
|
||||
req.push("PING");
|
||||
req.push("PING", "msg2", "error"); // Error.
|
||||
req.push("PING");
|
||||
req.push("EXEC");
|
||||
req.push("PING");
|
||||
|
||||
response<
|
||||
ignore_t, // hello
|
||||
ignore_t, // multi
|
||||
ignore_t, // ping
|
||||
ignore_t, // ping
|
||||
ignore_t, // ping
|
||||
response<std::string, std::string, std::string>, // exec
|
||||
std::string // ping
|
||||
> resp;
|
||||
|
||||
|
||||
net::io_context ioc;
|
||||
|
||||
auto const endpoints = resolve();
|
||||
connection conn{ioc};
|
||||
net::connect(conn.next_layer(), endpoints);
|
||||
|
||||
conn.async_exec(req, resp, [&](auto ec, auto){
|
||||
BOOST_TEST(!ec);
|
||||
|
||||
BOOST_TEST(std::get<0>(resp).has_value());
|
||||
BOOST_TEST(std::get<1>(resp).has_value());
|
||||
BOOST_TEST(std::get<2>(resp).has_value());
|
||||
BOOST_TEST(std::get<3>(resp).has_value());
|
||||
BOOST_TEST(std::get<4>(resp).has_value());
|
||||
BOOST_TEST(std::get<5>(resp).has_value());
|
||||
|
||||
// Test errors in the pipeline commands.
|
||||
BOOST_TEST(std::get<0>(std::get<5>(resp).value()).has_value());
|
||||
BOOST_CHECK_EQUAL(std::get<0>(std::get<5>(resp).value()).value(), "PONG");
|
||||
|
||||
// The ping in the transaction that should be an error.
|
||||
BOOST_TEST(std::get<1>(std::get<5>(resp).value()).has_error());
|
||||
BOOST_CHECK_EQUAL(std::get<1>(std::get<5>(resp).value()).error().data_type, resp3::type::simple_error);
|
||||
auto const diag = std::get<1>(std::get<5>(resp).value()).error().diagnostic;
|
||||
BOOST_TEST(!std::empty(diag));
|
||||
|
||||
// The ping thereafter in the transaction should not be an error.
|
||||
BOOST_TEST(std::get<2>(std::get<5>(resp).value()).has_value());
|
||||
//BOOST_CHECK_EQUAL(std::get<2>(std::get<4>(resp).value()).value(), "PONG");
|
||||
|
||||
// The command right after the pipeline should be successful.
|
||||
BOOST_TEST(std::get<6>(resp).has_value());
|
||||
BOOST_CHECK_EQUAL(std::get<6>(resp).value(), "PONG");
|
||||
|
||||
conn.cancel(redis::operation::run);
|
||||
});
|
||||
|
||||
conn.async_run([](auto ec){
|
||||
BOOST_CHECK_EQUAL(ec, boost::asio::error::basic_errors::operation_aborted);
|
||||
});
|
||||
|
||||
ioc.run();
|
||||
}
|
||||
|
||||
// This test is important because a subscriber has no response on
|
||||
// success, but on error, for example when using a wrong syntax, the
|
||||
// server will send a simple error response the client is not
|
||||
// expecting.
|
||||
BOOST_AUTO_TEST_CASE(subscriber_wrong_syntax)
|
||||
{
|
||||
request req1;
|
||||
req1.push("HELLO", 3);
|
||||
|
||||
request req2;
|
||||
req2.push("SUBSCRIBE"); // Wrong command synthax.
|
||||
|
||||
net::io_context ioc;
|
||||
auto const endpoints = resolve();
|
||||
connection conn{ioc};
|
||||
net::connect(conn.next_layer(), endpoints);
|
||||
|
||||
auto c2 = [&](auto ec, auto)
|
||||
{
|
||||
std::cout << "async_exec: subscribe" << std::endl;
|
||||
BOOST_TEST(!ec);
|
||||
};
|
||||
|
||||
auto c1 = [&](auto ec, auto)
|
||||
{
|
||||
std::cout << "async_exec: hello" << std::endl;
|
||||
BOOST_TEST(!ec);
|
||||
conn.async_exec(req2, ignore, c2);
|
||||
};
|
||||
|
||||
conn.async_exec(req1, ignore, c1);
|
||||
|
||||
generic_response gresp;
|
||||
auto c3 = [&](auto ec, auto)
|
||||
{
|
||||
std::cout << "async_receive" << std::endl;
|
||||
BOOST_TEST(!ec);
|
||||
BOOST_TEST(gresp.has_error());
|
||||
BOOST_CHECK_EQUAL(gresp.error().data_type, resp3::type::simple_error);
|
||||
BOOST_TEST(!std::empty(gresp.error().diagnostic));
|
||||
std::cout << gresp.error().diagnostic << std::endl;
|
||||
conn.cancel(redis::operation::run);
|
||||
};
|
||||
|
||||
conn.async_receive(gresp, c3);
|
||||
|
||||
conn.async_run([](auto ec){
|
||||
std::cout << "async_run" << std::endl;
|
||||
BOOST_CHECK_EQUAL(ec, boost::asio::error::basic_errors::operation_aborted);
|
||||
});
|
||||
|
||||
ioc.run();
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ using net::experimental::as_tuple;
|
||||
using boost::redis::request;
|
||||
using boost::redis::response;
|
||||
using boost::redis::ignore;
|
||||
using boost::redis::ignore_t;
|
||||
|
||||
BOOST_AUTO_TEST_CASE(push_filtered_out)
|
||||
{
|
||||
@@ -41,7 +42,7 @@ BOOST_AUTO_TEST_CASE(push_filtered_out)
|
||||
req.push("SUBSCRIBE", "channel");
|
||||
req.push("QUIT");
|
||||
|
||||
response<boost::redis::ignore_t, std::string, std::string> resp;
|
||||
response<ignore_t, std::string, std::string> resp;
|
||||
conn.async_exec(req, resp, [](auto ec, auto){
|
||||
BOOST_TEST(!ec);
|
||||
});
|
||||
@@ -56,31 +57,8 @@ BOOST_AUTO_TEST_CASE(push_filtered_out)
|
||||
|
||||
ioc.run();
|
||||
|
||||
BOOST_CHECK_EQUAL(std::get<1>(resp), "PONG");
|
||||
BOOST_CHECK_EQUAL(std::get<2>(resp), "OK");
|
||||
}
|
||||
|
||||
void receive_wrong_syntax(request const& req)
|
||||
{
|
||||
net::io_context ioc;
|
||||
auto const endpoints = resolve();
|
||||
connection conn{ioc};
|
||||
net::connect(conn.next_layer(), endpoints);
|
||||
|
||||
conn.async_exec(req, ignore, [](auto ec, auto){
|
||||
BOOST_TEST(!ec);
|
||||
});
|
||||
|
||||
conn.async_run([](auto ec){
|
||||
BOOST_CHECK_EQUAL(ec, boost::asio::error::basic_errors::operation_aborted);
|
||||
});
|
||||
|
||||
conn.async_receive(ignore, [&](auto ec, auto){
|
||||
BOOST_TEST(!ec);
|
||||
conn.cancel(boost::redis::operation::run);
|
||||
});
|
||||
|
||||
ioc.run();
|
||||
BOOST_CHECK_EQUAL(std::get<1>(resp).value(), "PONG");
|
||||
BOOST_CHECK_EQUAL(std::get<2>(resp).value(), "OK");
|
||||
}
|
||||
|
||||
#ifdef BOOST_ASIO_HAS_CO_AWAIT
|
||||
@@ -301,33 +279,6 @@ BOOST_AUTO_TEST_CASE(many_subscribers)
|
||||
}
|
||||
#endif
|
||||
|
||||
BOOST_AUTO_TEST_CASE(receive_wrong_syntax1)
|
||||
{
|
||||
request req1{{false}};
|
||||
req1.push("HELLO", 3);
|
||||
req1.push("PING", "Message");
|
||||
req1.push("SUBSCRIBE"); // Wrong command synthax.
|
||||
|
||||
req1.get_config().coalesce = true;
|
||||
receive_wrong_syntax(req1);
|
||||
|
||||
req1.get_config().coalesce = false;
|
||||
receive_wrong_syntax(req1);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(receice_wrong_syntay2)
|
||||
{
|
||||
request req2{{false}};
|
||||
req2.push("HELLO", 3);
|
||||
req2.push("SUBSCRIBE"); // Wrong command syntax.
|
||||
|
||||
req2.get_config().coalesce = true;
|
||||
receive_wrong_syntax(req2);
|
||||
|
||||
req2.get_config().coalesce = false;
|
||||
receive_wrong_syntax(req2);
|
||||
}
|
||||
|
||||
#else
|
||||
int main() {}
|
||||
#endif
|
||||
|
||||
@@ -21,6 +21,7 @@ namespace net = boost::asio;
|
||||
using connection = boost::redis::ssl::connection;
|
||||
using boost::redis::request;
|
||||
using boost::redis::response;
|
||||
using boost::redis::ignore_t;
|
||||
|
||||
struct endpoint {
|
||||
std::string host;
|
||||
@@ -43,8 +44,7 @@ BOOST_AUTO_TEST_CASE(ping)
|
||||
req.push("PING", in);
|
||||
req.push("QUIT");
|
||||
|
||||
std::string out;
|
||||
auto resp = std::tie(std::ignore, out, std::ignore);
|
||||
response<ignore_t, std::string, ignore_t> resp;
|
||||
|
||||
auto const endpoints = resolve("db.occase.de", "6380");
|
||||
|
||||
@@ -67,6 +67,6 @@ BOOST_AUTO_TEST_CASE(ping)
|
||||
|
||||
ioc.run();
|
||||
|
||||
BOOST_CHECK_EQUAL(in, out);
|
||||
BOOST_CHECK_EQUAL(in, std::get<1>(resp).value());
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ using namespace net::experimental::awaitable_operators;
|
||||
using steady_timer = net::use_awaitable_t<>::as_default_on_t<net::steady_timer>;
|
||||
using boost::redis::request;
|
||||
using boost::redis::response;
|
||||
using boost::redis::ignore;
|
||||
|
||||
// Push consumer
|
||||
auto receiver(std::shared_ptr<connection> conn) -> net::awaitable<void>
|
||||
@@ -36,12 +37,11 @@ auto periodic_task(std::shared_ptr<connection> conn) -> net::awaitable<void>
|
||||
// that result in the connection being closed.
|
||||
request req;
|
||||
req.push("GET", "mykey");
|
||||
response<std::string> response;
|
||||
auto [ec, u] = co_await conn->async_exec(req, response, net::as_tuple(net::use_awaitable));
|
||||
auto [ec, u] = co_await conn->async_exec(req, ignore, net::as_tuple(net::use_awaitable));
|
||||
if (ec) {
|
||||
std::cout << "Error: " << ec << std::endl;
|
||||
} else {
|
||||
std::cout << "Response is: " << std::get<0>(response) << std::endl;
|
||||
std::cout << "no error: " << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,43 +34,46 @@ namespace net = boost::asio;
|
||||
namespace resp3 = boost::redis::resp3;
|
||||
using boost::redis::request;
|
||||
using boost::redis::response;
|
||||
using boost::redis::ignore;
|
||||
using boost::redis::ignore_t;
|
||||
using boost::redis::adapter::result;
|
||||
|
||||
using test_stream = boost::beast::test::stream;
|
||||
using boost::redis::adapter::adapt2;
|
||||
using node_type = boost::redis::resp3::node<std::string>;
|
||||
using vec_node_type = std::vector<node_type>;
|
||||
using vec_type = std::vector<std::string>;
|
||||
using op_vec_type = std::optional<std::vector<std::string>>;
|
||||
using node_type = result<boost::redis::resp3::node<std::string>>;
|
||||
using vec_node_type = result<std::vector<boost::redis::resp3::node<std::string>>>;
|
||||
using vec_type = result<std::vector<std::string>>;
|
||||
using op_vec_type = result<std::optional<std::vector<std::string>>>;
|
||||
|
||||
// Set
|
||||
using set_type = std::set<std::string>;
|
||||
using mset_type = std::multiset<std::string>;
|
||||
using uset_type = std::unordered_set<std::string>;
|
||||
using muset_type = std::unordered_multiset<std::string>;
|
||||
using set_type = result<std::set<std::string>>;
|
||||
using mset_type = result<std::multiset<std::string>>;
|
||||
using uset_type = result<std::unordered_set<std::string>>;
|
||||
using muset_type = result<std::unordered_multiset<std::string>>;
|
||||
|
||||
// Array
|
||||
using tuple_int_2 = response<int, int>;
|
||||
using array_type = std::array<int, 3>;
|
||||
using array_type2 = std::array<int, 1>;
|
||||
using tuple_int_2 = result<response<int, int>>;
|
||||
using array_type = result<std::array<int, 3>>;
|
||||
using array_type2 = result<std::array<int, 1>>;
|
||||
|
||||
// Map
|
||||
using map_type = std::map<std::string, std::string>;
|
||||
using mmap_type = std::multimap<std::string, std::string>;
|
||||
using umap_type = std::unordered_map<std::string, std::string>;
|
||||
using mumap_type = std::unordered_multimap<std::string, std::string>;
|
||||
using op_map_type = std::optional<std::map<std::string, std::string>>;
|
||||
using tuple8_type = response<std::string, std::string, std::string, std::string, std::string, std::string, std::string, std::string>;
|
||||
using map_type = result<std::map<std::string, std::string>>;
|
||||
using mmap_type = result<std::multimap<std::string, std::string>>;
|
||||
using umap_type = result<std::unordered_map<std::string, std::string>>;
|
||||
using mumap_type = result<std::unordered_multimap<std::string, std::string>>;
|
||||
using op_map_type = result<std::optional<std::map<std::string, std::string>>>;
|
||||
using tuple8_type = result<response<std::string, std::string, std::string, std::string, std::string, std::string, std::string, std::string>>;
|
||||
|
||||
// Null
|
||||
using op_type_01 = std::optional<bool>;
|
||||
using op_type_02 = std::optional<int>;
|
||||
using op_type_03 = std::optional<std::string>;
|
||||
using op_type_04 = std::optional<std::vector<std::string>>;
|
||||
using op_type_05 = std::optional<std::list<std::string>>;
|
||||
using op_type_06 = std::optional<std::map<std::string, std::string>>;
|
||||
using op_type_07 = std::optional<std::unordered_map<std::string, std::string>>;
|
||||
using op_type_08 = std::optional<std::set<std::string>>;
|
||||
using op_type_09 = std::optional<std::unordered_set<std::string>>;
|
||||
using op_type_01 = result<std::optional<bool>>;
|
||||
using op_type_02 = result<std::optional<int>>;
|
||||
using op_type_03 = result<std::optional<std::string>>;
|
||||
using op_type_04 = result<std::optional<std::vector<std::string>>>;
|
||||
using op_type_05 = result<std::optional<std::list<std::string>>>;
|
||||
using op_type_06 = result<std::optional<std::map<std::string, std::string>>>;
|
||||
using op_type_07 = result<std::optional<std::unordered_map<std::string, std::string>>>;
|
||||
using op_type_08 = result<std::optional<std::set<std::string>>>;
|
||||
using op_type_09 = result<std::optional<std::unordered_set<std::string>>>;
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
|
||||
@@ -79,12 +82,13 @@ struct expect {
|
||||
std::string in;
|
||||
Result expected;
|
||||
boost::system::error_code ec{};
|
||||
resp3::type error_type = resp3::type::invalid;
|
||||
};
|
||||
|
||||
template <class Result>
|
||||
auto make_expected(std::string in, Result expected, boost::system::error_code ec = {})
|
||||
auto make_expected(std::string in, Result expected, boost::system::error_code ec = {}, resp3::type error_type = resp3::type::invalid)
|
||||
{
|
||||
return expect<Result>{in, expected, ec};
|
||||
return expect<Result>{in, expected, ec, error_type};
|
||||
}
|
||||
|
||||
template <class Result>
|
||||
@@ -96,12 +100,21 @@ void test_sync(net::any_io_executor ex, expect<Result> e)
|
||||
Result result;
|
||||
boost::system::error_code ec;
|
||||
resp3::read(ts, net::dynamic_buffer(rbuffer), adapt2(result), ec);
|
||||
BOOST_CHECK_EQUAL(ec, e.ec);
|
||||
if (e.ec)
|
||||
if (e.ec) {
|
||||
BOOST_CHECK_EQUAL(ec, e.ec);
|
||||
return;
|
||||
}
|
||||
|
||||
BOOST_TEST(!ec);
|
||||
BOOST_TEST(rbuffer.empty());
|
||||
auto const res = result == e.expected;
|
||||
BOOST_TEST(res);
|
||||
|
||||
if (result.has_value()) {
|
||||
auto const res = result == e.expected;
|
||||
BOOST_TEST(res);
|
||||
} else {
|
||||
BOOST_TEST(result.has_error());
|
||||
BOOST_CHECK_EQUAL(result.error().data_type, e.error_type);
|
||||
}
|
||||
}
|
||||
|
||||
template <class Result>
|
||||
@@ -125,12 +138,21 @@ public:
|
||||
auto self = this->shared_from_this();
|
||||
auto f = [self](auto ec, auto)
|
||||
{
|
||||
BOOST_CHECK_EQUAL(ec, self->data_.ec);
|
||||
if (self->data_.ec)
|
||||
if (self->data_.ec) {
|
||||
BOOST_CHECK_EQUAL(ec, self->data_.ec);
|
||||
return;
|
||||
}
|
||||
|
||||
BOOST_TEST(!ec);
|
||||
BOOST_TEST(self->rbuffer_.empty());
|
||||
auto const res = self->result_ == self->data_.expected;
|
||||
BOOST_TEST(res);
|
||||
|
||||
if (self->result_.has_value()) {
|
||||
auto const res = self->result_ == self->data_.expected;
|
||||
BOOST_TEST(res);
|
||||
} else {
|
||||
BOOST_TEST(self->result_.has_error());
|
||||
BOOST_CHECK_EQUAL(self->result_.error().data_type, self->data_.error_type);
|
||||
}
|
||||
};
|
||||
|
||||
resp3::async_read(
|
||||
@@ -171,65 +193,66 @@ auto make_blob_string(std::string const& b)
|
||||
return wire;
|
||||
}
|
||||
|
||||
std::optional<int> op_int_ok = 11;
|
||||
std::optional<bool> op_bool_ok = true;
|
||||
result<std::optional<int>> op_int_ok = 11;
|
||||
result<std::optional<bool>> op_bool_ok = true;
|
||||
|
||||
// TODO: Test a streamed string that is not finished with a string of
|
||||
// size 0 but other command comes in.
|
||||
std::vector<node_type> streamed_string_e1
|
||||
{ {boost::redis::resp3::type::streamed_string, 0, 1, ""}
|
||||
vec_node_type streamed_string_e1
|
||||
{{ {boost::redis::resp3::type::streamed_string, 0, 1, ""}
|
||||
, {boost::redis::resp3::type::streamed_string_part, 1, 1, "Hell"}
|
||||
, {boost::redis::resp3::type::streamed_string_part, 1, 1, "o wor"}
|
||||
, {boost::redis::resp3::type::streamed_string_part, 1, 1, "d"}
|
||||
, {boost::redis::resp3::type::streamed_string_part, 1, 1, ""}
|
||||
};
|
||||
}};
|
||||
|
||||
std::vector<node_type> streamed_string_e2 { {resp3::type::streamed_string, 0UL, 1UL, {}}, {resp3::type::streamed_string_part, 1UL, 1UL, {}} };
|
||||
vec_node_type streamed_string_e2
|
||||
{{{resp3::type::streamed_string, 0UL, 1UL, {}}, {resp3::type::streamed_string_part, 1UL, 1UL, {}} }};
|
||||
|
||||
std::vector<node_type> const push_e1a
|
||||
{ {resp3::type::push, 4UL, 0UL, {}}
|
||||
vec_node_type const push_e1a
|
||||
{{ {resp3::type::push, 4UL, 0UL, {}}
|
||||
, {resp3::type::simple_string, 1UL, 1UL, "pubsub"}
|
||||
, {resp3::type::simple_string, 1UL, 1UL, "message"}
|
||||
, {resp3::type::simple_string, 1UL, 1UL, "some-channel"}
|
||||
, {resp3::type::simple_string, 1UL, 1UL, "some message"}
|
||||
};
|
||||
}};
|
||||
|
||||
std::vector<node_type> const push_e1b
|
||||
{ {resp3::type::push, 0UL, 0UL, {}} };
|
||||
vec_node_type const push_e1b
|
||||
{{{resp3::type::push, 0UL, 0UL, {}}}};
|
||||
|
||||
std::vector<node_type> const set_expected1a
|
||||
{ {resp3::type::set, 6UL, 0UL, {}}
|
||||
vec_node_type const set_expected1a
|
||||
{{{resp3::type::set, 6UL, 0UL, {}}
|
||||
, {resp3::type::simple_string, 1UL, 1UL, {"orange"}}
|
||||
, {resp3::type::simple_string, 1UL, 1UL, {"apple"}}
|
||||
, {resp3::type::simple_string, 1UL, 1UL, {"one"}}
|
||||
, {resp3::type::simple_string, 1UL, 1UL, {"two"}}
|
||||
, {resp3::type::simple_string, 1UL, 1UL, {"three"}}
|
||||
, {resp3::type::simple_string, 1UL, 1UL, {"orange"}}
|
||||
};
|
||||
}};
|
||||
|
||||
mset_type const set_e1f{"apple", "one", "orange", "orange", "three", "two"};
|
||||
uset_type const set_e1c{"apple", "one", "orange", "three", "two"};
|
||||
muset_type const set_e1g{"apple", "one", "orange", "orange", "three", "two"};
|
||||
vec_type const set_e1d = {"orange", "apple", "one", "two", "three", "orange"};
|
||||
mset_type const set_e1f{{"apple", "one", "orange", "orange", "three", "two"}};
|
||||
uset_type const set_e1c{{"apple", "one", "orange", "three", "two"}};
|
||||
muset_type const set_e1g{{"apple", "one", "orange", "orange", "three", "two"}};
|
||||
vec_type const set_e1d = {{"orange", "apple", "one", "two", "three", "orange"}};
|
||||
op_vec_type const set_expected_1e = set_e1d;
|
||||
|
||||
std::vector<node_type> const array_e1a
|
||||
{ {resp3::type::array, 3UL, 0UL, {}}
|
||||
vec_node_type const array_e1a
|
||||
{{ {resp3::type::array, 3UL, 0UL, {}}
|
||||
, {resp3::type::blob_string, 1UL, 1UL, {"11"}}
|
||||
, {resp3::type::blob_string, 1UL, 1UL, {"22"}}
|
||||
, {resp3::type::blob_string, 1UL, 1UL, {"3"}}
|
||||
};
|
||||
}};
|
||||
|
||||
std::vector<int> const array_e1b{11, 22, 3};
|
||||
std::vector<std::string> const array_e1c{"11", "22", "3"};
|
||||
std::vector<std::string> const array_e1d{};
|
||||
std::vector<node_type> const array_e1e{{resp3::type::array, 0UL, 0UL, {}}};
|
||||
array_type const array_e1f{11, 22, 3};
|
||||
std::list<int> const array_e1g{11, 22, 3};
|
||||
std::deque<int> const array_e1h{11, 22, 3};
|
||||
result<std::vector<int>> const array_e1b{{11, 22, 3}};
|
||||
result<std::vector<std::string>> const array_e1c{{"11", "22", "3"}};
|
||||
result<std::vector<std::string>> const array_e1d{};
|
||||
vec_node_type const array_e1e{{{resp3::type::array, 0UL, 0UL, {}}}};
|
||||
array_type const array_e1f{{11, 22, 3}};
|
||||
result<std::list<int>> const array_e1g{{11, 22, 3}};
|
||||
result<std::deque<int>> const array_e1h{{11, 22, 3}};
|
||||
|
||||
std::vector<node_type> const map_expected_1a
|
||||
{ {resp3::type::map, 4UL, 0UL, {}}
|
||||
vec_node_type const map_expected_1a
|
||||
{{ {resp3::type::map, 4UL, 0UL, {}}
|
||||
, {resp3::type::blob_string, 1UL, 1UL, {"key1"}}
|
||||
, {resp3::type::blob_string, 1UL, 1UL, {"value1"}}
|
||||
, {resp3::type::blob_string, 1UL, 1UL, {"key2"}}
|
||||
@@ -238,40 +261,40 @@ std::vector<node_type> const map_expected_1a
|
||||
, {resp3::type::blob_string, 1UL, 1UL, {"value3"}}
|
||||
, {resp3::type::blob_string, 1UL, 1UL, {"key3"}}
|
||||
, {resp3::type::blob_string, 1UL, 1UL, {"value3"}}
|
||||
};
|
||||
}};
|
||||
|
||||
map_type const map_expected_1b
|
||||
{ {"key1", "value1"}
|
||||
{{ {"key1", "value1"}
|
||||
, {"key2", "value2"}
|
||||
, {"key3", "value3"}
|
||||
};
|
||||
}};
|
||||
|
||||
umap_type const map_e1g
|
||||
{ {"key1", "value1"}
|
||||
{{ {"key1", "value1"}
|
||||
, {"key2", "value2"}
|
||||
, {"key3", "value3"}
|
||||
};
|
||||
}};
|
||||
|
||||
mmap_type const map_e1k
|
||||
{ {"key1", "value1"}
|
||||
{{ {"key1", "value1"}
|
||||
, {"key2", "value2"}
|
||||
, {"key3", "value3"}
|
||||
, {"key3", "value3"}
|
||||
};
|
||||
}};
|
||||
|
||||
mumap_type const map_e1l
|
||||
{ {"key1", "value1"}
|
||||
{{ {"key1", "value1"}
|
||||
, {"key2", "value2"}
|
||||
, {"key3", "value3"}
|
||||
, {"key3", "value3"}
|
||||
};
|
||||
}};
|
||||
|
||||
std::vector<std::string> const map_expected_1c
|
||||
{ "key1", "value1"
|
||||
result<std::vector<std::string>> const map_expected_1c
|
||||
{{ "key1", "value1"
|
||||
, "key2", "value2"
|
||||
, "key3", "value3"
|
||||
, "key3", "value3"
|
||||
};
|
||||
}};
|
||||
|
||||
op_map_type const map_expected_1d = map_expected_1b;
|
||||
|
||||
@@ -284,18 +307,18 @@ tuple8_type const map_e1f
|
||||
, std::string{"key3"}, std::string{"value3"}
|
||||
};
|
||||
|
||||
std::vector<node_type> const attr_e1a
|
||||
{ {resp3::type::attribute, 1UL, 0UL, {}}
|
||||
vec_node_type const attr_e1a
|
||||
{{ {resp3::type::attribute, 1UL, 0UL, {}}
|
||||
, {resp3::type::simple_string, 1UL, 1UL, "key-popularity"}
|
||||
, {resp3::type::map, 2UL, 1UL, {}}
|
||||
, {resp3::type::blob_string, 1UL, 2UL, "a"}
|
||||
, {resp3::type::doublean, 1UL, 2UL, "0.1923"}
|
||||
, {resp3::type::blob_string, 1UL, 2UL, "b"}
|
||||
, {resp3::type::doublean, 1UL, 2UL, "0.0012"}
|
||||
};
|
||||
} };
|
||||
|
||||
std::vector<node_type> const attr_e1b
|
||||
{ {resp3::type::attribute, 0UL, 0UL, {}} };
|
||||
vec_node_type const attr_e1b
|
||||
{{{resp3::type::attribute, 0UL, 0UL, {}} }};
|
||||
|
||||
#define S01a "#11\r\n"
|
||||
#define S01b "#f\r\n"
|
||||
@@ -370,38 +393,34 @@ std::vector<node_type> const attr_e1b
|
||||
#define S18d "$0\r\n\r\n"
|
||||
|
||||
#define NUMBER_TEST_CONDITIONS(test) \
|
||||
test(ex, make_expected(S01a, std::optional<bool>{}, boost::redis::error::unexpected_bool_value)); \
|
||||
test(ex, make_expected(S01b, bool{false})); \
|
||||
test(ex, make_expected(S01b, node_type{resp3::type::boolean, 1UL, 0UL, {"f"}})); \
|
||||
test(ex, make_expected(S01c, bool{true})); \
|
||||
test(ex, make_expected(S01c, node_type{resp3::type::boolean, 1UL, 0UL, {"t"}})); \
|
||||
test(ex, make_expected(S01a, result<std::optional<bool>>{}, boost::redis::error::unexpected_bool_value)); \
|
||||
test(ex, make_expected(S01b, result<bool>{{false}})); \
|
||||
test(ex, make_expected(S01b, node_type{{resp3::type::boolean, 1UL, 0UL, {"f"}}})); \
|
||||
test(ex, make_expected(S01c, result<bool>{{true}})); \
|
||||
test(ex, make_expected(S01c, node_type{{resp3::type::boolean, 1UL, 0UL, {"t"}}})); \
|
||||
test(ex, make_expected(S01c, op_bool_ok)); \
|
||||
test(ex, make_expected(S01c, std::map<int, int>{}, boost::redis::error::expects_resp3_map)); \
|
||||
test(ex, make_expected(S01c, std::set<int>{}, boost::redis::error::expects_resp3_set)); \
|
||||
test(ex, make_expected(S01c, std::unordered_map<int, int>{}, boost::redis::error::expects_resp3_map)); \
|
||||
test(ex, make_expected(S01c, std::unordered_set<int>{}, boost::redis::error::expects_resp3_set)); \
|
||||
test(ex, make_expected(S01c, result<std::map<int, int>>{}, boost::redis::error::expects_resp3_map)); \
|
||||
test(ex, make_expected(S01c, result<std::set<int>>{}, boost::redis::error::expects_resp3_set)); \
|
||||
test(ex, make_expected(S01c, result<std::unordered_map<int, int>>{}, boost::redis::error::expects_resp3_map)); \
|
||||
test(ex, make_expected(S01c, result<std::unordered_set<int>>{}, boost::redis::error::expects_resp3_set)); \
|
||||
test(ex, make_expected(S02a, streamed_string_e2)); \
|
||||
test(ex, make_expected(S03a, int{}, boost::redis::error::expects_resp3_simple_type));\
|
||||
test(ex, make_expected(S03a, std::optional<int>{}, boost::redis::error::expects_resp3_simple_type));; \
|
||||
test(ex, make_expected(S02b, int{}, boost::redis::error::not_a_number)); \
|
||||
test(ex, make_expected(S02b, std::string{"Hello word"})); \
|
||||
test(ex, make_expected(S03a, result<int>{}, boost::redis::error::expects_resp3_simple_type));\
|
||||
test(ex, make_expected(S03a, result<std::optional<int>>{}, boost::redis::error::expects_resp3_simple_type));; \
|
||||
test(ex, make_expected(S02b, result<int>{}, boost::redis::error::not_a_number)); \
|
||||
test(ex, make_expected(S02b, result<std::string>{std::string{"Hello word"}})); \
|
||||
test(ex, make_expected(S02b, streamed_string_e1)); \
|
||||
test(ex, make_expected(S02c, std::string{}, boost::redis::error::not_a_number)); \
|
||||
test(ex, make_expected(S04a, response<int>{11})); \
|
||||
test(ex, make_expected(S05a, node_type{resp3::type::number, 1UL, 0UL, {"-3"}})); \
|
||||
test(ex, make_expected(S05b, int{11})); \
|
||||
test(ex, make_expected(S02c, result<std::string>{}, boost::redis::error::not_a_number)); \
|
||||
test(ex, make_expected(S05a, node_type{{resp3::type::number, 1UL, 0UL, {"-3"}}})); \
|
||||
test(ex, make_expected(S05b, result<int>{11})); \
|
||||
test(ex, make_expected(S05b, op_int_ok)); \
|
||||
test(ex, make_expected(S05b, std::list<std::string>{}, boost::redis::error::expects_resp3_aggregate)); \
|
||||
test(ex, make_expected(S05b, std::map<std::string, std::string>{}, boost::redis::error::expects_resp3_map)); \
|
||||
test(ex, make_expected(S05b, std::set<std::string>{}, boost::redis::error::expects_resp3_set)); \
|
||||
test(ex, make_expected(S05b, std::unordered_map<std::string, std::string>{}, boost::redis::error::expects_resp3_map)); \
|
||||
test(ex, make_expected(S05b, std::unordered_set<std::string>{}, boost::redis::error::expects_resp3_set)); \
|
||||
test(ex, make_expected(S05b, result<std::list<std::string>>{}, boost::redis::error::expects_resp3_aggregate)); \
|
||||
test(ex, make_expected(S05b, result<std::map<std::string, std::string>>{}, boost::redis::error::expects_resp3_map)); \
|
||||
test(ex, make_expected(S05b, result<std::set<std::string>>{}, boost::redis::error::expects_resp3_set)); \
|
||||
test(ex, make_expected(S05b, result<std::unordered_map<std::string, std::string>>{}, boost::redis::error::expects_resp3_map)); \
|
||||
test(ex, make_expected(S05b, result<std::unordered_set<std::string>>{}, boost::redis::error::expects_resp3_set)); \
|
||||
test(ex, make_expected(s05c, array_type2{}, boost::redis::error::expects_resp3_aggregate));\
|
||||
test(ex, make_expected(s05c, node_type{resp3::type::number, 1UL, 0UL, {"3"}})); \
|
||||
test(ex, make_expected(S06a, array_type{}, boost::redis::error::resp3_null));\
|
||||
test(ex, make_expected(S06a, int{0}, boost::redis::error::resp3_null)); \
|
||||
test(ex, make_expected(S06a, map_type{}, boost::redis::error::resp3_null));\
|
||||
test(ex, make_expected(S06a, op_type_01{}));\
|
||||
test(ex, make_expected(s05c, node_type{{resp3::type::number, 1UL, 0UL, {"3"}}}));\
|
||||
test(ex, make_expected(S06a, op_type_01{})); \
|
||||
test(ex, make_expected(S06a, op_type_02{}));\
|
||||
test(ex, make_expected(S06a, op_type_03{}));\
|
||||
test(ex, make_expected(S06a, op_type_04{}));\
|
||||
@@ -410,21 +429,17 @@ std::vector<node_type> const attr_e1b
|
||||
test(ex, make_expected(S06a, op_type_07{}));\
|
||||
test(ex, make_expected(S06a, op_type_08{}));\
|
||||
test(ex, make_expected(S06a, op_type_09{}));\
|
||||
test(ex, make_expected(S06a, std::list<int>{}, boost::redis::error::resp3_null));\
|
||||
test(ex, make_expected(S06a, std::vector<int>{}, boost::redis::error::resp3_null));\
|
||||
test(ex, make_expected(S07a, push_e1a)); \
|
||||
test(ex, make_expected(S07b, push_e1b)); \
|
||||
test(ex, make_expected(S04b, map_type{}, boost::redis::error::expects_resp3_map));\
|
||||
test(ex, make_expected(S03b, map_e1f));\
|
||||
test(ex, make_expected(S03b, map_e1g));\
|
||||
test(ex, make_expected(S03b, map_e1k));\
|
||||
test(ex, make_expected(S03b, map_e1l));\
|
||||
test(ex, make_expected(S03b, map_expected_1a));\
|
||||
test(ex, make_expected(S03b, map_expected_1b));\
|
||||
test(ex, make_expected(S03b, map_expected_1c));\
|
||||
test(ex, make_expected(S03b, map_expected_1d));\
|
||||
test(ex, make_expected(S03b, map_expected_1e));\
|
||||
test(ex, make_expected(S04c, response<op_map_type>{map_expected_1d}));\
|
||||
test(ex, make_expected(S08a, attr_e1a)); \
|
||||
test(ex, make_expected(S08b, attr_e1b)); \
|
||||
test(ex, make_expected(S04e, array_e1a));\
|
||||
@@ -446,41 +461,49 @@ std::vector<node_type> const attr_e1b
|
||||
test(ex, make_expected(S09a, set_e1g)); \
|
||||
test(ex, make_expected(S09a, set_expected1a)); \
|
||||
test(ex, make_expected(S09a, set_expected_1e)); \
|
||||
test(ex, make_expected(S09a, set_type{"apple", "one", "orange", "three", "two"})); \
|
||||
test(ex, make_expected(S04d, response<uset_type>{set_e1c})); \
|
||||
test(ex, make_expected(S09b, std::vector<node_type>{ {resp3::type::set, 0UL, 0UL, {}} })); \
|
||||
test(ex, make_expected(S10a, boost::redis::ignore, boost::redis::error::resp3_simple_error)); \
|
||||
test(ex, make_expected(S10a, node_type{resp3::type::simple_error, 1UL, 0UL, {"Error"}}, boost::redis::error::resp3_simple_error)); \
|
||||
test(ex, make_expected(S10b, node_type{resp3::type::simple_error, 1UL, 0UL, {""}}, boost::redis::error::resp3_simple_error)); \
|
||||
test(ex, make_expected(S09a, set_type{{"apple", "one", "orange", "three", "two"}})); \
|
||||
test(ex, make_expected(S09b, vec_node_type{{{resp3::type::set, 0UL, 0UL, {}}}})); \
|
||||
test(ex, make_expected(S03c, map_type{}));\
|
||||
test(ex, make_expected(S11a, node_type{resp3::type::doublean, 1UL, 0UL, {"1.23"}}));\
|
||||
test(ex, make_expected(S11b, node_type{resp3::type::doublean, 1UL, 0UL, {"inf"}}));\
|
||||
test(ex, make_expected(S11c, node_type{resp3::type::doublean, 1UL, 0UL, {"-inf"}}));\
|
||||
test(ex, make_expected(S11d, double{1.23}));\
|
||||
test(ex, make_expected(S11e, double{0}, boost::redis::error::not_a_double));\
|
||||
test(ex, make_expected(S12a, node_type{resp3::type::blob_error, 1UL, 0UL, {"SYNTAX invalid syntax"}}, boost::redis::error::resp3_blob_error));\
|
||||
test(ex, make_expected(S12b, node_type{resp3::type::blob_error, 1UL, 0UL, {}}, boost::redis::error::resp3_blob_error));\
|
||||
test(ex, make_expected(S12c, boost::redis::ignore, boost::redis::error::resp3_blob_error));\
|
||||
test(ex, make_expected(S13a, node_type{resp3::type::verbatim_string, 1UL, 0UL, {"txt:Some string"}}));\
|
||||
test(ex, make_expected(S13b, node_type{resp3::type::verbatim_string, 1UL, 0UL, {}}));\
|
||||
test(ex, make_expected(S14a, node_type{resp3::type::big_number, 1UL, 0UL, {"3492890328409238509324850943850943825024385"}}));\
|
||||
test(ex, make_expected(S14b, int{}, boost::redis::error::empty_field));\
|
||||
test(ex, make_expected(S15a, std::optional<std::string>{"OK"}));\
|
||||
test(ex, make_expected(S15a, std::string{"OK"}));\
|
||||
test(ex, make_expected(S15b, std::optional<std::string>{""}));\
|
||||
test(ex, make_expected(S15b, std::string{""}));\
|
||||
test(ex, make_expected(S16a, int{}, boost::redis::error::invalid_data_type));\
|
||||
test(ex, make_expected(S05d, int{11}, boost::redis::error::not_a_number));\
|
||||
test(ex, make_expected(S11a, node_type{{resp3::type::doublean, 1UL, 0UL, {"1.23"}}}));\
|
||||
test(ex, make_expected(S11b, node_type{{resp3::type::doublean, 1UL, 0UL, {"inf"}}}));\
|
||||
test(ex, make_expected(S11c, node_type{{resp3::type::doublean, 1UL, 0UL, {"-inf"}}}));\
|
||||
test(ex, make_expected(S11d, result<double>{{1.23}}));\
|
||||
test(ex, make_expected(S11e, result<double>{{0}}, boost::redis::error::not_a_double));\
|
||||
test(ex, make_expected(S13a, node_type{{resp3::type::verbatim_string, 1UL, 0UL, {"txt:Some string"}}}));\
|
||||
test(ex, make_expected(S13b, node_type{{resp3::type::verbatim_string, 1UL, 0UL, {}}}));\
|
||||
test(ex, make_expected(S14a, node_type{{resp3::type::big_number, 1UL, 0UL, {"3492890328409238509324850943850943825024385"}}}));\
|
||||
test(ex, make_expected(S14b, result<int>{}, boost::redis::error::empty_field));\
|
||||
test(ex, make_expected(S15a, result<std::optional<std::string>>{{"OK"}}));\
|
||||
test(ex, make_expected(S15a, result<std::string>{{"OK"}}));\
|
||||
test(ex, make_expected(S15b, result<std::optional<std::string>>{""}));\
|
||||
test(ex, make_expected(S15b, result<std::string>{{""}}));\
|
||||
test(ex, make_expected(S16a, result<int>{}, boost::redis::error::invalid_data_type));\
|
||||
test(ex, make_expected(S05d, result<int>{11}, boost::redis::error::not_a_number));\
|
||||
test(ex, make_expected(S03d, map_type{}, boost::redis::error::not_a_number));\
|
||||
test(ex, make_expected(S02d, std::string{}, boost::redis::error::not_a_number));\
|
||||
test(ex, make_expected(S17a, std::string{}, boost::redis::error::not_a_number));\
|
||||
test(ex, make_expected(S05e, int{}, boost::redis::error::empty_field));\
|
||||
test(ex, make_expected(S01d, std::optional<bool>{}, boost::redis::error::empty_field));\
|
||||
test(ex, make_expected(S11f, std::string{}, boost::redis::error::empty_field));\
|
||||
test(ex, make_expected(S17b, node_type{resp3::type::blob_string, 1UL, 0UL, {"hh"}}));\
|
||||
test(ex, make_expected(S18c, node_type{resp3::type::blob_string, 1UL, 0UL, {"hhaa\aaaa\raaaaa\r\naaaaaaaaaa"}}));\
|
||||
test(ex, make_expected(S18d, node_type{resp3::type::blob_string, 1UL, 0UL, {}}));\
|
||||
test(ex, make_expected(make_blob_string(blob), node_type{resp3::type::blob_string, 1UL, 0UL, {blob}}));\
|
||||
test(ex, make_expected(S02d, result<std::string>{}, boost::redis::error::not_a_number));\
|
||||
test(ex, make_expected(S17a, result<std::string>{}, boost::redis::error::not_a_number));\
|
||||
test(ex, make_expected(S05e, result<int>{}, boost::redis::error::empty_field));\
|
||||
test(ex, make_expected(S01d, result<std::optional<bool>>{}, boost::redis::error::empty_field));\
|
||||
test(ex, make_expected(S11f, result<std::string>{}, boost::redis::error::empty_field));\
|
||||
test(ex, make_expected(S17b, node_type{{resp3::type::blob_string, 1UL, 0UL, {"hh"}}}));\
|
||||
test(ex, make_expected(S18c, node_type{{resp3::type::blob_string, 1UL, 0UL, {"hhaa\aaaa\raaaaa\r\naaaaaaaaaa"}}}));\
|
||||
test(ex, make_expected(S18d, node_type{{resp3::type::blob_string, 1UL, 0UL, {}}}));\
|
||||
test(ex, make_expected(make_blob_string(blob), node_type{{resp3::type::blob_string, 1UL, 0UL, {blob}}}));\
|
||||
test(ex, make_expected(S04a, result<std::vector<int>>{{11}})); \
|
||||
test(ex, make_expected(S04d, result<response<std::unordered_set<std::string>>>{response<std::unordered_set<std::string>>{{set_e1c}}})); \
|
||||
test(ex, make_expected(S04c, result<response<std::map<std::string, std::string>>>{response<std::map<std::string, std::string>>{{map_expected_1b}}}));\
|
||||
test(ex, make_expected(S03b, map_e1l));\
|
||||
test(ex, make_expected(S06a, result<int>{0}, {}, resp3::type::null)); \
|
||||
test(ex, make_expected(S06a, map_type{}, {}, resp3::type::null));\
|
||||
test(ex, make_expected(S06a, array_type{}, {}, resp3::type::null));\
|
||||
test(ex, make_expected(S06a, result<std::list<int>>{}, {}, resp3::type::null));\
|
||||
test(ex, make_expected(S06a, result<std::vector<int>>{}, {}, resp3::type::null));\
|
||||
test(ex, make_expected(S10a, result<ignore_t>{}, boost::redis::error::resp3_simple_error)); \
|
||||
test(ex, make_expected(S10a, node_type{{resp3::type::simple_error, 1UL, 0UL, {"Error"}}}, {}, resp3::type::simple_error)); \
|
||||
test(ex, make_expected(S10b, node_type{{resp3::type::simple_error, 1UL, 0UL, {""}}}, {}, resp3::type::simple_error)); \
|
||||
test(ex, make_expected(S12a, node_type{{resp3::type::blob_error, 1UL, 0UL, {"SYNTAX invalid syntax"}}}, {}, resp3::type::blob_error));\
|
||||
test(ex, make_expected(S12b, node_type{{resp3::type::blob_error, 1UL, 0UL, {}}}, {}, resp3::type::blob_error));\
|
||||
test(ex, make_expected(S12c, result<ignore_t>{}, boost::redis::error::resp3_blob_error));\
|
||||
|
||||
BOOST_AUTO_TEST_CASE(parser)
|
||||
{
|
||||
@@ -508,7 +531,7 @@ BOOST_AUTO_TEST_CASE(ignore_adapter_simple_error)
|
||||
|
||||
test_stream ts {ioc};
|
||||
ts.append(S10a);
|
||||
resp3::read(ts, net::dynamic_buffer(rbuffer), adapt2(), ec);
|
||||
resp3::read(ts, net::dynamic_buffer(rbuffer), adapt2(ignore), ec);
|
||||
BOOST_CHECK_EQUAL(ec, boost::redis::error::resp3_simple_error);
|
||||
BOOST_TEST(!rbuffer.empty());
|
||||
}
|
||||
@@ -521,7 +544,7 @@ BOOST_AUTO_TEST_CASE(ignore_adapter_blob_error)
|
||||
|
||||
test_stream ts {ioc};
|
||||
ts.append(S12a);
|
||||
resp3::read(ts, net::dynamic_buffer(rbuffer), adapt2(), ec);
|
||||
resp3::read(ts, net::dynamic_buffer(rbuffer), adapt2(ignore), ec);
|
||||
BOOST_CHECK_EQUAL(ec, boost::redis::error::resp3_blob_error);
|
||||
BOOST_TEST(!rbuffer.empty());
|
||||
}
|
||||
@@ -534,7 +557,7 @@ BOOST_AUTO_TEST_CASE(ignore_adapter_no_error)
|
||||
|
||||
test_stream ts {ioc};
|
||||
ts.append(S05b);
|
||||
resp3::read(ts, net::dynamic_buffer(rbuffer), adapt2(), ec);
|
||||
resp3::read(ts, net::dynamic_buffer(rbuffer), adapt2(ignore), ec);
|
||||
BOOST_TEST(!ec);
|
||||
BOOST_TEST(rbuffer.empty());
|
||||
}
|
||||
@@ -635,18 +658,16 @@ BOOST_AUTO_TEST_CASE(adapter)
|
||||
|
||||
boost::system::error_code ec;
|
||||
|
||||
std::string a;
|
||||
int b;
|
||||
auto resp = std::tie(a, b, std::ignore);
|
||||
response<std::string, int, ignore_t> resp;
|
||||
|
||||
auto f = boost_redis_adapt(resp);
|
||||
f(0, resp3::node<std::string_view>{type::simple_string, 1, 0, "Hello"}, ec);
|
||||
f(1, resp3::node<std::string_view>{type::number, 1, 0, "42"}, ec);
|
||||
|
||||
BOOST_CHECK_EQUAL(a, "Hello");
|
||||
BOOST_CHECK_EQUAL(std::get<0>(resp).value(), "Hello");
|
||||
BOOST_TEST(!ec);
|
||||
|
||||
BOOST_CHECK_EQUAL(b, 42);
|
||||
BOOST_CHECK_EQUAL(std::get<1>(resp).value(), 42);
|
||||
BOOST_TEST(!ec);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user