diff --git a/INSTALL b/INSTALL index 191bde46..175ec4b4 100644 --- a/INSTALL +++ b/INSTALL @@ -1,7 +1 @@ -CC=/opt/gcc-10.2.0/bin/gcc-10.2.0 CXX=/opt/gcc-10.2.0/bin/g++-10.2.0 CXXFLAGS="-std=c++20 -fcoroutines -g -Wall -Wno-subobject-linkage -Werror" ./configure --with-boost=/opt/boost_1_78_0 --with-boost-libdir=/opt/boost_1_78_0/lib - -CPPFLAGS="-std=c++20 -fcoroutines -g -Wall -Wno-subobject-linkage -Werror" -BOOST_LDFLAGS = -L/opt/boost_1_78_0/lib -BOOST_CPPFLAGS = -I/opt/boost_1_78_0/include -export CPPFLAGS="$(BOOST_CPPFLAGS) $(CPPFLAGS)" -export LDFLAGS="$(BOOST_LDFLAGS)" +See file:///tmp/aedis/html/installation.html diff --git a/Makefile.am b/Makefile.am index a3d63769..8a39e11a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,102 +1,62 @@ -ACLOCAL_AMFLAGS = -I m4 AUTOMAKE_OPTIONS = subdir-objects +ACLOCAL_AMFLAGS = -I m4 AM_COLOR_TESTS = always DISTCHECK_CONFIGURE_FLAGS = CPPFLAGS="$(BOOST_CPPFLAGS) $(CPPFLAGS)" LDFLAGS="$(BOOST_LDFLAGS)" -MY_CPPFLAGS = -MY_CPPFLAGS += $(BOOST_CPPFLAGS) -MY_CPPFLAGS += -I$(top_srcdir)/ +AM_CPPFLAGS = +AM_CPPFLAGS += $(BOOST_CPPFLAGS) -MY_LDADD = -MY_LDADD += -lpthread +AM_LDFLAGS = +AM_LDFLAGS += -pthread check_PROGRAMS = check_PROGRAMS += intro -intro_SOURCES = $(top_srcdir)/examples/intro.cpp -intro_CPPFLAGS = $(MY_CPPFLAGS) -intro_LDADD = $(MY_LDADD) - check_PROGRAMS += sets -sets_SOURCES = $(top_srcdir)/examples/sets.cpp -sets_CPPFLAGS = $(MY_CPPFLAGS) -sets_LDADD = $(MY_LDADD) - check_PROGRAMS += hashes -hashes_SOURCES = $(top_srcdir)/examples/hashes.cpp -hashes_CPPFLAGS = $(MY_CPPFLAGS) -hashes_LDADD = $(MY_LDADD) - check_PROGRAMS += serialization -serialization_SOURCES = $(top_srcdir)/examples/serialization.cpp -serialization_CPPFLAGS = $(MY_CPPFLAGS) -serialization_LDADD = $(MY_LDADD) - check_PROGRAMS += nested_response -nested_response_SOURCES = $(top_srcdir)/examples/nested_response.cpp -nested_response_CPPFLAGS = $(MY_CPPFLAGS) -nested_response_LDADD = $(MY_LDADD) - check_PROGRAMS += lists -lists_SOURCES = $(top_srcdir)/examples/lists.cpp -lists_CPPFLAGS = $(MY_CPPFLAGS) -lists_LDADD = $(MY_LDADD) - check_PROGRAMS += key_expiration -key_expiration_SOURCES = $(top_srcdir)/examples/key_expiration.cpp -key_expiration_CPPFLAGS = $(MY_CPPFLAGS) -key_expiration_LDADD = $(MY_LDADD) - check_PROGRAMS += response_adapter -response_adapter_SOURCES = $(top_srcdir)/examples/response_adapter.cpp -response_adapter_CPPFLAGS = $(MY_CPPFLAGS) -response_adapter_LDADD = $(MY_LDADD) - check_PROGRAMS += sync -sync_SOURCES = $(top_srcdir)/examples/sync.cpp -sync_CPPFLAGS = $(MY_CPPFLAGS) -sync_LDADD = $(MY_LDADD) - check_PROGRAMS += redis_client -redis_client_SOURCES = $(top_srcdir)/examples/redis_client.cpp -redis_client_CPPFLAGS = $(MY_CPPFLAGS) -redis_client_LDADD = $(MY_LDADD) - -check_PROGRAMS += commands -commands_SOURCES = $(top_srcdir)/tools/commands.cpp -commands_CPPFLAGS = $(MY_CPPFLAGS) -commands_LDADD = $(MY_LDADD) - check_PROGRAMS += test_offline -test_offline_SOURCES = $(top_srcdir)/tests/offline.cpp -test_offline_CPPFLAGS = $(MY_CPPFLAGS) -test_offline_LDADD = $(MY_LDADD) - check_PROGRAMS += test_online -test_online_SOURCES = $(top_srcdir)/tests/online.cpp -test_online_CPPFLAGS = $(MY_CPPFLAGS) -test_online_LDADD = $(MY_LDADD) EXTRA_PROGRAMS = EXTRA_PROGRAMS += subscriber -subscriber_SOURCES = $(top_srcdir)/examples/subscriber.cpp -subscriber_CPPFLAGS = $(MY_CPPFLAGS) -subscriber_LDADD = $(MY_LDADD) - EXTRA_PROGRAMS += echo_server -echo_server_SOURCES = $(top_srcdir)/examples/echo_server.cpp -echo_server_CPPFLAGS = $(MY_CPPFLAGS) -echo_server_LDADD = $(MY_LDADD) - EXTRA_PROGRAMS += chat_room +EXTRA_PROGRAMS += commands + +.PHONY: all +all: $(check_PROGRAMS) $(EXTRA_PROGRAMS) + +intro_SOURCES = $(top_srcdir)/examples/intro.cpp +sets_SOURCES = $(top_srcdir)/examples/sets.cpp +hashes_SOURCES = $(top_srcdir)/examples/hashes.cpp +serialization_SOURCES = $(top_srcdir)/examples/serialization.cpp +nested_response_SOURCES = $(top_srcdir)/examples/nested_response.cpp +lists_SOURCES = $(top_srcdir)/examples/lists.cpp +key_expiration_SOURCES = $(top_srcdir)/examples/key_expiration.cpp +response_adapter_SOURCES = $(top_srcdir)/examples/response_adapter.cpp +sync_SOURCES = $(top_srcdir)/examples/sync.cpp +redis_client_SOURCES = $(top_srcdir)/examples/redis_client.cpp +commands_SOURCES = $(top_srcdir)/tools/commands.cpp +test_offline_SOURCES = $(top_srcdir)/tests/offline.cpp +test_online_SOURCES = $(top_srcdir)/tests/online.cpp +subscriber_SOURCES = $(top_srcdir)/examples/subscriber.cpp +echo_server_SOURCES = $(top_srcdir)/examples/echo_server.cpp chat_room_SOURCES = $(top_srcdir)/examples/chat_room.cpp -chat_room_CPPFLAGS = $(MY_CPPFLAGS) -chat_room_LDADD = $(MY_LDADD) + +nobase_nodist_include_HEADERS =\ + $(top_srcdir)/aedis/config.hpp nobase_include_HEADERS =\ $(top_srcdir)/aedis/src.hpp\ - $(top_srcdir)/aedis/net.hpp\ - $(top_srcdir)/aedis/command.hpp\ + $(top_srcdir)/aedis/redis/command.hpp\ + $(top_srcdir)/aedis/sentinel/command.hpp\ $(top_srcdir)/aedis/aedis.hpp\ $(top_srcdir)/aedis/resp3/adapter/detail/adapters.hpp\ $(top_srcdir)/aedis/resp3/adapter/error.hpp\ @@ -104,26 +64,24 @@ nobase_include_HEADERS =\ $(top_srcdir)/aedis/resp3/detail/composer.hpp\ $(top_srcdir)/aedis/resp3/detail/read_ops.hpp\ $(top_srcdir)/aedis/resp3/detail/parser.hpp\ - $(top_srcdir)/aedis/resp3/client.hpp\ $(top_srcdir)/aedis/resp3/serializer.hpp\ $(top_srcdir)/aedis/resp3/response_traits.hpp\ $(top_srcdir)/aedis/resp3/node.hpp\ $(top_srcdir)/aedis/resp3/error.hpp\ $(top_srcdir)/aedis/resp3/type.hpp\ $(top_srcdir)/aedis/resp3/read.hpp\ - $(top_srcdir)/aedis/impl/command.ipp\ + $(top_srcdir)/aedis/redis/impl/command.ipp\ + $(top_srcdir)/aedis/sentinel/impl/command.ipp\ $(top_srcdir)/aedis/resp3/detail/impl/parser.ipp\ $(top_srcdir)/aedis/resp3/impl/type.ipp\ - $(top_srcdir)/aedis/resp3/impl/client.ipp\ - $(top_srcdir)/aedis/resp3/impl/node.ipp + $(top_srcdir)/aedis/resp3/impl/node.ipp\ + $(top_srcdir)/aedis/redis/experimental/client.hpp\ + $(top_srcdir)/aedis/redis/experimental/impl/client.ipp nobase_noinst_HEADERS =\ - $(top_srcdir)/examples/utils.ipp\ - $(top_srcdir)/examples/lib/responses.ipp\ + $(top_srcdir)/examples/lib/net_utils.hpp\ $(top_srcdir)/examples/lib/responses.hpp\ $(top_srcdir)/examples/lib/user_session.hpp\ - $(top_srcdir)/examples/lib/user_session.ipp\ - $(top_srcdir)/examples/src.hpp\ $(top_srcdir)/tests/check.hpp\ $(top_srcdir)/tests/test_stream.hpp @@ -138,23 +96,9 @@ EXTRA_DIST += $(top_srcdir)/doc/htmlfooter.html EXTRA_DIST += $(top_srcdir)/doc/htmlheader.html CLEANFILES = -#CLEANFILES += Makefile.dep .PHONY: doc doc: rm -rf /tmp/aedis doxygen doc/Doxyfile -.PHONY: deb -deb: dist - export CPPFLAGS="$(BOOST_CPPFLAGS) $(CPPFLAGS)" &&\ - export LDFLAGS="$(BOOST_LDFLAGS)" &&\ - rm -rf tmp &&\ - mkdir tmp &&\ - mv $(distdir).tar.gz $(distdir)-1.tar.gz &&\ - mv $(distdir)-1.tar.gz tmp &&\ - cd tmp &&\ - ln $(distdir)-1.tar.gz $(PACKAGE)_$(VERSION).orig.tar.gz &&\ - tar -xvvzf $(distdir)-1.tar.gz &&\ - cd $(distdir)/debian; debuild --no-sign -j1 - diff --git a/aedis/aedis.hpp b/aedis/aedis.hpp index 037d34b1..1342872f 100644 --- a/aedis/aedis.hpp +++ b/aedis/aedis.hpp @@ -7,46 +7,44 @@ #pragma once -#include -#include +#include +#include +#include #include #include #include #include #include #include -#include +#include /** \mainpage - - Introduction - - - \subpage overview - - \subpage installation - - \subpage examples - - Reference - - - \subpage enums - - \subpage classes - - \subpage functions - - \subpage operators + * + * \b Overview + * + * Aedis is low-level redis client built on top of Boost.Asio that + * implements communication with a Redis server over its native + * protocol RESP3. It has first-class support for STL containers and + * C++ built in types. You will be able to implement your own redis + * client or use a general purpose provided by the library. For more + * information about Redis see https://redis.io/ + * + * \b Using \b Aedis + * + * - \subpage installation + * - \subpage examples + * + * \b Reference + * + * - \subpage enums + * - \subpage classes + * - \subpage functions + * - \subpage operators */ //--------------------------------------------------------- // Pages -/** \page overview Overview - * - * Aedis provides low-level communication with a Redis server over - * its native protocol RESP3. Some of its featues are - * - * - First class support to STL containers and C++ built-in types. - * - Support for pipelining, transactions and TLS. - * - Serialization and de-serialization of your own types. - * - First class support to async programming through ASIO. - */ - /** \page examples Examples * \b Basics: Focuses on small code snippets that show basic usage of @@ -67,7 +65,8 @@ Shows how to read responses to commands that cannot be translated in a C++ built-in type like std::string or STL - containers. + containers, for example all commands contained in a transaction + will be nested by Redis in a single response. - subscriber.cpp @@ -77,11 +76,6 @@ Shows hot to use the Aedis synchronous api. - - redis_client.cpp - - Shows how to use and experimental high level redis client that - keeps a long lasting connections to a redis server. - \b STL \b Containers: Many of the Redis data structures can be directly translated in to STL containers, below you will find some example code. For a list of Redis data types see @@ -119,6 +113,12 @@ servers that interact with users and Redis asynchronously over long lasting connections using a higher level API. + - redis_client.cpp + + Shows how to use and experimental high level redis client that + keeps a long lasting connections to a redis server. This is the + starting point for the next examples. + - echo_server.cpp Shows the basic principles behind asynchronous communication @@ -133,18 +133,84 @@ /** \page installation Installation * - * This library is header only. To install it run + * \section Requirements * - * ```cpp + * To use Aedis you will need + * + * Required + * + * - \b C++20 with \b coroutine support. + * - \b Boost \b 1.78 or greater. + * - \b Redis \b server. + * - \b Bash to configure that package for installation. + * + * Optional + * + * - \b Redis \b Sentinel \b server: used in some examples.. + * - \b Redis \b client: used in some examples. + * - \b Make to build the examples and tests. + * + * \section Installing + * + * Download + * + * Get the latest release and follow the steps + * + * ```bash + * # Download the libray on github. + * $ wget github-link + * + * # Uncompress the tarball and cd into the dir + * $ tar -xzvf aedis-1.0.0.tar.gz && cd aedis-1.0.0 + * + * # Run configure with appropriate C++ flags and your boost installation, for example + * $ CXXFLAGS="-std=c++20 -fcoroutines -g -Wall -Wno-subobject-linkage"\ + * ./configure --prefix=/opt/aedis-1.0.0 --with-boost=/opt/boost_1_78_0 --with-boost-libdir=/opt/boost_1_78_0/lib + * + * ``` + * + * Install + * + * ```bash + * # Optional: Build aedis examples. + * $ make examples + * + * # Optional: Test aedis in your machine. + * $ make check + * + * # Install the aedis. * $ sudo make install * ``` * - * or copy the include folder to the location you want. You will - * also need to include the following header in one of your source - * files e.g. `aedis.cpp` + * \section using Using Aedis + * + * This library in not header-only. You have to include the following + * header * * ```cpp - * #include + * #include + * ``` + * + * in exactly one source file in your applications. + * + * \section Developers + * + * Aedis uses Autotools for its build system. To generate the build + * system run + * + * ```bash + * $ autoreconf -i + * ``` + * + * After that you will have a config in the project dir that you can + * run as explained above, for example, to use a compiler other that + * the system compiler use + * + * ```bash + * CC=/opt/gcc-10.2.0/bin/gcc-10.2.0\ + * CXX=/opt/gcc-10.2.0/bin/g++-10.2.0\ + * CXXFLAGS="-std=c++20 -fcoroutines -g -Wall -Wno-subobject-linkage -Werror"\ + * ./configure ... * ``` */ diff --git a/aedis/net.hpp b/aedis/config.hpp.in similarity index 89% rename from aedis/net.hpp rename to aedis/config.hpp.in index 1b7be285..4c053711 100644 --- a/aedis/net.hpp +++ b/aedis/config.hpp.in @@ -7,6 +7,8 @@ #pragma once +#define AEDIS_VERSION @PACKAGE_VERSION@ + #include namespace aedis { diff --git a/aedis/command.hpp b/aedis/redis/command.hpp similarity index 96% rename from aedis/command.hpp rename to aedis/redis/command.hpp index f6bd990c..265a1237 100644 --- a/aedis/command.hpp +++ b/aedis/redis/command.hpp @@ -8,8 +8,12 @@ #pragma once #include +#include + +#include namespace aedis { +namespace redis { /** \brief Redis commands. * \ingroup enums @@ -451,4 +455,16 @@ std::ostream& operator<<(std::ostream& os, command c); */ bool has_push_response(command cmd); +/** \brief Creates a serializer for a \c std::string. + * \ingroup functions + * \param storage The string. + */ +template +resp3::serializer +make_serializer(std::basic_string& storage) +{ + return resp3::serializer, command>(storage); +} + +} // redis } // aedis diff --git a/aedis/resp3/client.hpp b/aedis/redis/experimental/client.hpp similarity index 89% rename from aedis/resp3/client.hpp rename to aedis/redis/experimental/client.hpp index 2221f5ef..50c9e5d0 100644 --- a/aedis/resp3/client.hpp +++ b/aedis/redis/experimental/client.hpp @@ -11,6 +11,7 @@ #include #include +#include namespace aedis { namespace resp3 { @@ -25,10 +26,10 @@ namespace experimental { class client : public std::enable_shared_from_this { public: /// The response adapter type. - using adapter_type = std::function; + using adapter_type = std::function; /// The type of the message callback. - using on_message_type = std::function; + using on_message_type = std::function; private: using tcp_socket = net::use_awaitable_t<>::as_default_on_t; @@ -46,7 +47,7 @@ private: std::string requests_; // The commands contained in the requests. - std::queue commands_; + std::queue commands_; // Info about the requests. std::queue req_info_; @@ -103,7 +104,7 @@ public: * \sa serializer.hpp */ template - void send(command cmd, Ts const&... args); + void send(redis::command cmd, Ts const&... args); /// Sets the response adapter. void set_adapter(adapter_type adapter); @@ -113,11 +114,11 @@ public: }; template -void client::send(command cmd, Ts const&... args) +void client::send(redis::command cmd, Ts const&... args) { auto const can_write = prepare_next(); - auto sr = make_serializer(requests_); + auto sr = redis::make_serializer(requests_); auto const before = std::size(requests_); sr.push(cmd, args...); auto const after = std::size(requests_); diff --git a/aedis/resp3/impl/client.ipp b/aedis/redis/experimental/impl/client.ipp similarity index 92% rename from aedis/resp3/impl/client.ipp rename to aedis/redis/experimental/impl/client.ipp index 7bd621ec..e0c67411 100644 --- a/aedis/resp3/impl/client.ipp +++ b/aedis/redis/experimental/impl/client.ipp @@ -7,7 +7,7 @@ #pragma once -#include +#include #include @@ -40,11 +40,11 @@ net::awaitable client::reader() auto const t = co_await async_read_type(socket_, net::dynamic_buffer(buffer)); if (t == type::push) { auto adapter = [this](type t, std::size_t aggregate_size, std::size_t depth, char const* data, std::size_t size, std::error_code& ec) - {adapter_(command::unknown, t, aggregate_size, depth, data, size, ec);}; + {adapter_(redis::command::unknown, t, aggregate_size, depth, data, size, ec);}; boost::system::error_code ec; co_await resp3::async_read(socket_, net::dynamic_buffer(buffer), adapter, net::redirect_error(net::use_awaitable, ec)); - on_msg_(ec, command::unknown); + on_msg_(ec, redis::command::unknown); } else { auto adapter = [this](type t, std::size_t aggregate_size, std::size_t depth, char const* data, std::size_t size, std::error_code& ec) {adapter_(commands_.front(), t, aggregate_size, depth, data, size, ec);}; @@ -89,13 +89,13 @@ net::awaitable client::writer() net::awaitable client::say_hello() { std::string request; - auto sr = make_serializer(request); - sr.push(command::hello, 3); + auto sr = redis::make_serializer(request); + sr.push(redis::command::hello, 3); co_await net::async_write(socket_, net::buffer(request)); std::string buffer; auto adapter = [this](type t, std::size_t aggregate_size, std::size_t depth, char const* data, std::size_t size, std::error_code& ec) - {adapter_(command::hello, t, aggregate_size, depth, data, size, ec);}; + {adapter_(redis::command::hello, t, aggregate_size, depth, data, size, ec);}; co_await resp3::async_read(socket_, net::dynamic_buffer(buffer), adapter); } diff --git a/aedis/impl/command.ipp b/aedis/redis/impl/command.ipp similarity index 96% rename from aedis/impl/command.ipp rename to aedis/redis/impl/command.ipp index 957b72bf..4a286f21 100644 --- a/aedis/impl/command.ipp +++ b/aedis/redis/impl/command.ipp @@ -1,15 +1,14 @@ -/* Copyright (c) 2019 - 2021 Marcelo Zimbres Silva (mzimbres at gmail dot com) +/* Copyright (c) 2019 Marcelo Zimbres Silva (mzimbres@gmail.com) * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -#include - -#include +#include namespace aedis { +namespace redis { char const* to_string(command c) { @@ -242,4 +241,5 @@ bool has_push_response(command cmd) } } +} // redis } // aedis diff --git a/aedis/resp3/adapter/detail/adapters.hpp b/aedis/resp3/adapter/detail/adapters.hpp index d9af555e..1ada412a 100644 --- a/aedis/resp3/adapter/detail/adapters.hpp +++ b/aedis/resp3/adapter/detail/adapters.hpp @@ -1,4 +1,4 @@ -/* Copyright (c) 2019 - 2021 Marcelo Zimbres Silva (mzimbres at gmail dot com) +/* Copyright (c) 2019 Marcelo Zimbres Silva (mzimbres@gmail.com) * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -16,7 +16,6 @@ #include #include -#include #include #include #include @@ -85,7 +84,7 @@ private: Container* result_; public: - general(Container& c = nullptr): result_(&c) {} + general(Container* c = nullptr): result_(c) {} /** @brief Function called by the parser when new data has been processed. * @@ -122,7 +121,7 @@ private: Node* result_; public: - adapter_node(Node& t) : result_(&t) {} + adapter_node(Node* t = nullptr) : result_(t) {} void operator()( @@ -147,7 +146,7 @@ private: T* result_; public: - simple(T& t) : result_(&t) {} + simple(T* t = nullptr) : result_(t) {} void operator()( @@ -166,12 +165,6 @@ public: } assert(aggregate_size == 1); - - if (depth != 0) { - ec == adapter::error::nested_unsupported; - return; - } - from_string(*result_, data, data_size, ec); } }; @@ -182,7 +175,7 @@ private: std::optional* result_; public: - simple_optional(std::optional& o) : result_(&o) {} + simple_optional(std::optional* o = nullptr) : result_(o) {} void operator()( @@ -226,7 +219,7 @@ private: Container* result_; public: - vector(Container& v) : result_{&v} {} + vector(Container* v = nullptr) : result_{v} {} void operator()(type t, @@ -239,7 +232,7 @@ public: set_on_resp3_error(t, ec); if (is_aggregate(t)) { - if (depth != 0 || i_ != -1) { + if (i_ != -1) { ec == adapter::error::nested_unsupported; return; } @@ -248,11 +241,6 @@ public: result_->resize(m * aggregate_size); ++i_; } else { - if (depth != 1) { - ec == adapter::error::nested_unsupported; - return; - } - assert(aggregate_size == 1); from_string(result_->at(i_), data, data_size, ec); @@ -267,7 +255,7 @@ private: Container* result_; public: - list(Container& ref): result_(&ref) {} + list(Container* ref = nullptr): result_(ref) {} void operator()(type t, @@ -306,9 +294,9 @@ private: Container::iterator hint_; public: - set(Container& c) - : result_(&c) - , hint_(std::end(c)) + set(Container* c = nullptr) + : result_(c) + , hint_(std::end(*c)) {} void @@ -350,9 +338,9 @@ private: bool on_key_ = true; public: - map(Container& c) - : result_(&c) - , current_(std::end(c)) + map(Container* c = nullptr) + : result_(c) + , current_(std::end(*c)) {} void diff --git a/aedis/resp3/detail/composer.hpp b/aedis/resp3/detail/composer.hpp index b2fb10ea..15426ad1 100644 --- a/aedis/resp3/detail/composer.hpp +++ b/aedis/resp3/detail/composer.hpp @@ -11,8 +11,6 @@ #include #include -#include - namespace aedis { namespace resp3 { namespace detail { diff --git a/aedis/resp3/detail/read_ops.hpp b/aedis/resp3/detail/read_ops.hpp index 6d0ce7ca..1560d2c6 100644 --- a/aedis/resp3/detail/read_ops.hpp +++ b/aedis/resp3/detail/read_ops.hpp @@ -8,9 +8,9 @@ #pragma once #include -#include -#include +#include +#include #include namespace aedis { diff --git a/aedis/resp3/read.hpp b/aedis/resp3/read.hpp index b09a1c55..f305e8e0 100644 --- a/aedis/resp3/read.hpp +++ b/aedis/resp3/read.hpp @@ -7,7 +7,7 @@ #pragma once -#include +#include #include #include #include diff --git a/aedis/resp3/response_traits.hpp b/aedis/resp3/response_traits.hpp index 08cb17c4..81856c97 100644 --- a/aedis/resp3/response_traits.hpp +++ b/aedis/resp3/response_traits.hpp @@ -1,4 +1,4 @@ -/* Copyright (c) 2019 - 2021 Marcelo Zimbres Silva (mzimbres at gmail dot com) +/* Copyright (c) 2019 Marcelo Zimbres Silva (mzimbres@gmail.com) * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -7,7 +7,6 @@ #pragma once -#include #include #include @@ -21,7 +20,8 @@ namespace aedis { namespace resp3 { -/* \brief Traits class for response objects. +/** \brief Traits class for response objects. + * \ingroup classes */ template struct response_traits @@ -33,7 +33,7 @@ struct response_traits using adapter_type = adapter::detail::simple; /// Returns an adapter for the reponse r - static auto adapt(response_type& r) noexcept { return adapter_type{r}; } + static auto adapt(response_type& r) noexcept { return adapter_type{&r}; } }; template @@ -41,7 +41,7 @@ struct response_traits> { using response_type = std::optional; using adapter_type = adapter::detail::simple_optional; - static auto adapt(response_type& i) noexcept { return adapter_type{i}; } + static auto adapt(response_type& i) noexcept { return adapter_type{&i}; } }; template @@ -49,7 +49,7 @@ struct response_traits> { using response_type = std::vector; using adapter_type = adapter::detail::vector; - static auto adapt(response_type& v) noexcept { return adapter_type{v}; } + static auto adapt(response_type& v) noexcept { return adapter_type{&v}; } }; template <> @@ -57,7 +57,7 @@ struct response_traits { using response_type = node; using adapter_type = adapter::detail::adapter_node; - static auto adapt(response_type& v) noexcept { return adapter_type{v}; } + static auto adapt(response_type& v) noexcept { return adapter_type{&v}; } }; template @@ -65,7 +65,7 @@ struct response_traits> { using response_type = std::vector; using adapter_type = adapter::detail::general; - static auto adapt(response_type& v) noexcept { return adapter_type{v}; } + static auto adapt(response_type& v) noexcept { return adapter_type{&v}; } }; template @@ -73,7 +73,7 @@ struct response_traits> { using response_type = std::list; using adapter_type = adapter::detail::list; - static auto adapt(response_type& v) noexcept { return adapter_type{v}; } + static auto adapt(response_type& v) noexcept { return adapter_type{&v}; } }; template @@ -81,7 +81,7 @@ struct response_traits> { using response_type = std::deque; using adapter_type = adapter::detail::list; - static auto adapt(response_type& v) noexcept { return adapter_type{v}; } + static auto adapt(response_type& v) noexcept { return adapter_type{&v}; } }; template @@ -89,7 +89,7 @@ struct response_traits> { using response_type = std::set; using adapter_type = adapter::detail::set; - static auto adapt(response_type& s) noexcept { return adapter_type{s}; } + static auto adapt(response_type& s) noexcept { return adapter_type{&s}; } }; template @@ -97,7 +97,7 @@ struct response_traits> { using response_type = std::unordered_set; using adapter_type = adapter::detail::set; - static auto adapt(response_type& s) noexcept { return adapter_type{s}; } + static auto adapt(response_type& s) noexcept { return adapter_type{&s}; } }; template @@ -105,7 +105,7 @@ struct response_traits> { using response_type = std::map; using adapter_type = adapter::detail::map; - static auto adapt(response_type& s) noexcept { return adapter_type{s}; } + static auto adapt(response_type& s) noexcept { return adapter_type{&s}; } }; template @@ -113,7 +113,7 @@ struct response_traits> { using response_type = std::unordered_map; using adapter_type = adapter::detail::map; - static auto adapt(response_type& s) noexcept { return adapter_type{s}; } + static auto adapt(response_type& s) noexcept { return adapter_type{&s}; } }; template <> diff --git a/aedis/resp3/serializer.hpp b/aedis/resp3/serializer.hpp index db9ef78f..01397567 100644 --- a/aedis/resp3/serializer.hpp +++ b/aedis/resp3/serializer.hpp @@ -7,9 +7,6 @@ #pragma once -#include - -#include #include namespace aedis { @@ -159,15 +156,5 @@ public: } }; -/** \brief Creates a serializer for a \c std::string. - * \ingroup functions - * \param storage The string. - */ -template -auto make_serializer(std::string& storage) -{ - return serializer(storage); -} - } // resp3 } // aedis diff --git a/aedis/sentinel/command.hpp b/aedis/sentinel/command.hpp new file mode 100644 index 00000000..587a7205 --- /dev/null +++ b/aedis/sentinel/command.hpp @@ -0,0 +1,63 @@ +/* Copyright (c) 2019 Marcelo Zimbres Silva (mzimbres@gmail.com) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include + +namespace aedis { +namespace sentinel { + +/** \brief Sentinel commands. + * \ingroup enums + * + * For a full list of commands see https://redis.io/topics/sentinel + * + * \remark The list of commands below are read from Redis with the + * help of the command \c command. + */ +enum class command { + acl, + auth, + client, + command, + hello, + info, + ping, + psubscribe, + publish, + punsubscribe, + role, + sentinel, + shutdown, + subscribe, + unsubscribe, + unknown +}; + +/** \brief Converts a sentinel command to a string + * \ingroup functions + * + * \param c The command to convert. + */ +char const* to_string(command c); + +/** \brief Write the text for a sentinel command name to an output stream. + * \ingroup operators + * + * \param os Output stream. + * \param c Sentinel command + */ +std::ostream& operator<<(std::ostream& os, command c); + +/** \brief Returns true for sentinel commands with push response. + * \ingroup functions + */ +bool has_push_response(command cmd); + +} // sentinel +} // aedis diff --git a/aedis/sentinel/impl/command.ipp b/aedis/sentinel/impl/command.ipp new file mode 100644 index 00000000..11cc38c6 --- /dev/null +++ b/aedis/sentinel/impl/command.ipp @@ -0,0 +1,56 @@ +/* Copyright (c) 2019 Marcelo Zimbres Silva (mzimbres@gmail.com) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include + +namespace aedis { +namespace sentinel { + +char const* to_string(command c) +{ + static char const* table[] = { + "ACL", + "AUTH", + "CLIENT", + "COMMAND", + "HELLO", + "INFO", + "PING", + "PSUBSCRIBE", + "PUBLISH", + "PUNSUBSCRIBE", + "ROLE", + "SENTINEL", + "SHUTDOWN", + "SUBSCRIBE", + "UNSUBSCRIBE", + }; + + return table[static_cast(c)]; +} + +std::ostream& operator<<(std::ostream& os, command c) +{ + os << to_string(c); + return os; +} + +bool has_push_response(command cmd) +{ + switch (cmd) { + case command::subscribe: + case command::unsubscribe: + case command::psubscribe: + return true; + + default: + return false; + } +} + +} // sentinel +} // aedis diff --git a/aedis/src.hpp b/aedis/src.hpp index fa780cb2..b1fdad3b 100644 --- a/aedis/src.hpp +++ b/aedis/src.hpp @@ -7,8 +7,9 @@ // Include this file in no more than one source file in your application. -#include +#include +#include #include #include -#include +#include #include diff --git a/configure.ac b/configure.ac index e53c3ae9..f1d02e2f 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ AC_PREREQ([2.69]) -AC_INIT([aedis], [1.0.0], [mzimbres@gmail.com]) +AC_INIT([aedis], [0.1.0], [mzimbres@gmail.com]) AC_CONFIG_MACRO_DIR([m4]) #AC_CONFIG_SRCDIR([src/aedis.cpp]) AC_CONFIG_HEADERS([config.h]) @@ -18,5 +18,5 @@ AC_CHECK_HEADER_STDBOOL AC_TYPE_UINT64_T AC_CHECK_TYPES([ptrdiff_t]) -AC_CONFIG_FILES([Makefile]) +AC_CONFIG_FILES([Makefile aedis/config.hpp]) AC_OUTPUT diff --git a/examples/chat_room.cpp b/examples/chat_room.cpp index a91d27fc..01c17a1d 100644 --- a/examples/chat_room.cpp +++ b/examples/chat_room.cpp @@ -8,15 +8,14 @@ #include #include -#include #include +#include #include "lib/user_session.hpp" #include "lib/responses.hpp" -#include "src.hpp" namespace net = aedis::net; -using aedis::command; +using aedis::redis::command; using aedis::user_session; using aedis::user_session_base; using aedis::resp3::experimental::client; diff --git a/examples/echo_server.cpp b/examples/echo_server.cpp index 4a2d68a1..7f2fb1dd 100644 --- a/examples/echo_server.cpp +++ b/examples/echo_server.cpp @@ -8,15 +8,14 @@ #include #include -#include #include +#include #include "lib/user_session.hpp" #include "lib/responses.hpp" -#include "src.hpp" namespace net = aedis::net; -using aedis::command; +using aedis::redis::command; using aedis::user_session; using aedis::user_session_base; using aedis::resp3::experimental::client; diff --git a/examples/hashes.cpp b/examples/hashes.cpp index 6cea1cf4..9d5a5312 100644 --- a/examples/hashes.cpp +++ b/examples/hashes.cpp @@ -10,13 +10,14 @@ #include #include -#include #include -#include "utils.ipp" +#include + +#include "lib/net_utils.hpp" namespace resp3 = aedis::resp3; -using aedis::command; -using resp3::make_serializer; +using aedis::redis::command; +using aedis::redis::make_serializer; using resp3::adapt; namespace net = aedis::net; @@ -38,7 +39,7 @@ net::awaitable containers() // Creates and sends the request. std::string request; - auto sr = make_serializer(request); + auto sr = make_serializer(request); sr.push(command::hello, 3); sr.push(command::flushall); sr.push_range(command::hset, "key", std::cbegin(map), std::cend(map)); diff --git a/examples/intro.cpp b/examples/intro.cpp index 8444a50c..7c0b5eb7 100644 --- a/examples/intro.cpp +++ b/examples/intro.cpp @@ -6,14 +6,15 @@ */ #include -#include -#include -#include "utils.ipp" +#include +#include + +#include "lib/net_utils.hpp" namespace resp3 = aedis::resp3; -using aedis::command; -using resp3::make_serializer; +using aedis::redis::command; +using aedis::redis::make_serializer; using resp3::adapt; namespace net = aedis::net; @@ -38,7 +39,7 @@ net::awaitable ping() // Creates and sends the request. std::string request; - auto sr = make_serializer(request); + auto sr = make_serializer(request); sr.push(command::hello, 3); sr.push(command::flushall); sr.push(command::ping); diff --git a/examples/key_expiration.cpp b/examples/key_expiration.cpp index da21f486..a0f158cc 100644 --- a/examples/key_expiration.cpp +++ b/examples/key_expiration.cpp @@ -11,11 +11,12 @@ #include #include -#include "utils.ipp" + +#include "lib/net_utils.hpp" namespace resp3 = aedis::resp3; -using aedis::command; -using resp3::make_serializer; +using aedis::redis::command; +using aedis::redis::make_serializer; using resp3::adapt; using resp3::node; @@ -33,7 +34,7 @@ net::awaitable key_expiration() // Creates and sends the first request. std::string request; - auto sr = make_serializer(request); + auto sr = make_serializer(request); sr.push(command::hello, 3); sr.push(command::flushall); sr.push(command::set, "key", "Some payload", "EX", "2"); diff --git a/examples/utils.ipp b/examples/lib/net_utils.hpp similarity index 97% rename from examples/utils.ipp rename to examples/lib/net_utils.hpp index c5aac3cb..9919391d 100644 --- a/examples/utils.ipp +++ b/examples/lib/net_utils.hpp @@ -5,7 +5,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -#include +#include using tcp_socket = aedis::net::use_awaitable_t<>::as_default_on_t; using tcp_resolver = aedis::net::use_awaitable_t<>::as_default_on_t; diff --git a/examples/lib/responses.hpp b/examples/lib/responses.hpp index 5733ce6d..d62a3692 100644 --- a/examples/lib/responses.hpp +++ b/examples/lib/responses.hpp @@ -10,7 +10,7 @@ #include namespace net = aedis::net; -using aedis::command; +using aedis::redis::command; using aedis::resp3::adapt; using aedis::resp3::response_traits; using aedis::resp3::type; @@ -31,15 +31,40 @@ private: response_traits>::adapter_type general_adapter_; public: - adapter_wrapper(responses& resps); + adapter_wrapper(responses& resps) + : number_adapter_{adapt(resps.number)} + , str_adapter_{adapt(resps.simple_string)} + , general_adapter_{adapt(resps.general)} + {} - void operator()( + void + operator()( command cmd, type t, std::size_t aggregate_size, std::size_t depth, char const* data, std::size_t size, - std::error_code& ec); + std::error_code& ec) + { + // Handles only the commands we are interested in the examples and + // ignores the rest. + switch (cmd) { + case command::quit: + case command::ping: + str_adapter_(t, aggregate_size, depth, data, size, ec); + return; + + case command::incr: + number_adapter_(t, aggregate_size, depth, data, size, ec); + return; + + case command::unknown: + general_adapter_(t, aggregate_size, depth, data, size, ec); + return; + + default: {} // Ignore. + } + } }; diff --git a/examples/lib/responses.ipp b/examples/lib/responses.ipp deleted file mode 100644 index 3e229b6d..00000000 --- a/examples/lib/responses.ipp +++ /dev/null @@ -1,49 +0,0 @@ -/* Copyright (c) 2019 Marcelo Zimbres Silva (mzimbres@gmail.com) - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -#include "responses.hpp" - -namespace net = aedis::net; -using aedis::command; -using aedis::resp3::adapt; -using aedis::resp3::response_traits; -using aedis::resp3::type; - -adapter_wrapper::adapter_wrapper(responses& resps) -: number_adapter_{adapt(resps.number)} -, str_adapter_{adapt(resps.simple_string)} -, general_adapter_{adapt(resps.general)} -{} - -void adapter_wrapper::operator()( - command cmd, - type t, - std::size_t aggregate_size, - std::size_t depth, - char const* data, - std::size_t size, - std::error_code& ec) -{ - // Handles only the commands we are interested in the examples and - // ignores the rest. - switch (cmd) { - case command::quit: - case command::ping: - str_adapter_(t, aggregate_size, depth, data, size, ec); - return; - - case command::incr: - number_adapter_(t, aggregate_size, depth, data, size, ec); - return; - - case command::unknown: - general_adapter_(t, aggregate_size, depth, data, size, ec); - return; - - default: {} // Ignore. - } -} diff --git a/examples/lib/user_session.hpp b/examples/lib/user_session.hpp index 6091d095..b26d23a9 100644 --- a/examples/lib/user_session.hpp +++ b/examples/lib/user_session.hpp @@ -9,7 +9,7 @@ #include -#include +#include // An example user session. @@ -26,14 +26,66 @@ class user_session: public user_session_base, public std::enable_shared_from_this { public: - user_session(net::ip::tcp::socket socket); - void start(std::function on_msg); - void deliver(std::string const& msg); + user_session(net::ip::tcp::socket socket) + : socket_(std::move(socket)) + , timer_(socket_.get_executor()) + { timer_.expires_at(std::chrono::steady_clock::time_point::max()); } + + void start(std::function on_msg) + { + co_spawn(socket_.get_executor(), + [self = shared_from_this(), on_msg]{ return self->reader(on_msg); }, + net::detached); + + co_spawn(socket_.get_executor(), + [self = shared_from_this()]{ return self->writer(); }, + net::detached); + } + + void deliver(std::string const& msg) + { + write_msgs_.push_back(msg); + timer_.cancel_one(); + } private: - net::awaitable reader(std::function on_msg); - net::awaitable writer(); - void stop(); + net::awaitable + reader(std::function on_msg) + { + try { + for (std::string msg;;) { + auto const n = co_await net::async_read_until(socket_, net::dynamic_buffer(msg, 1024), "\n", net::use_awaitable); + on_msg(msg); + msg.erase(0, n); + } + } catch (std::exception&) { + stop(); + } + } + + net::awaitable writer() + { + try { + while (socket_.is_open()) { + if (write_msgs_.empty()) { + boost::system::error_code ec; + co_await timer_.async_wait(redirect_error(net::use_awaitable, ec)); + } else { + co_await net::async_write(socket_, net::buffer(write_msgs_.front()), net::use_awaitable); + write_msgs_.pop_front(); + } + } + } catch (std::exception&) { + stop(); + } + } + + void stop() + { + socket_.close(); + timer_.cancel(); + } + net::ip::tcp::socket socket_; net::steady_timer timer_; std::deque write_msgs_; diff --git a/examples/lib/user_session.ipp b/examples/lib/user_session.ipp deleted file mode 100644 index 3b0ecddf..00000000 --- a/examples/lib/user_session.ipp +++ /dev/null @@ -1,72 +0,0 @@ -/* Copyright (c) 2019 Marcelo Zimbres Silva (mzimbres@gmail.com) - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ - -#include "user_session.hpp" - -namespace aedis -{ - -user_session::user_session(net::ip::tcp::socket socket) -: socket_(std::move(socket)) -, timer_(socket_.get_executor()) - { timer_.expires_at(std::chrono::steady_clock::time_point::max()); } - -net::awaitable user_session::writer() -{ - try { - while (socket_.is_open()) { - if (write_msgs_.empty()) { - boost::system::error_code ec; - co_await timer_.async_wait(redirect_error(net::use_awaitable, ec)); - } else { - co_await net::async_write(socket_, net::buffer(write_msgs_.front()), net::use_awaitable); - write_msgs_.pop_front(); - } - } - } catch (std::exception&) { - stop(); - } -} - -void user_session::stop() -{ - socket_.close(); - timer_.cancel(); -} - -void user_session::deliver(std::string const& msg) -{ - write_msgs_.push_back(msg); - timer_.cancel_one(); -} - -void user_session::start(std::function on_msg) -{ - co_spawn(socket_.get_executor(), - [self = shared_from_this(), on_msg]{ return self->reader(on_msg); }, - net::detached); - - co_spawn(socket_.get_executor(), - [self = shared_from_this()]{ return self->writer(); }, - net::detached); -} - -net::awaitable -user_session::reader(std::function on_msg) -{ - try { - for (std::string msg;;) { - auto const n = co_await net::async_read_until(socket_, net::dynamic_buffer(msg, 1024), "\n", net::use_awaitable); - on_msg(msg); - msg.erase(0, n); - } - } catch (std::exception&) { - stop(); - } -} - -} // aedis diff --git a/examples/lists.cpp b/examples/lists.cpp index efbb0a94..4441a91e 100644 --- a/examples/lists.cpp +++ b/examples/lists.cpp @@ -11,13 +11,14 @@ #include #include -#include #include -#include "utils.ipp" +#include + +#include "lib/net_utils.hpp" namespace resp3 = aedis::resp3; -using aedis::command; -using resp3::make_serializer; +using aedis::redis::command; +using aedis::redis::make_serializer; using resp3::adapt; namespace net = aedis::net; @@ -42,7 +43,7 @@ net::awaitable ping() // Creates and sends the request. auto vec = {1, 2, 3, 4, 5, 6}; std::string request; - auto sr = make_serializer(request); + auto sr = make_serializer(request); sr.push(command::hello, 3); sr.push(command::flushall); sr.push_range(command::rpush, "key", std::cbegin(vec), std::cend(vec)); diff --git a/examples/nested_response.cpp b/examples/nested_response.cpp index 10e0f86b..47b2c063 100644 --- a/examples/nested_response.cpp +++ b/examples/nested_response.cpp @@ -6,46 +6,72 @@ */ #include +#include +#include +#include + +#include -#include #include -#include "utils.ipp" +#include + +#include "lib/net_utils.hpp" namespace resp3 = aedis::resp3; -using aedis::command; -using resp3::make_serializer; +using aedis::redis::command; +using aedis::redis::make_serializer; using resp3::adapt; using resp3::node; +using resp3::is_aggregate; +using resp3::response_traits; +using resp3::type; namespace net = aedis::net; using net::async_write; using net::buffer; using net::dynamic_buffer; -/// Shows how to read nested responses. - -net::awaitable nested_response() +// Reads the response to a transaction in a general format that is +// suitable for all kinds of responses, but which users will most +// likely have to convert into their own desired format. +net::awaitable nested_response1() { try { auto socket = co_await connect(); + auto list = {"one", "two", "three"}; + std::string request; - auto sr = make_serializer(request); + auto sr = make_serializer(request); sr.push(command::hello, 3); + sr.push(command::flushall); + sr.push(command::multi); + sr.push(command::ping, "Some message"); + sr.push(command::incr, "incr-key"); + sr.push_range(command::rpush, "list-key", std::cbegin(list), std::cend(list)); + sr.push(command::lrange, "list-key", 0, -1); + sr.push(command::exec); sr.push(command::quit); co_await async_write(socket, buffer(request)); // Expected responses. - node ping; - std::vector hello; + std::vector exec; // Reads the response. std::string buffer; - co_await resp3::async_read(socket, dynamic_buffer(buffer), adapt(hello)); - co_await resp3::async_read(socket, dynamic_buffer(buffer)); + co_await resp3::async_read(socket, dynamic_buffer(buffer)); // hello + co_await resp3::async_read(socket, dynamic_buffer(buffer)); // flushall + co_await resp3::async_read(socket, dynamic_buffer(buffer)); // multi + co_await resp3::async_read(socket, dynamic_buffer(buffer)); // ping + co_await resp3::async_read(socket, dynamic_buffer(buffer)); // incr + co_await resp3::async_read(socket, dynamic_buffer(buffer)); // rpush + co_await resp3::async_read(socket, dynamic_buffer(buffer)); // lrange + co_await resp3::async_read(socket, dynamic_buffer(buffer), adapt(exec)); + co_await resp3::async_read(socket, dynamic_buffer(buffer)); // quit // Prints the response. - for (auto const& e: hello) std::cout << e << "\n"; + std::cout << "General format:\n"; + for (auto const& e: exec) std::cout << e << "\n"; } catch (std::exception const& e) { std::cerr << e.what() << std::endl; @@ -53,9 +79,148 @@ net::awaitable nested_response() } } +template +struct helper { + template + static void assign(T1& dest, T2& from) + { + dest[N].template emplace(adapt(std::get(from))); + helper::assign(dest, from); + } +}; + +template <> +struct helper<0> { + template + static void assign(T1& dest, T2& from) + { + dest[0] = adapt(std::get<0>(from)); + } +}; + +// Same as above but parses the responses directly in their final data +// structures. +// +// WARNING: This example is not interesting for most users, it is an +// adavanced feature meant for people with strong performance need. + +template +using response_traits_t = typename response_traits::adapter_type; + +// Adapts the responses above to a read operation. +template +class flat_transaction_adapter { +private: + using variant_type = + boost::mp11::mp_rename, std::variant>; + + std::size_t i_ = 0; + std::size_t aggregate_size_ = 0; + std::array::value> adapters_; + +public: + flat_transaction_adapter(Tuple& r) + { helper::value - 1>::assign(adapters_, r); } + + void count(type t, std::size_t aggregate_size, std::size_t depth) + { + if (depth == 1) { + if (is_aggregate(t)) + aggregate_size_ = aggregate_size; + else + ++i_; + + return; + } + + if (--aggregate_size_ == 0) + ++i_; + } + + void + operator()( + type t, + std::size_t aggregate_size, + std::size_t depth, + char const* data, + std::size_t size, + std::error_code& ec) + { + if (depth == 0) { + // aggregate_size must be equal to the tuple size. Check + // this and add a new error code. + return; // We are interested in the size of the transaction. + } + + std::visit([&](auto& arg){arg(t, aggregate_size, depth, data, size, ec);}, adapters_[i_]); + count(t, aggregate_size, depth); + } +}; + +template +auto make_adapter(Tuple& t) +{ + return flat_transaction_adapter(t); +} + +net::awaitable nested_response2() +{ + try { + auto socket = co_await connect(); + + auto list = {"one", "two", "three"}; + + std::string request; + auto sr = make_serializer(request); + sr.push(command::hello, 3); + sr.push(command::flushall); + + // Adds a transaction + sr.push(command::multi); + sr.push(command::ping, "Some message"); + sr.push(command::incr, "incr1-key"); + sr.push_range(command::rpush, "list-key", std::cbegin(list), std::cend(list)); + sr.push(command::lrange, "list-key", 0, -1); + sr.push(command::incr, "incr2-key"); + sr.push(command::exec); + + sr.push(command::quit); + co_await async_write(socket, buffer(request)); + + // Expected responses. + std::tuple, int> execs; + + // Reads the response. + std::string buffer; + co_await resp3::async_read(socket, dynamic_buffer(buffer)); // hello + co_await resp3::async_read(socket, dynamic_buffer(buffer)); // flushall + co_await resp3::async_read(socket, dynamic_buffer(buffer)); // multi + co_await resp3::async_read(socket, dynamic_buffer(buffer)); // ping + co_await resp3::async_read(socket, dynamic_buffer(buffer)); // incr + co_await resp3::async_read(socket, dynamic_buffer(buffer)); // rpush + co_await resp3::async_read(socket, dynamic_buffer(buffer)); // lrange + co_await resp3::async_read(socket, dynamic_buffer(buffer)); // incr + co_await resp3::async_read(socket, dynamic_buffer(buffer), make_adapter(execs)); + co_await resp3::async_read(socket, dynamic_buffer(buffer)); // quit + + // Prints the response to the transaction. + std::cout << "ping: " << std::get<0>(execs) << "\n"; + std::cout << "incr1: " << std::get<1>(execs) << "\n"; + std::cout << "rpush: " << std::get<2>(execs) << "\n"; + std::cout << "lrange: "; + for (auto const& e: std::get<3>(execs)) std::cout << e << " "; + std::cout << "\n"; + std::cout << "incr2: " << std::get<4>(execs) << "\n"; + + } catch (std::exception const& e) { + std::cerr << e.what() << std::endl; + exit(EXIT_FAILURE); + } +} int main() { net::io_context ioc; - co_spawn(ioc, nested_response(), net::detached); + co_spawn(ioc, nested_response1(), net::detached); + co_spawn(ioc, nested_response2(), net::detached); ioc.run(); } diff --git a/examples/redis_client.cpp b/examples/redis_client.cpp index ed8286a5..57839790 100644 --- a/examples/redis_client.cpp +++ b/examples/redis_client.cpp @@ -11,10 +11,9 @@ #include #include "lib/responses.hpp" -#include "src.hpp" namespace net = aedis::net; -using aedis::command; +using aedis::redis::command; using aedis::resp3::experimental::client; int main() diff --git a/examples/response_adapter.cpp b/examples/response_adapter.cpp index a4932c75..2be98c09 100644 --- a/examples/response_adapter.cpp +++ b/examples/response_adapter.cpp @@ -8,14 +8,15 @@ #include #include -#include #include -#include "utils.ipp" +#include + +#include "lib/net_utils.hpp" namespace resp3 = aedis::resp3; -using aedis::command; +using aedis::redis::command; using resp3::type; -using resp3::make_serializer; +using aedis::redis::make_serializer; using resp3::adapt; namespace net = aedis::net; @@ -57,7 +58,7 @@ net::awaitable adapter_example() // Creates and sends the request. std::string request; - auto sr = make_serializer(request); + auto sr = make_serializer(request); sr.push(command::hello, 3); sr.push(command::flushall); sr.push_range(command::rpush, "key", std::cbegin(list), std::cend(list)); diff --git a/examples/serialization.cpp b/examples/serialization.cpp index 8d7022e1..f0fda7fa 100644 --- a/examples/serialization.cpp +++ b/examples/serialization.cpp @@ -9,13 +9,14 @@ #include #include -#include #include -#include "utils.ipp" +#include + +#include "lib/net_utils.hpp" namespace resp3 = aedis::resp3; -using aedis::command; -using resp3::make_serializer; +using aedis::redis::make_serializer; +using aedis::redis::command; using resp3::adapt; namespace net = aedis::net; @@ -81,7 +82,7 @@ net::awaitable serialization() mydata obj{21, 22}; std::string request; - auto sr = make_serializer(request); + auto sr = make_serializer(request); sr.push(command::hello, 3); sr.push(command::flushall); sr.push(command::set, "key", obj); diff --git a/examples/sets.cpp b/examples/sets.cpp index 88fead2e..6a8732b9 100644 --- a/examples/sets.cpp +++ b/examples/sets.cpp @@ -10,13 +10,14 @@ #include #include -#include #include -#include "utils.ipp" +#include + +#include "lib/net_utils.hpp" namespace resp3 = aedis::resp3; -using aedis::command; -using resp3::make_serializer; +using aedis::redis::command; +using aedis::redis::make_serializer; using resp3::adapt; namespace net = aedis::net; @@ -36,7 +37,7 @@ net::awaitable containers() // Creates and sends the request. std::string request; - auto sr = make_serializer(request); + auto sr = make_serializer(request); sr.push(command::hello, 3); sr.push(command::flushall); sr.push_range(command::sadd, "key", std::cbegin(set), std::cend(set)); diff --git a/examples/src.hpp b/examples/src.hpp deleted file mode 100644 index f740ff11..00000000 --- a/examples/src.hpp +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once - -#include "lib/user_session.ipp" -#include "lib/responses.ipp" diff --git a/examples/subscriber.cpp b/examples/subscriber.cpp index 6cc75f2a..99766bab 100644 --- a/examples/subscriber.cpp +++ b/examples/subscriber.cpp @@ -8,13 +8,14 @@ #include #include -#include #include -#include "utils.ipp" +#include + +#include "lib/net_utils.hpp" namespace resp3 = aedis::resp3; -using aedis::command; -using resp3::make_serializer; +using aedis::redis::command; +using aedis::redis::make_serializer; using resp3::adapt; using resp3::node; @@ -48,7 +49,7 @@ net::awaitable subscriber() auto socket = co_await connect(); std::string request; - auto sr = make_serializer(request); + auto sr = make_serializer(request); sr.push(command::hello, "3"); sr.push(command::subscribe, "channel1", "channel2"); co_await async_write(socket, buffer(request)); diff --git a/examples/sync.cpp b/examples/sync.cpp index 68f28b83..701a2f48 100644 --- a/examples/sync.cpp +++ b/examples/sync.cpp @@ -7,12 +7,12 @@ #include -#include #include +#include namespace resp3 = aedis::resp3; -using aedis::command; -using aedis::resp3::make_serializer; +using aedis::redis::command; +using aedis::redis::make_serializer; using aedis::resp3::adapt; using aedis::resp3::node; @@ -34,7 +34,7 @@ int main() connect(socket, res); std::string request; - auto sr = make_serializer(request); + auto sr = make_serializer(request); sr.push(command::hello, 3); sr.push(command::command); sr.push(command::quit); diff --git a/tests/online.cpp b/tests/online.cpp index f8ca74b7..d1e42c44 100644 --- a/tests/online.cpp +++ b/tests/online.cpp @@ -8,8 +8,8 @@ #include #include -#include #include +#include #include "test_stream.hpp" #include "check.hpp" @@ -22,6 +22,8 @@ namespace this_coro = net::this_coro; using namespace aedis; using namespace aedis::resp3; +using aedis::redis::command; +using aedis::redis::make_serializer; std::vector gresp; @@ -37,7 +39,7 @@ test_general(net::ip::tcp::resolver::results_type const& res) //---------------------------------- std::string request; - auto sr = make_serializer(request); + auto sr = make_serializer(request); sr.push(command::hello, 3); sr.push(command::flushall); sr.push_range(command::rpush, "a", std::cbegin(list_), std::cend(list_)); @@ -482,7 +484,7 @@ test_set(net::ip::tcp::resolver::results_type const& results) std::string test_bulk2 = "aaaaa"; std::string request; - auto sr = make_serializer(request); + auto sr = make_serializer(request); sr.push(command::hello, 3); sr.push(command::flushall); sr.push(command::set, "s", test_bulk1); diff --git a/tools/commands.cpp b/tools/commands.cpp index 98f21bda..5de0b8fa 100644 --- a/tools/commands.cpp +++ b/tools/commands.cpp @@ -9,12 +9,12 @@ #include #include -#include #include +#include namespace resp3 = aedis::resp3; -using aedis::command; -using resp3::make_serializer; +using aedis::redis::command; +using aedis::redis::make_serializer; using resp3::adapt; using resp3::node; @@ -81,7 +81,7 @@ int main() connect(socket, res); std::string request; - auto sr = make_serializer(request); + auto sr = make_serializer(request); sr.push(command::hello, 3); sr.push(command::command); sr.push(command::quit);