diff --git a/library/include/larra/bind.hpp b/library/include/larra/bind.hpp index bdbdb90..6079f36 100644 --- a/library/include/larra/bind.hpp +++ b/library/include/larra/bind.hpp @@ -14,37 +14,11 @@ struct Bind { std::optional jid; - friend constexpr auto operator<<(xmlpp::Element* element, const Bind& bind) { - element->set_attribute("xmlns", Bind::kDefaultNamespace); - - if(bind.jid) { - auto* jid_el = element->add_child_element("jid"); - jid_el->add_child_text(ToString(*bind.jid)); - } - } - [[nodiscard]] static constexpr auto Parse(xmlpp::Element* element) -> Bind { - const auto* jid_node = element->get_first_child("jid"); - if(!jid_node) { - SPDLOG_DEBUG("No Jid Node at Iq::Bind"); - return {}; - } - - auto* jid_el = dynamic_cast(jid_node); - if(!jid_el) { - throw std::runtime_error("dynamic_cast to const xmlpp::Element* failed"); - } - - const auto* text = jid_el->get_first_child_text(); - if(!jid_el) { - throw std::runtime_error("No text at Iq::Bind jid child"); - } - - return {.jid = (jid_node ? std::optional{FullJid::Parse(text->get_content())} : std::nullopt)}; - } + friend auto operator<<(xmlpp::Element* element, const Bind& bind) -> void; + [[nodiscard]] static auto Parse(xmlpp::Element* element) -> Bind; }; using SetBind = Set; using ResultBind = Result; -using IqBind = Iq; } // namespace larra::xmpp::iq diff --git a/library/include/larra/client/client.hpp b/library/include/larra/client/client.hpp index 9b9890b..876f13e 100644 --- a/library/include/larra/client/client.hpp +++ b/library/include/larra/client/client.hpp @@ -97,7 +97,7 @@ struct Client { auto UpdateListOfContacts() -> boost::asio::awaitable { SPDLOG_INFO("Send IQ: Get::Roster"); - co_await this->Send(::iq::GetRoster{.id = "1", .from = std::format("{}@{}", "invalid_user", jid.server), .payload = {}}); + co_await this->Send(::iq::GetRoster{.id = "1", .from = jid, .payload = {}}); const auto get_roster_response = co_await connection.template Read>(); std::visit(utempl::Overloaded( diff --git a/library/include/larra/iq.hpp b/library/include/larra/iq.hpp index 7a3a811..0ed886b 100644 --- a/library/include/larra/iq.hpp +++ b/library/include/larra/iq.hpp @@ -1,4 +1,5 @@ #pragma once +#include #include #include #include @@ -12,8 +13,8 @@ namespace iq { template struct BaseImplWithPayload { std::string id; - std::optional from{}; - std::optional to{}; + std::optional from{}; + std::optional to{}; PayloadType payload; static const inline std::string kName = Name; static constexpr auto kDefaultName = "iq"; @@ -23,11 +24,11 @@ struct BaseImplWithPayload { return utils::FieldSetHelper::With<"id", BaseImplWithPayload>(std::forward(self), std::move(id)); } template - [[nodiscard]] constexpr auto To(this Self&& self, std::string to) -> std::decay_t { + [[nodiscard]] constexpr auto To(this Self&& self, Jid to) -> std::decay_t { return utils::FieldSetHelper::With<"to", BaseImplWithPayload>(std::forward(self), std::move(to)); } template - [[nodiscard]] constexpr auto From(this Self&& self, std::string from) -> std::decay_t { + [[nodiscard]] constexpr auto From(this Self&& self, Jid from) -> std::decay_t { return utils::FieldSetHelper::With<"from", BaseImplWithPayload>(std::forward(self), std::move(from)); } template @@ -38,10 +39,10 @@ struct BaseImplWithPayload { element->set_attribute("id", self.id); if(self.to) { - element->set_attribute("to", *self.to); + element->set_attribute("to", ToString(*self.to)); } if(self.from) { - element->set_attribute("from", *self.from); + element->set_attribute("from", ToString(*self.from)); } element->set_attribute("type", kName); using S = Serialization; @@ -86,8 +87,8 @@ struct BaseImplWithPayload { throw std::runtime_error("Invalid payload for parse Iq"); } return {.id = idNode->get_value(), - .from = (from ? std::optional{from->get_value()} : std::nullopt), - .to = (to ? std::optional{to->get_value()} : std::nullopt), + .from = (from ? std::optional{Jid::Parse(from->get_value())} : std::nullopt), + .to = (to ? std::optional{Jid::Parse(to->get_value())} : std::nullopt), .payload = S::Parse(payload2)}; } }; diff --git a/library/include/larra/roster.hpp b/library/include/larra/roster.hpp index c0635da..a0f04f0 100644 --- a/library/include/larra/roster.hpp +++ b/library/include/larra/roster.hpp @@ -11,18 +11,28 @@ namespace larra::xmpp::iq { +struct RosterItem { + BareJid jid; + friend constexpr auto ToString(const RosterItem& item) { + return ToString(item.jid); + } + constexpr auto operator==(const RosterItem&) const -> bool = default; + friend auto operator<<(xmlpp::Element* element, const RosterItem& item) -> void; + [[nodiscard]] static auto Parse(xmlpp::Element* element) -> RosterItem; +}; + struct Roster { static constexpr auto kDefaultName = "query"; static constexpr auto kDefaultNamespace = "jabber:iq:roster"; - std::vector items; + std::vector items; friend auto ToString(const Roster& roster) -> std::string { static constexpr std::string_view prefix = "Roster: [\n\t"; static constexpr std::string_view suffix = "]"; // \n\r\t std::size_t total_length = std::ranges::fold_left(roster.items | std::views::transform([](const auto& el) { - return larra::xmpp::utils::AccumulateFieldLength(el) + 3; + return larra::xmpp::utils::AccumulateFieldLength(el.jid) + 3; }), prefix.length() + suffix.length(), std::plus<>{}); @@ -36,37 +46,11 @@ struct Roster { } return s += suffix; } - friend constexpr auto operator<<(xmlpp::Element* element, const Roster& roster) { - element->set_attribute("xmlns", Roster::kDefaultNamespace); - for(const auto& item : roster.items) { - element->add_child_element("item")->set_attribute("jid", ToString(item)); - } - } - [[nodiscard]] static constexpr auto Parse(xmlpp::Element* element) -> Roster { - const auto& item_nodes = element->get_children("item"); - if(item_nodes.empty()) { - SPDLOG_DEBUG("No items at Iq::Roster"); - } - - return {.items = item_nodes | std::views::transform([](const xmlpp::Node* node) { - auto item_element = dynamic_cast(node); - if(!item_element) { - throw std::runtime_error("Can't convert xmlpp::Node to xmlpp::Element"); - } - - auto jid_ptr = item_element->get_attribute("jid"); - if(!jid_ptr) { - throw std::runtime_error("Not found attribute 'jid' for parse Roster item"); - } - - return BareJid::Parse(jid_ptr->get_value()); - }) | - std::ranges::to>()}; - } + friend auto operator<<(xmlpp::Element* element, const Roster& roster) -> void; + [[nodiscard]] static auto Parse(xmlpp::Element* element) -> Roster; }; using GetRoster = Get; using ResultRoster = Result; -using IqRoster = Iq; } // namespace larra::xmpp::iq diff --git a/library/include/larra/serialization/auto.hpp b/library/include/larra/serialization/auto.hpp index b469dae..79486eb 100644 --- a/library/include/larra/serialization/auto.hpp +++ b/library/include/larra/serialization/auto.hpp @@ -115,7 +115,7 @@ struct Config : V { requires(!HasParse) : V(AttributeConfig{}) { } - constexpr auto Base() const -> const V& { + [[nodiscard]] constexpr auto Base() const -> const V& { return static_cast(*this); } using type = T; diff --git a/library/include/larra/utils.hpp b/library/include/larra/utils.hpp index 214e676..7534947 100644 --- a/library/include/larra/utils.hpp +++ b/library/include/larra/utils.hpp @@ -302,11 +302,11 @@ concept LengthCalculatable = requires(const T& obj) { template auto AccumulateFieldLength(const T& obj) -> std::size_t { - std::size_t total_length = 0; + std::size_t totalLength = 0; boost::pfr::for_each_field(obj, [&](const LengthCalculatable auto& field) { - total_length += field.length(); // Accumulate length of each field + totalLength += field.length(); // Accumulate length of each field }); - return total_length; + return totalLength; } } // namespace larra::xmpp::utils diff --git a/library/src/bind.cpp b/library/src/bind.cpp new file mode 100644 index 0000000..b82f72b --- /dev/null +++ b/library/src/bind.cpp @@ -0,0 +1,31 @@ +#include + +namespace larra::xmpp::iq { +auto operator<<(xmlpp::Element* element, const Bind& bind) -> void { + element->set_attribute("xmlns", Bind::kDefaultNamespace); + + if(bind.jid) { + auto* jid_el = element->add_child_element("jid"); + jid_el->add_child_text(ToString(*bind.jid)); + } +} +[[nodiscard]] auto Bind::Parse(xmlpp::Element* element) -> Bind { + const auto* jid_node = element->get_first_child("jid"); + if(!jid_node) { + SPDLOG_DEBUG("No Jid Node at Iq::Bind"); + return {}; + } + + auto* jid_el = dynamic_cast(jid_node); + if(!jid_el) { + throw std::runtime_error("dynamic_cast to const xmlpp::Element* failed"); + } + + const auto* text = jid_el->get_first_child_text(); + if(!jid_el) { + throw std::runtime_error("No text at Iq::Bind jid child"); + } + + return {.jid = (jid_node ? std::optional{FullJid::Parse(text->get_content())} : std::nullopt)}; +} +} // namespace larra::xmpp::iq diff --git a/library/src/roster.cpp b/library/src/roster.cpp new file mode 100644 index 0000000..7b01dcc --- /dev/null +++ b/library/src/roster.cpp @@ -0,0 +1,30 @@ +#include +#include + +namespace larra::xmpp::serialization { +namespace iq = larra::xmpp::iq; + +template <> +constexpr auto kSerializationConfig = SerializationConfig{}; +template <> +constexpr auto kSerializationConfig = SerializationConfig{}.With<"items">({Config>{}}); +} // namespace larra::xmpp::serialization + +namespace larra::xmpp::iq { +namespace S = larra::xmpp::serialization; + +auto operator<<(xmlpp::Element* element, const RosterItem& self) -> void { + S::Serialize(element, self); +} +auto RosterItem::Parse(xmlpp::Element* element) -> RosterItem { + return S::Parse(element); +} + +auto operator<<(xmlpp::Element* element, const Roster& self) -> void { + element->set_attribute("xmlns", Roster::kDefaultNamespace); + S::Serialize(element, self); +} +auto Roster::Parse(xmlpp::Element* element) -> Roster { + return S::Parse(element); +} +} // namespace larra::xmpp::iq diff --git a/tests/roster.cpp b/tests/roster.cpp index b30e15b..6944a0c 100644 --- a/tests/roster.cpp +++ b/tests/roster.cpp @@ -7,29 +7,29 @@ namespace larra::xmpp { TEST(Roster, SerializeAndParse) { FullJid jid{.username = "test", .server = "server", .resource = "res"}; // NOLINT - auto roster = iq::GetRoster{.id = "1", .from = ToString(jid), .payload = iq::Roster{.items = {{"u1", "s1"}, {"u2", "s2"}, {"u3", "s3"}}}}; + auto roster = iq::GetRoster{.id = "1", .from = jid, .payload = iq::Roster{.items = {{"u1", "s1"}, {"u2", "s2"}, {"u3", "s3"}}}}; xmlpp::Document doc; auto node = doc.create_root_node("iq"); node << roster; - auto parse_res = decltype(roster)::Parse(node); + auto parseRes = decltype(roster)::Parse(node); - ASSERT_EQ(roster.payload.items.size(), parse_res.payload.items.size()); - for(const auto& [idx, expect_el, parsed_el] : std::views::zip(std::views::iota(0), roster.payload.items, parse_res.payload.items)) { - EXPECT_EQ(expect_el, parsed_el) << "Mismatched on idx: " << idx; + ASSERT_EQ(roster.payload.items.size(), parseRes.payload.items.size()); + for(const auto& [idx, expectEl, parsedEl] : std::views::zip(std::views::iota(0), roster.payload.items, parseRes.payload.items)) { + EXPECT_EQ(expectEl, parsedEl) << "Mismatched on idx: " << idx; } } static constexpr std::string_view kRosterPrintExpectedData = "Roster: [\n\tu1@s1\n\tu2@s2\n\tu3@s3\n\t]"; TEST(Roster, Print) { FullJid jid{.username = "test", .server = "server", .resource = "res"}; // NOLINT - auto roster = iq::GetRoster{.id = "1", .from = ToString(jid), .payload = iq::Roster{.items = {{"u1", "s1"}, {"u2", "s2"}, {"u3", "s3"}}}}; + auto roster = iq::GetRoster{.id = "1", .from = jid, .payload = iq::Roster{.items = {{"u1", "s1"}, {"u2", "s2"}, {"u3", "s3"}}}}; EXPECT_NO_THROW({ - auto roster_str = ToString(roster.payload); - EXPECT_EQ(kRosterPrintExpectedData.length(), roster_str.capacity()); - EXPECT_EQ(kRosterPrintExpectedData, roster_str); + auto rosterStr = ToString(roster.payload); + EXPECT_EQ(kRosterPrintExpectedData.length(), rosterStr.capacity()); + EXPECT_EQ(kRosterPrintExpectedData, rosterStr); }); } diff --git a/tests/serialization.cpp b/tests/serialization.cpp index 10b69cd..abe41b3 100644 --- a/tests/serialization.cpp +++ b/tests/serialization.cpp @@ -5,9 +5,6 @@ #include #include #include -#include - -#include "utempl/utils.hpp" using namespace std::literals;