From 64e2aeb8aa05bf2991501e0d6d0aed193f6670a1 Mon Sep 17 00:00:00 2001 From: sha512sum Date: Sat, 21 Dec 2024 18:56:09 +1100 Subject: [PATCH] Add message serialization and deserialization --- .forgejo/workflows/pr_check.yaml | 12 ++--- library/include/larra/message.hpp | 49 +++++++++++++++++++ library/include/larra/serialization/auto.hpp | 36 +++++++++++--- library/include/larra/xml_language.hpp | 19 ++++++++ tests/message.cpp | 51 ++++++++++++++++++++ tests/roster.cpp | 2 +- tests/xml_lang.cpp | 42 ++++++++++++++++ 7 files changed, 196 insertions(+), 15 deletions(-) create mode 100644 library/include/larra/message.hpp create mode 100644 library/include/larra/xml_language.hpp create mode 100644 tests/message.cpp create mode 100644 tests/xml_lang.cpp diff --git a/.forgejo/workflows/pr_check.yaml b/.forgejo/workflows/pr_check.yaml index b8f8f80..fb4a692 100644 --- a/.forgejo/workflows/pr_check.yaml +++ b/.forgejo/workflows/pr_check.yaml @@ -13,9 +13,9 @@ jobs: pacman -S --noconfirm cmake make gcc ninja meson pacman -S --noconfirm gtk4 gtkmm-4.0 boost spdlog fmt libxml++-5.0 gtest - - name: Setup environment - Install LLVM-19.1.3 + - name: Setup environment - Install LLVM-19.1.5 run: | - export LLVM_VER=19.1.3 + export LLVM_VER=19.1.5 echo "::group::Download LLVM-${LLVM_VER}" wget https://github.com/llvm/llvm-project/releases/download/llvmorg-${LLVM_VER}/LLVM-${LLVM_VER}-Linux-X64.tar.xz -O /LLVM-${LLVM_VER}-Linux-X64.tar.xz @@ -108,7 +108,7 @@ jobs: id: clang_format_check continue-on-error: true run: | - export LLVM_VER=19.1.3 + export LLVM_VER=19.1.5 export PATH="/home/LLVM-${LLVM_VER}/bin:${PATH}" cd ${{ github.workspace }} export REPO_FILES=$(cat repo_files_to_check.txt) @@ -148,7 +148,7 @@ jobs: id: clang_build continue-on-error: true run: | - export LLVM_VER=19.1.3 + export LLVM_VER=19.1.5 export PATH="/home/LLVM-${LLVM_VER}/bin:${PATH}" mkdir -p ${{ github.workspace }}/build_clang @@ -172,7 +172,7 @@ jobs: id: clang_tidy continue-on-error: true run: | - export LLVM_VER=19.1.3 + export LLVM_VER=19.1.5 export PATH="/home/LLVM-${LLVM_VER}/bin:${PATH}" cd ${{ github.workspace }} @@ -197,7 +197,7 @@ jobs: #- name: Clang unit tests with -fsanitize=address # run: | - # export LLVM_VER=19.1.3 + # export LLVM_VER=19.1.5 # export PATH="/home/LLVM-${LLVM_VER}/bin:${PATH}" # cd ${{ github.workspace }}/build_clang # ASAN_SYMBOLIZER_PATH=llvm-symbolizer ASAN_OPTIONS=detect_stack_use_after_return=1:check_initialization_order=1:detect_leaks=1:atexit=1:abort_on_error=1 ./larra_xmpp_tests diff --git a/library/include/larra/message.hpp b/library/include/larra/message.hpp new file mode 100644 index 0000000..76df40d --- /dev/null +++ b/library/include/larra/message.hpp @@ -0,0 +1,49 @@ +#pragma once +#include + +#include +#include +#include + +namespace larra::xmpp { + +template +struct Message { + static constexpr auto kDefaultName = "message"; + struct Body : std::string { + using std::string::basic_string; + constexpr Body(std::string str) : std::string{std::move(str)} { + } + static constexpr auto kDefaultName = "body"; + friend constexpr auto operator<<(xmlpp::Element* node, const Body& message) -> void { + node->add_child_text(message); + } + static constexpr auto Parse(xmlpp::Element* node) -> Body { + auto ptr = node->get_first_child_text(); + if(!ptr) { + throw std::runtime_error("Message::Body: [ Text node not found ]"); + } + return {ptr->get_content()}; + } + }; + static auto Parse(xmlpp::Element* element) -> Message { + return serialization::Parse(element); + } + friend auto operator<<(xmlpp::Element* element, const Message& message) -> void { + serialization::Serialize(element, message); + } + constexpr auto operator==(const Message& other) const -> bool = default; + From from; + To to; + std::string type; + std::optional id; + XmlLanguage language; + Body body; +}; + +template +struct serialization::SerializationConfigT> { + static constexpr auto kValue = serialization::SerializationConfig>{}; +}; + +} // namespace larra::xmpp diff --git a/library/include/larra/serialization/auto.hpp b/library/include/larra/serialization/auto.hpp index 79486eb..db7adfe 100644 --- a/library/include/larra/serialization/auto.hpp +++ b/library/include/larra/serialization/auto.hpp @@ -4,7 +4,6 @@ #include #include #include -#include #include #include #include @@ -16,10 +15,20 @@ template struct Tag {}; template -inline constexpr auto kSerializationConfig = std::monostate{}; +struct SerializationConfigT { + static constexpr auto kValue = std::monostate{}; +}; template -inline constexpr auto kDeserializationConfig = kSerializationConfig; +inline constexpr auto kSerializationConfig = SerializationConfigT::kValue; + +template +struct DeserializationConfigT { + static constexpr auto kValue = kSerializationConfig; +}; + +template +inline constexpr auto kDeserializationConfig = DeserializationConfigT::kValue; template constexpr auto Parse(xmlpp::Element* element, Tag = {}) -> T @@ -66,6 +75,13 @@ struct FieldInfo { return static_cast(Info::template kFieldName); } }(); + static inline const std::string kNamespace = [] { + if constexpr(requires { Info::template TupleElement::kDefaultNamespace; }) { + return Info::template TupleElement::kDefaultNamespace; + } else { + return std::string{}; + } + }(); }; template @@ -216,9 +232,13 @@ struct ElementSerializer, Config, Info> { template struct AttributeSerializer { static constexpr auto Parse(xmlpp::Element* element) -> T { - auto node = element->get_attribute(Info::kName); + auto node = element->get_attribute(Info::kName, Info::kNamespace); if(!node) { - throw AttributeParsingError(std::format("Attribute [{}: {}] parsing error", Info::kName, nameof::nameof_full_type())); + throw AttributeParsingError(std::format("Attribute [{}{}{}: {}] parsing error", + Info::kNamespace, + Info::kNamespace == std::string{} ? "" : ":", + Info::kName, + nameof::nameof_full_type())); } if constexpr(requires(std::string_view view) { T::Parse(view); }) { return T::Parse(node->get_value()); @@ -230,9 +250,9 @@ struct AttributeSerializer { if constexpr(requires { { ToString(obj) } -> std::convertible_to; }) { - element->set_attribute(Info::kName, ToString(obj)); + element->set_attribute(Info::kName, ToString(obj), Info::kNamespace); } else { - element->set_attribute(Info::kName, obj); + element->set_attribute(Info::kName, obj, Info::kNamespace); } } }; @@ -240,7 +260,7 @@ struct AttributeSerializer { template struct AttributeSerializer, Info> { static constexpr auto Parse(xmlpp::Element* element) -> std::optional { - auto node = element->get_attribute(Info::kName); + auto node = element->get_attribute(Info::kName, Info::kNamespace); return node ? std::optional{AttributeSerializer::Parse(element)} : std::nullopt; } static constexpr auto Serialize(xmlpp::Element* element, const std::optional& obj) { diff --git a/library/include/larra/xml_language.hpp b/library/include/larra/xml_language.hpp new file mode 100644 index 0000000..40b338e --- /dev/null +++ b/library/include/larra/xml_language.hpp @@ -0,0 +1,19 @@ +#pragma once +#include +namespace larra::xmpp { + +struct XmlLanguage : std::string { + static constexpr auto kDefaultName = "lang"; + static constexpr auto kDefaultNamespace = "xml"; + using std::string::basic_string; + constexpr XmlLanguage(std::string str) : std::string(std::move(str)) { + } + static constexpr auto Parse(std::string_view view) -> XmlLanguage { + return static_cast(view); + } + friend constexpr auto ToString(XmlLanguage value) -> std::string { + return std::move(static_cast(value)); + } +}; + +} // namespace larra::xmpp diff --git a/tests/message.cpp b/tests/message.cpp new file mode 100644 index 0000000..ba5524c --- /dev/null +++ b/tests/message.cpp @@ -0,0 +1,51 @@ +#include + +#include +#include + +namespace larra::xmpp { + +namespace { + +auto CreateTestData() { + auto doc = std::make_unique(); + auto node = doc->create_root_node("message"); + node->set_attribute("from", "user1@server.i2p"); + node->set_attribute("to", "user2@server.i2p"); + node->set_attribute("type", "chat"); + node->set_attribute("id", "1"); + node->set_attribute("lang", "en", "xml"); + + auto bodyNode = node->add_child_element("body"); + bodyNode->add_child_text("hello"); + return doc; +} + +constexpr Message kMessage{ + .from = {.username = "user1", .server = "server.i2p"}, + .to = {.username = "user2", .server = "server.i2p"}, + .type = "chat", + .id = "1", + .language = "en", + .body = "hello" // +}; + +} // namespace + +TEST(Parse, Message) { + auto doc = CreateTestData(); + auto node = doc->get_root_node(); + auto message = Serialization>::Parse(node); + + EXPECT_EQ(message, kMessage); +} + +TEST(Serialize, Message) { + auto expected = CreateTestData()->write_to_string(); + xmlpp::Document doc; + auto node = doc.create_root_node("message"); + Serialization>::Serialize(node, kMessage); + EXPECT_EQ(doc.write_to_string(), expected); +} + +} // namespace larra::xmpp diff --git a/tests/roster.cpp b/tests/roster.cpp index 6944a0c..75935fe 100644 --- a/tests/roster.cpp +++ b/tests/roster.cpp @@ -28,7 +28,7 @@ TEST(Roster, Print) { EXPECT_NO_THROW({ auto rosterStr = ToString(roster.payload); - EXPECT_EQ(kRosterPrintExpectedData.length(), rosterStr.capacity()); + EXPECT_EQ(kRosterPrintExpectedData.length(), rosterStr.length()); EXPECT_EQ(kRosterPrintExpectedData, rosterStr); }); } diff --git a/tests/xml_lang.cpp b/tests/xml_lang.cpp new file mode 100644 index 0000000..73c4c45 --- /dev/null +++ b/tests/xml_lang.cpp @@ -0,0 +1,42 @@ +#include + +#include +#include + +namespace larra::xmpp { + +namespace tests::message { + +struct SomeStruct { + static constexpr auto kDefaultName = "some"; + XmlLanguage language; + friend auto operator<<(xmlpp::Element*, const SomeStruct&) -> void; + static auto Parse(xmlpp::Element* node) -> SomeStruct; +}; + +} // namespace tests::message +template <> +constexpr auto serialization::kSerializationConfig = + serialization::SerializationConfig{}; + +namespace tests::message { + +auto SomeStruct::Parse(xmlpp::Element* element) -> SomeStruct { + return serialization::Parse(element); +} + +auto operator<<(xmlpp::Element* element, const SomeStruct& some) -> void { + serialization::Serialize(element, some); +}; + +} // namespace tests::message + +TEST(Parse, XmlLanguage) { + xmlpp::Document doc; + auto node = doc.create_root_node("some"); + node->set_attribute("lang", "en", "xml"); + auto value = Serialization::Parse(node); + EXPECT_EQ(value.language, "en"); +} + +} // namespace larra::xmpp