Add message serialization and deserialization
Some checks failed
PR Check / on-push-commit-check (push) Has been cancelled

This commit is contained in:
sha512sum 2024-12-21 18:56:09 +11:00
parent 7f5c9cfd49
commit 9bccfe3025
5 changed files with 171 additions and 9 deletions

View 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

View file

@ -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) {

51
tests/message.cpp Normal file
View 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

View file

@ -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
View 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