From 375799fa79d1af76f33299acc20a11a167a021f8 Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Thu, 17 Aug 2023 12:32:50 +0200 Subject: project: restructure libraries and build env --- source/lib/proto/CMakeLists.txt | 38 +++++++++++ source/lib/proto/include/wanda/proto/command.hpp | 46 +++++++++++++ source/lib/proto/include/wanda/proto/message.hpp | 84 ++++++++++++++++++++++++ source/lib/proto/include/wanda/proto/version.hpp | 12 ++++ source/lib/proto/src/command.cpp | 47 +++++++++++++ source/lib/proto/src/message.cpp | 82 +++++++++++++++++++++++ 6 files changed, 309 insertions(+) create mode 100644 source/lib/proto/CMakeLists.txt create mode 100644 source/lib/proto/include/wanda/proto/command.hpp create mode 100644 source/lib/proto/include/wanda/proto/message.hpp create mode 100644 source/lib/proto/include/wanda/proto/version.hpp create mode 100644 source/lib/proto/src/command.cpp create mode 100644 source/lib/proto/src/message.cpp (limited to 'source/lib/proto') diff --git a/source/lib/proto/CMakeLists.txt b/source/lib/proto/CMakeLists.txt new file mode 100644 index 0000000..9967a95 --- /dev/null +++ b/source/lib/proto/CMakeLists.txt @@ -0,0 +1,38 @@ +cmake_path(GET CMAKE_CURRENT_SOURCE_DIR STEM LIB_NAME) + +file(GLOB_RECURSE LIB_HEADERS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" CONFIGURE_DEPENDS "**/*.hpp") +file(GLOB_RECURSE LIB_SOURCES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" CONFIGURE_DEPENDS "**/*.cpp") + +find_package("fmt" REQUIRED) + +add_library("wanda-${LIB_NAME}" + ${LIB_SOURCES} +) + +target_sources("wanda-${LIB_NAME}" INTERFACE + FILE_SET HEADERS + FILES ${LIB_HEADERS} + BASE_DIRS "include" +) + +target_include_directories("wanda-${LIB_NAME}" PUBLIC + "$" +) + +target_include_directories("wanda-${LIB_NAME}" SYSTEM PUBLIC + "$" +) + +target_compile_features("wanda-${LIB_NAME}" PUBLIC + "cxx_std_20" +) + +target_link_libraries("wanda-${LIB_NAME}" PUBLIC + "fmt::fmt" +) + +install(TARGETS "wanda-${LIB_NAME}" + FILE_SET HEADERS +) + +add_library("wanda::${LIB_NAME}" ALIAS "wanda-${LIB_NAME}") diff --git a/source/lib/proto/include/wanda/proto/command.hpp b/source/lib/proto/include/wanda/proto/command.hpp new file mode 100644 index 0000000..c8dae65 --- /dev/null +++ b/source/lib/proto/include/wanda/proto/command.hpp @@ -0,0 +1,46 @@ +#ifndef WANDA_PROTO_COMMAND_HPP +#define WANDA_PROTO_COMMAND_HPP + +#include "wanda/proto/message.hpp" + +#include +#include +#include + +namespace wanda::proto +{ + /** + * @brief An enum to describe different command IDs + */ + enum struct command_id : char + { + change, //< Change the wallpaper + }; + + /** + * @brief A simple type to represent commands transported through the control connection + */ + struct command + { + command_id const id; + std::vector const arguments; + + /** + * @brief Convert the command to a message for transmission to a remote endpoint + */ + std::optional message() const; + }; + + /** + * @brief Extract a command from a message + */ + std::optional make_command(message message); + + /** + * @brief A simple factory to create a "Change wallpaper" command + */ + command make_change_command(); + +} // namespace wanda::proto + +#endif \ No newline at end of file diff --git a/source/lib/proto/include/wanda/proto/message.hpp b/source/lib/proto/include/wanda/proto/message.hpp new file mode 100644 index 0000000..f4a6a3b --- /dev/null +++ b/source/lib/proto/include/wanda/proto/message.hpp @@ -0,0 +1,84 @@ +/** + * @file message.hpp + * @author Felix Morgner (felix.morgner@gmail.com) + * @since 1.0.0 + */ + +#ifndef WANDA_PROTO_MESSAGE_HPP +#define WANDA_PROTO_MESSAGE_HPP + +#include + +#include +#include +#include +#include +#include + +namespace wanda::proto +{ + /** + * @brief A tag to mark messages originating from the controller + */ + auto constexpr message_source_controller = "C"; + + /** + * @brief A tag to mark messages originating from the daemon + */ + auto constexpr message_source_daemon = "D"; + + /** + * @brief The command of the hello message + */ + auto constexpr message_command_hello = "HELLO"; + + /** + * @brief A control protocol message, consisting of a @p source, @p command, and @p arguments + */ + struct message + { + /** + * @brief Serialize this message into a string + */ + explicit operator std::string() const; + + /** + * @brief Get the size of the message as if it was serialized + */ + std::size_t size() const; + + /** + * @brief The source of the message + */ + std::string source; + + /** + * @brief The command of the message + */ + std::string command; + + /** + * @brief The arguments of the message command + */ + std::optional argument; + }; + + /** + * @brief Deserialize a message from the given stream + */ + std::istream & operator>>(std::istream & in, message & message); + + /** + * @brief Serialize a message to the given stream + */ + std::ostream & operator<<(std::ostream & out, message const & message); + +} // namespace wanda::proto + +template<> +struct fmt::formatter : fmt::formatter +{ + auto format(wanda::proto::message const & message, format_context & context) const -> decltype(context.out()); +}; + +#endif \ No newline at end of file diff --git a/source/lib/proto/include/wanda/proto/version.hpp b/source/lib/proto/include/wanda/proto/version.hpp new file mode 100644 index 0000000..faa17d7 --- /dev/null +++ b/source/lib/proto/include/wanda/proto/version.hpp @@ -0,0 +1,12 @@ +#ifndef WANDA_PROTO_VERSION_HPP +#define WANDA_PROTO_VERSION_HPP + +namespace wanda::proto +{ + inline namespace v1 + { + auto constexpr version = "1.0.0"; + } +} // namespace wanda::proto + +#endif \ No newline at end of file diff --git a/source/lib/proto/src/command.cpp b/source/lib/proto/src/command.cpp new file mode 100644 index 0000000..5a669f5 --- /dev/null +++ b/source/lib/proto/src/command.cpp @@ -0,0 +1,47 @@ +#include "wanda/proto/command.hpp" + +namespace wanda::proto +{ + std::optional command::message() const + { + using namespace std::string_literals; + auto const command = [this] { + switch (id) + { + case command_id::change: + return "CHANGE"s; + default: + return ""s; + } + }(); + + auto argument_string = std::string{}; + for (int index = 0ul; index < arguments.size(); ++index) + { + argument_string += (index) ? "," + arguments[index] : arguments[index]; + } + + if (command.empty()) + { + return std::nullopt; + } + + return wanda::proto::message{"C", command, argument_string}; + } + + std::optional make_command(message message) + { + if (message.command == "CHANGE") + { + return {{command_id::change}}; + } + + return std::nullopt; + } + + command make_change_command() + { + return {command_id::change}; + } + +} // namespace wanda::proto \ No newline at end of file diff --git a/source/lib/proto/src/message.cpp b/source/lib/proto/src/message.cpp new file mode 100644 index 0000000..dad3e86 --- /dev/null +++ b/source/lib/proto/src/message.cpp @@ -0,0 +1,82 @@ +#include "wanda/proto/message.hpp" + +#include + +#include +#include +#include + +namespace wanda::proto +{ + message::operator std::string() const + { + std::ostringstream buffer{}; + buffer << source + << ':' + << command; + if (argument.has_value()) + { + buffer << ':' << *argument; + } + return buffer.str(); + } + + std::size_t message::size() const + { + return static_cast(*this).size(); + } + + template + OutputIt copy_until(InputIt first, InputIt last, OutputIt out, UnaryPredicate predicate) + { + while (first != last && !predicate(*first)) + { + *out++ = *first++; + } + return out; + } + + std::istream & operator>>(std::istream & in, message & message) + { + auto pos = std::istream_iterator{in}; + auto end = std::istream_iterator{}; + auto buffer = std::string{}; + + copy_until(pos, end, std::back_inserter(buffer), [](auto const & c) { return c == ':'; }); + if (in.eof() || buffer.size() != 1) + { + in.setstate(std::ios_base::failbit); + return in; + } + message.source = buffer; + + buffer.clear(); + copy_until(++pos, end, std::back_inserter(buffer), [](auto const & c) { return c == ':'; }); + if (in.eof()) + { + in.setstate(std::ios_base::failbit); + } + message.command = buffer; + + buffer.clear(); + copy(++pos, end, std::back_inserter(buffer)); + if (buffer.size()) + { + message.argument = std::optional{std::move(buffer)}; + } + + in.clear(in.rdstate() ^ std::ios_base::failbit); + return in; + } + + std::ostream & operator<<(std::ostream & out, message const & message) + { + return out << static_cast(message); + } + +} // namespace wanda::proto + +auto fmt::formatter::format(wanda::proto::message const & message, format_context & context) const -> decltype(context.out()) +{ + return formatter::format(static_cast(message), context); +} -- cgit v1.2.3