diff --git a/Makefile.am b/Makefile.am index 1ccb2383..03ca5b9b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -83,7 +83,6 @@ nobase_include_HEADERS =\ nobase_noinst_HEADERS =\ $(top_srcdir)/examples/lib/net_utils.hpp\ - $(top_srcdir)/examples/lib/responses.hpp\ $(top_srcdir)/examples/lib/user_session.hpp\ $(top_srcdir)/tests/check.hpp\ $(top_srcdir)/tests/test_stream.hpp diff --git a/README.md b/README.md index 086ac2e4..05c95a9e 100644 --- a/README.md +++ b/README.md @@ -1,53 +1 @@ -# Aedis - -Aedis is a redis client designed for scalability and performance while -providing an easy and intuitive interface. - -## Example - -Lets us see some examples with increasing order of complexity. -See the `examples` directory for more examples. - -### Ping - -A very simple example with illustrative purposes. The example above -will start writing the `hello` command and proceed reading its -response. After that users can add further commands to the queue. See -the example directory for a complete example. - - -```cpp -net::awaitable ping2() -{ - auto socket = co_await make_connection(); - resp3::stream stream{std::move(socket)}; - - resp3::request req; - req.push(command::hello, 3); - req.push(command::ping); - req.push(command::quit); - co_await stream.async_write(req); - - while (!std::empty(req.commands)) { - resp3::response resp; - co_await stream.async_read(resp); - req.commands.pop(); - std::cout << resp << std::endl; - } -} -``` - -## Installation - -This library is header only. To install it run - -```cpp -$ sudo make install -``` - -or copy the include folder to where you want. You will also need to include -the following header in one of your source files e.g. `aedis.cpp` - -```cpp -#include -``` +See https://mzimbres.github.io/aedis/ diff --git a/aedis/aedis.hpp b/aedis/aedis.hpp index f690596c..b6e656b0 100644 --- a/aedis/aedis.hpp +++ b/aedis/aedis.hpp @@ -162,7 +162,7 @@ * - \b Redis-client: used in on example. * - \b Redis \b Sentinel \b server: used in some examples. * - * \section Installing + * \section Installation * * Get the latest release and follow the steps * @@ -197,6 +197,12 @@ * $ make check * ``` * + * \subsection Windows + * + * Windows users can use aedis by either adding the project root + * directory to their include path or manually copying to another + * location. + * * \section using Using Aedis * * This library in not header-only. You have to include the following diff --git a/aedis/redis/experimental/client.hpp b/aedis/redis/experimental/client.hpp index 50c9e5d0..c15aa7f6 100644 --- a/aedis/redis/experimental/client.hpp +++ b/aedis/redis/experimental/client.hpp @@ -59,11 +59,11 @@ private: // next message in the output queue. net::steady_timer timer_; - // Response adapter. TODO: Set a default adapter. - adapter_type adapter_; + // Response adapter. + adapter_type adapter_ = [](redis::command, type, std::size_t, std::size_t, char const*, std::size_t, std::error_code&) {}; - // Message callback. TODO: Set a default callback. - on_message_type on_msg_; + // Message callback. + on_message_type on_msg_ = [](std::error_code ec, redis::command) {}; // A coroutine that keeps reading the socket. When a message // arrives it calls on_message. diff --git a/aedis/resp3/serializer.hpp b/aedis/resp3/serializer.hpp index 01397567..506c5d1d 100644 --- a/aedis/resp3/serializer.hpp +++ b/aedis/resp3/serializer.hpp @@ -39,7 +39,13 @@ namespace resp3 { * to_string, which must be made available over ADL. */ -// TODO: Add an overload for containers. +// NOTE: Consider adding an overload for containers. +// +// TODO: Should we detect any std::pair or tuple in the type in the parameter +// pack to calculate the header size correctly? +// +// NOTE: For some commands like hset it would be a good idea to assert +// the value type is a pair. template class serializer { private: @@ -73,9 +79,6 @@ public: template void push(Command cmd, Ts const&... args) { - // TODO: Should we detect any std::pair in the type in the pack - // to calculate the header size correctly? - auto constexpr pack_size = sizeof...(Ts); detail::add_header(*request_, 1 + pack_size); @@ -106,9 +109,6 @@ public: template void push_range(Command cmd, Key const& key, ForwardIterator begin, ForwardIterator end) { - // Note: For some commands like hset it would helpful to users - // to assert the value type is a pair. - using value_type = typename std::iterator_traits::value_type; auto constexpr size = detail::value_type_size::size; @@ -141,9 +141,6 @@ public: template void push_range(Command cmd, ForwardIterator begin, ForwardIterator end) { - // Note: For some commands like hset it would be a good idea to assert - // the value type is a pair. - using value_type = typename std::iterator_traits::value_type; auto constexpr size = detail::value_type_size::size; diff --git a/doc/Doxyfile b/doc/Doxyfile index dd88de39..920e33a9 100644 --- a/doc/Doxyfile +++ b/doc/Doxyfile @@ -58,7 +58,7 @@ PROJECT_LOGO = # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. -OUTPUT_DIRECTORY = /tmp/aedis +OUTPUT_DIRECTORY = /home/marcelo/my/aedis-gh-pages # If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- # directories (in 2 levels) under the output directory of each output format and @@ -1170,7 +1170,7 @@ GENERATE_HTML = YES # The default directory is: html. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_OUTPUT = html +HTML_OUTPUT = . # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each # generated HTML page (for example: .htm, .php, .asp). diff --git a/examples/chat_room.cpp b/examples/chat_room.cpp index 6966a042..f2161979 100644 --- a/examples/chat_room.cpp +++ b/examples/chat_room.cpp @@ -12,26 +12,24 @@ #include #include "lib/user_session.hpp" -#include "lib/responses.hpp" namespace net = aedis::net; using aedis::redis::command; +using aedis::resp3::adapt; +using aedis::resp3::experimental::client; +using aedis::resp3::node; +using aedis::resp3::type; using aedis::user_session; using aedis::user_session_base; -using aedis::resp3::experimental::client; +// TODO: Delete sessions that have expired. class receiver : public std::enable_shared_from_this { +public: private: - responses resps_; + std::vector resps_; std::vector> sessions_; public: - auto get_adapter() - { return adapter_wrapper{resps_}; } - - auto add(std::shared_ptr session) - { sessions_.push_back(session); } - void on_message(std::error_code ec, command cmd) { if (ec) { @@ -42,24 +40,33 @@ public: switch (cmd) { case command::incr: { - std::cout << "Message so far: " << resps_.number << std::endl; + std::cout << "Message so far: " << resps_.front().data << std::endl; } break; case command::unknown: // Push { - // TODO: Delete sessions lazily on traversal. for (auto& weak: sessions_) { if (auto session = weak.lock()) { - session->deliver(resps_.general.at(3).data); + session->deliver(resps_.at(3).data); } else { std::cout << "Session expired." << std::endl; } } - resps_.general.clear(); } break; default: { /* Ignore */ } } + + resps_.clear(); } + + auto get_adapter() + { + return [adapter = adapt(resps_)](command, type t, std::size_t aggregate_size, std::size_t depth, char const* data, std::size_t size, std::error_code& ec) mutable + { return adapter(t, aggregate_size, depth, data, size, ec); }; + } + + auto add(std::shared_ptr session) + { sessions_.push_back(session); } }; net::awaitable listener() diff --git a/examples/echo_server.cpp b/examples/echo_server.cpp index 0c4b87c4..2cd8ccdd 100644 --- a/examples/echo_server.cpp +++ b/examples/echo_server.cpp @@ -7,31 +7,28 @@ #include #include +#include #include #include #include "lib/user_session.hpp" -#include "lib/responses.hpp" namespace net = aedis::net; using aedis::redis::command; using aedis::user_session; using aedis::user_session_base; +using aedis::resp3::node; +using aedis::resp3::adapt; using aedis::resp3::experimental::client; +using aedis::resp3::type; class receiver : public std::enable_shared_from_this { private: - responses resps_; + std::vector resps_; std::queue> sessions_; public: - auto get_adapter() - { return adapter_wrapper{resps_}; } - - void add_user_session(std::shared_ptr session) - { sessions_.push(session); } - void on_message(std::error_code ec, command cmd) { if (ec) { @@ -43,21 +40,31 @@ public: case command::ping: { if (auto session = sessions_.front().lock()) { - session->deliver(resps_.simple_string); + session->deliver(resps_.front().data); } else { std::cout << "Session expired." << std::endl; } sessions_.pop(); - resps_.simple_string.clear(); } break; case command::incr: { - std::cout << "Echos so far: " << resps_.number << std::endl; + std::cout << "Echos so far: " << resps_.front().data << std::endl; } break; default: { assert(false); } } + + resps_.clear(); } + + auto get_adapter() + { + return [adapter = adapt(resps_)](command, type t, std::size_t aggregate_size, std::size_t depth, char const* data, std::size_t size, std::error_code& ec) mutable + { return adapter(t, aggregate_size, depth, data, size, ec); }; + } + + void add_user_session(std::shared_ptr session) + { sessions_.push(session); } }; net::awaitable listener() diff --git a/examples/lib/responses.hpp b/examples/lib/responses.hpp deleted file mode 100644 index d62a3692..00000000 --- a/examples/lib/responses.hpp +++ /dev/null @@ -1,70 +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/. - */ - -#pragma once - -#include - -namespace net = aedis::net; -using aedis::redis::command; -using aedis::resp3::adapt; -using aedis::resp3::response_traits; -using aedis::resp3::type; -using aedis::resp3::node; - -// Groups the responses used in the examples. -struct responses { - int number; - std::string simple_string; - std::vector general; -}; - -// Adpter as required by experimental::client. -class adapter_wrapper { -private: - response_traits::adapter_type number_adapter_; - response_traits::adapter_type str_adapter_; - response_traits>::adapter_type general_adapter_; - -public: - adapter_wrapper(responses& resps) - : number_adapter_{adapt(resps.number)} - , str_adapter_{adapt(resps.simple_string)} - , general_adapter_{adapt(resps.general)} - {} - - 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) - { - // 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/redis_client.cpp b/examples/redis_client.cpp index a654c75e..54ca5326 100644 --- a/examples/redis_client.cpp +++ b/examples/redis_client.cpp @@ -10,16 +10,16 @@ #include #include -#include "lib/responses.hpp" - namespace net = aedis::net; using aedis::redis::command; using aedis::resp3::experimental::client; +using aedis::resp3::node; +using aedis::resp3::type; int main() { try { - responses resps; + std::vector resps; auto on_msg = [&resps](std::error_code ec, command cmd) { @@ -28,29 +28,19 @@ int main() return; } - switch (cmd) { - case command::ping: - { - std::cout << "ping: " << resps.simple_string << std::endl; - resps.simple_string.clear(); - } break; - case command::quit: - { - std::cout << "quit: " << resps.simple_string << std::endl; - resps.simple_string.clear(); - } break; - case command::incr: - { - std::cout << "incr: " << resps.number << std::endl; - } break; - default: { assert(false); } - } + std::cout << cmd << ": " << resps.front().data << std::endl; + resps.clear(); }; net::io_context ioc{1}; + // This adapter uses the general response that is suitable for + // all commands, so the command parameter will be ignored. + auto adapter = [adapter = adapt(resps)](command, type t, std::size_t aggregate_size, std::size_t depth, char const* data, std::size_t size, std::error_code& ec) mutable + { return adapter(t, aggregate_size, depth, data, size, ec); }; + auto db = std::make_shared(ioc.get_executor()); - db->set_adapter(adapter_wrapper{resps}); + db->set_adapter(adapter); db->set_msg_callback(on_msg); db->send(command::ping, "O rato roeu a roupa do rei de Roma"); db->send(command::incr, "redis-client-counter");