Add message serialization and deserialization
All checks were successful
PR Check / on-push-commit-check (push) Successful in 12m18s
All checks were successful
PR Check / on-push-commit-check (push) Successful in 12m18s
This commit is contained in:
parent
7f5c9cfd49
commit
64e2aeb8aa
7 changed files with 196 additions and 15 deletions
|
@ -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
|
||||
|
|
49
library/include/larra/message.hpp
Normal file
49
library/include/larra/message.hpp
Normal file
|
@ -0,0 +1,49 @@
|
|||
#pragma once
|
||||
#include <libxml++/libxml++.h>
|
||||
|
||||
#include <larra/serialization/auto.hpp>
|
||||
#include <larra/xml_language.hpp>
|
||||
#include <string>
|
||||
|
||||
namespace larra::xmpp {
|
||||
|
||||
template <typename From, typename To>
|
||||
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<Message>(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<std::string> id;
|
||||
XmlLanguage language;
|
||||
Body body;
|
||||
};
|
||||
|
||||
template <typename From, typename To>
|
||||
struct serialization::SerializationConfigT<Message<From, To>> {
|
||||
static constexpr auto kValue = serialization::SerializationConfig<Message<From, To>>{};
|
||||
};
|
||||
|
||||
} // namespace larra::xmpp
|
|
@ -4,7 +4,6 @@
|
|||
#include <boost/pfr.hpp>
|
||||
#include <larra/serialization.hpp>
|
||||
#include <larra/serialization/error.hpp>
|
||||
#include <ranges>
|
||||
#include <utempl/constexpr_string.hpp>
|
||||
#include <utempl/tuple.hpp>
|
||||
#include <utempl/utils.hpp>
|
||||
|
@ -16,10 +15,20 @@ template <typename T>
|
|||
struct Tag {};
|
||||
|
||||
template <typename T>
|
||||
inline constexpr auto kSerializationConfig = std::monostate{};
|
||||
struct SerializationConfigT {
|
||||
static constexpr auto kValue = std::monostate{};
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
inline constexpr auto kDeserializationConfig = kSerializationConfig<T>;
|
||||
inline constexpr auto kSerializationConfig = SerializationConfigT<T>::kValue;
|
||||
|
||||
template <typename T>
|
||||
struct DeserializationConfigT {
|
||||
static constexpr auto kValue = kSerializationConfig<T>;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
inline constexpr auto kDeserializationConfig = DeserializationConfigT<T>::kValue;
|
||||
|
||||
template <typename T>
|
||||
constexpr auto Parse(xmlpp::Element* element, Tag<T> = {}) -> T
|
||||
|
@ -66,6 +75,13 @@ struct FieldInfo {
|
|||
return static_cast<std::string>(Info::template kFieldName<Element>);
|
||||
}
|
||||
}();
|
||||
static inline const std::string kNamespace = [] {
|
||||
if constexpr(requires { Info::template TupleElement<Element>::kDefaultNamespace; }) {
|
||||
return Info::template TupleElement<Element>::kDefaultNamespace;
|
||||
} else {
|
||||
return std::string{};
|
||||
}
|
||||
}();
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
|
@ -216,9 +232,13 @@ struct ElementSerializer<std::vector<T>, Config, Info> {
|
|||
template <typename T, typename Info>
|
||||
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<T>()));
|
||||
throw AttributeParsingError(std::format("Attribute [{}{}{}: {}] parsing error",
|
||||
Info::kNamespace,
|
||||
Info::kNamespace == std::string{} ? "" : ":",
|
||||
Info::kName,
|
||||
nameof::nameof_full_type<T>()));
|
||||
}
|
||||
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<const std::string&>;
|
||||
}) {
|
||||
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 <typename T, typename Info>
|
||||
struct AttributeSerializer<std::optional<T>, Info> {
|
||||
static constexpr auto Parse(xmlpp::Element* element) -> std::optional<T> {
|
||||
auto node = element->get_attribute(Info::kName);
|
||||
auto node = element->get_attribute(Info::kName, Info::kNamespace);
|
||||
return node ? std::optional{AttributeSerializer<T, Info>::Parse(element)} : std::nullopt;
|
||||
}
|
||||
static constexpr auto Serialize(xmlpp::Element* element, const std::optional<T>& obj) {
|
||||
|
|
19
library/include/larra/xml_language.hpp
Normal file
19
library/include/larra/xml_language.hpp
Normal file
|
@ -0,0 +1,19 @@
|
|||
#pragma once
|
||||
#include <string>
|
||||
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<std::string>(view);
|
||||
}
|
||||
friend constexpr auto ToString(XmlLanguage value) -> std::string {
|
||||
return std::move(static_cast<std::string&>(value));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace larra::xmpp
|
51
tests/message.cpp
Normal file
51
tests/message.cpp
Normal file
|
@ -0,0 +1,51 @@
|
|||
#include <gtest/gtest.h>
|
||||
|
||||
#include <larra/jid.hpp>
|
||||
#include <larra/message.hpp>
|
||||
|
||||
namespace larra::xmpp {
|
||||
|
||||
namespace {
|
||||
|
||||
auto CreateTestData() {
|
||||
auto doc = std::make_unique<xmlpp::Document>();
|
||||
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<BareJid, BareJid> 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<Message<BareJid, BareJid>>::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<Message<BareJid, BareJid>>::Serialize(node, kMessage);
|
||||
EXPECT_EQ(doc.write_to_string(), expected);
|
||||
}
|
||||
|
||||
} // namespace larra::xmpp
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
|
42
tests/xml_lang.cpp
Normal file
42
tests/xml_lang.cpp
Normal file
|
@ -0,0 +1,42 @@
|
|||
#include <gtest/gtest.h>
|
||||
|
||||
#include <larra/serialization/auto.hpp>
|
||||
#include <larra/xml_language.hpp>
|
||||
|
||||
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<tests::message::SomeStruct> =
|
||||
serialization::SerializationConfig<tests::message::SomeStruct>{};
|
||||
|
||||
namespace tests::message {
|
||||
|
||||
auto SomeStruct::Parse(xmlpp::Element* element) -> SomeStruct {
|
||||
return serialization::Parse<SomeStruct>(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<tests::message::SomeStruct>::Parse(node);
|
||||
EXPECT_EQ(value.language, "en");
|
||||
}
|
||||
|
||||
} // namespace larra::xmpp
|
Loading…
Reference in a new issue