Features: iq::Bind and iq::Roster #4

Open
Ivan-lis wants to merge 3 commits from feature_roster_on_login into main
3 changed files with 101 additions and 19 deletions
Showing only changes of commit 77640de2e9 - Show all commits

View file

@ -0,0 +1,53 @@
#pragma once
#include <libxml++/libxml++.h>
#include <spdlog/spdlog.h>
#include <larra/iq.hpp>
#include <larra/jid.hpp>
#include <optional>
namespace larra::xmpp::iq {
struct Bind {
static constexpr auto kDefaultName = "bind";
static constexpr auto kDefaultNamespace = "urn:ietf:params:xml:ns:xmpp-bind";
std::optional<FullJid> 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<const xmlpp::Element*>(jid_node);
Review

You can create a structure that contains jid as an attribute and use automatic generation of serialization and deserialization

You can create a structure that contains jid as an attribute and use automatic generation of serialization and deserialization
Review

I tried, but roster result should has 'jid' attr, while with Serialization with BareJid I got <NO_NAME username="n" server="s" >

Expected ResultRoster

<iq id='bv1bs71f'
       to='juliet@example.com/chamber'
       type='result'>
    <query xmlns='jabber:iq:roster' ver='ver7'>
      <item jid='nurse@example.com'/>
      <item jid='romeo@example.net'/>
    </query>
  </iq>
I tried, but roster result should has **'jid' attr,** while with Serialization with BareJid I got `<NO_NAME username="n" server="s" >` Expected ResultRoster ```json <iq id='bv1bs71f' to='juliet@example.com/chamber' type='result'> <query xmlns='jabber:iq:roster' ver='ver7'> <item jid='nurse@example.com'/> <item jid='romeo@example.net'/> </query> </iq> ```
Review

I tried, but roster result should has 'jid' attr, while with Serialization with BareJid I got <NO_NAME username="n" server="s" >

Expected ResultRoster

<iq id='bv1bs71f'
       to='juliet@example.com/chamber'
       type='result'>
    <query xmlns='jabber:iq:roster' ver='ver7'>
      <item jid='nurse@example.com'/>
      <item jid='romeo@example.net'/>
    </query>
  </iq>

See how it's done in SomeStruct5 in tests/serialization.cpp

> I tried, but roster result should has **'jid' attr,** while with Serialization with BareJid I got `<NO_NAME username="n" server="s" >` > > Expected ResultRoster > ```json > <iq id='bv1bs71f' > to='juliet@example.com/chamber' > type='result'> > <query xmlns='jabber:iq:roster' ver='ver7'> > <item jid='nurse@example.com'/> > <item jid='romeo@example.net'/> > </query> > </iq> > ``` > > See how it's done in SomeStruct5 in tests/serialization.cpp
Review

Interesting, looks like last time I did something wrong and got <username="n" server="s"> instead. Will try t use BareJid

Interesting, looks like last time I did something wrong and got <username="n" server="s"> instead. Will try t use BareJid
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)};
}
};
using SetBind = Set<Bind>;
using ResultBind = Result<Bind>;
using IqBind = Iq<Bind>;
inline auto MakeSetBind() {
return SetBind{.id = "1", .payload = Bind{}};
Ivan-lis marked this conversation as resolved Outdated

For what?

For what?
}
} // namespace larra::xmpp::iq

View file

@ -2,6 +2,7 @@
#include <larra/serialization.hpp>
#include <larra/stream_error.hpp>
#include <larra/utils.hpp>
#include <optional>
#include <string>
namespace larra::xmpp {
@ -11,6 +12,8 @@ namespace iq {
template <auto& Name, typename PayloadType>
struct BaseImplWithPayload {
std::string id;
std::optional<std::string> from{};
Review

Why std::string and not a more limited type like Jid ?

Why std::string and not a more limited type like Jid ?
std::optional<std::string> to{};
PayloadType payload;
static const inline std::string kName = Name;
static constexpr auto kDefaultName = "iq";
@ -19,16 +22,32 @@ struct BaseImplWithPayload {
[[nodiscard]] constexpr auto Id(this Self&& self, std::string id) -> std::decay_t<Self> {
return utils::FieldSetHelper::With<"id", BaseImplWithPayload>(std::forward<Self>(self), std::move(id));
}
template <typename Self>
[[nodiscard]] constexpr auto To(this Self&& self, std::string to) -> std::decay_t<Self> {
return utils::FieldSetHelper::With<"to", BaseImplWithPayload>(std::forward<Self>(self), std::move(to));
}
template <typename Self>
[[nodiscard]] constexpr auto From(this Self&& self, std::string from) -> std::decay_t<Self> {
return utils::FieldSetHelper::With<"from", BaseImplWithPayload>(std::forward<Self>(self), std::move(from));
}
template <typename NewPayloadType, typename Self>
[[nodiscard]] constexpr auto Payload(this Self&& self, NewPayloadType value) {
return utils::FieldSetHelper::With<"payload", BaseImplWithPayload, false>(std::forward<Self>(self), std::move(value));
}
friend constexpr auto operator<<(xmlpp::Element* element, const BaseImplWithPayload& self) {
element->set_attribute("id", self.id);
if (self.to) {
element->set_attribute("to", *self.to);
}
if (self.from) {
element->set_attribute("from", *self.from);
}
element->set_attribute("type", kName);
using S = Serialization<PayloadType>;
S::Serialize(element->add_child_element(S::kDefaultName, S::kPrefix), self.payload);
}
[[nodiscard]] static constexpr auto Parse(xmlpp::Element* element) -> BaseImplWithPayload {
auto node = element->get_attribute("type");
if(!node) {
@ -41,6 +60,9 @@ struct BaseImplWithPayload {
if(!idNode) {
throw std::runtime_error("Not found attribute id for parse Iq");
}
auto from = element->get_attribute("from");
auto to = element->get_attribute("to");
using S = Serialization<PayloadType>;
auto payload = element->get_first_child(S::kDefaultName);
@ -51,7 +73,11 @@ struct BaseImplWithPayload {
if(!payload2) {
throw std::runtime_error("Invalid payload for parse Iq");
}
return {.id = idNode->get_value(), .payload = S::Parse(payload2)};
return {
.id = idNode->get_value(),
.from = (from ? std::optional{from->get_value()} : std::nullopt),
.to = (to ? std::optional{to->get_value()} : std::nullopt),
.payload = S::Parse(payload2)};
}
};
static constexpr auto kGetName = "get";

View file

@ -7,24 +7,6 @@
namespace larra::xmpp {
struct BareJid {
std::string username;
std::string server;
[[nodiscard]] static auto Parse(std::string_view jid) -> BareJid;
friend auto ToString(const BareJid& jid) -> std::string;
constexpr auto operator==(const BareJid&) const -> bool = default;
template <typename Self>
[[nodiscard]] constexpr auto Username(this Self&& self, std::string username) -> std::decay_t<Self> {
return utils::FieldSetHelper::With<"username", BareJid>(std::forward<Self>(self), std::move(username));
}
template <typename Self>
[[nodiscard]] constexpr auto Server(this Self&& self, std::string server) -> std::decay_t<Self> {
return utils::FieldSetHelper::With<"server", BareJid>(std::forward<Self>(self), std::move(server));
}
};
struct BareResourceJid {
std::string server;
std::string resource;
@ -69,6 +51,27 @@ struct FullJid {
}
};
struct BareJid {
std::string username;
std::string server;
constexpr operator FullJid() const {
return FullJid{.username = username, .server = server, .resource = ""};
Ivan-lis marked this conversation as resolved Outdated

Extra copying if there is an rvalue. Better to forward depending on the type with which it is called.

Extra copying if there is an rvalue. Better to forward depending on the type with which it is called.
}
[[nodiscard]] static auto Parse(std::string_view jid) -> BareJid;
friend auto ToString(const BareJid& jid) -> std::string;
constexpr auto operator==(const BareJid&) const -> bool = default;
template <typename Self>
[[nodiscard]] constexpr auto Username(this Self&& self, std::string username) -> std::decay_t<Self> {
return utils::FieldSetHelper::With<"username", BareJid>(std::forward<Self>(self), std::move(username));
}
template <typename Self>
[[nodiscard]] constexpr auto Server(this Self&& self, std::string server) -> std::decay_t<Self> {
return utils::FieldSetHelper::With<"server", BareJid>(std::forward<Self>(self), std::move(server));
}
};
using JidVariant = std::variant<BareJid, BareResourceJid, FullJid>;
struct Jid : JidVariant {