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..b6da806 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"; @@ -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{BareJid::Parse(from->get_value())} : std::nullopt), + .to = (to ? std::optional{BareJid::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..c3389b0 100644 --- a/library/include/larra/roster.hpp +++ b/library/include/larra/roster.hpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -11,18 +12,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 constexpr auto operator<<(xmlpp::Element* element, const RosterItem& item); + [[nodiscard]] static constexpr 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,33 +47,8 @@ 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 constexpr auto operator<<(xmlpp::Element* element, const Roster& roster); + [[nodiscard]] static constexpr auto Parse(xmlpp::Element* element) -> Roster; }; using GetRoster = Get; @@ -70,3 +56,31 @@ using ResultRoster = Result; using IqRoster = Iq; } // namespace larra::xmpp::iq + +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; + +constexpr auto operator<<(xmlpp::Element* element, const RosterItem& self) { + S::Serialize(element, self); +} +constexpr auto RosterItem::Parse(xmlpp::Element* element) -> RosterItem { + return S::Parse(element); +} + +constexpr auto operator<<(xmlpp::Element* element, const Roster& self) { + element->set_attribute("xmlns", Roster::kDefaultNamespace); + S::Serialize(element, self); +} +constexpr auto Roster::Parse(xmlpp::Element* element) -> Roster { + return S::Parse(element); +} +} // 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/tests/roster.cpp b/tests/roster.cpp index b30e15b..1a21438 100644 --- a/tests/roster.cpp +++ b/tests/roster.cpp @@ -7,7 +7,7 @@ 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"); @@ -24,7 +24,7 @@ TEST(Roster, SerializeAndParse) { 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);