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/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