diff --git a/src/lib/jid.cpp b/src/lib/jid.cpp index 21f5b33..4d06fc7 100644 --- a/src/lib/jid.cpp +++ b/src/lib/jid.cpp @@ -4,72 +4,144 @@ import fmt; namespace larra::xmpp { -export struct Jid { - [[nodiscard]] static auto Parse(std::string_view jid) -> Jid { - Jid response; +export struct BareJid; + +auto ParseBareJid(std::string_view, std::size_t) -> BareJid; + +export struct BareJid { + std::string username; + std::string server; + + [[nodiscard]] friend auto ToString(const BareJid& jid) -> std::string { + return std::format("{}@{}", jid.username, jid.server); + } + + template + [[nodiscard]] auto Username(this Self&& self, std::string username) -> BareJid { + return {std::move(username), std::forward_like(self.resource)}; + } + + template + [[nodiscard]] auto Server(this Self&& self, std::string server) -> BareJid { + return {std::forward_like(self.server), std::move(server)}; + } + + [[nodiscard]] static auto Parse(std::string_view jid) -> BareJid { const auto at = jid.find('@'); - const auto slash = jid.find('/', at == std::string_view::npos ? 0 : at); - - if(at != std::string_view::npos) { - response.username = jid.substr(0, at); - response.server = jid.substr(at + 1, (slash == std::string_view::npos ? jid.size() : slash) - (at + 1)); - } else { - response.server = jid.substr(0, slash); + if(at == std::string_view::npos) { + throw std::invalid_argument("Invalid string for jid"); } - if(slash != std::string_view::npos) { - response.resource = jid.substr(slash + 1); - } - - return response; + return ParseBareJid(jid, at); } +}; - template - [[nodiscard]] auto Username(this Self&& self, std::string username) -> Jid { - return {std::forward_like(self.server), std::move(username), std::forward_like(self.resource)}; - } +auto ParseBareJid(std::string_view jid, std::size_t at) -> BareJid { + return {static_cast(jid.substr(0, at)), static_cast(jid.substr(at + 1))}; +} - template - [[nodiscard]] auto Server(this Self&& self, std::string server) -> Jid { - return {std::move(server), std::forward_like(self.username), std::forward_like(self.resource)}; - } +export struct BareResourceJid; - template - [[nodiscard]] auto Resource(this Self&& self, std::string resource) -> Jid { - return {std::forward_like(self.server), std::forward_like(self.username), std::move(resource)}; - } +auto ParseBareResourceJid(std::string_view, std::size_t) -> BareResourceJid; - [[nodiscard]] auto IsValid() const -> bool { - return this->server && this->username || this->resource; - } +export struct BareResourceJid { + std::string server; + std::string resource; - [[nodiscard]] auto Username() const -> std::string_view { - return *this->username; - } - - [[nodiscard]] auto Server() const -> std::string_view { - return *this->server; - } - - [[nodiscard]] auto Resource() const -> std::string_view { - return *this->resource; - } - - [[nodiscard]] explicit operator std::string() const { - if(!IsValid()) { - throw std::invalid_argument("Invalid jid obj"); - } - return !this->username ? std::format("{}/{}", *this->server, *this->resource) - : !this->resource ? std::format("{}@{}", *this->username, *this->server) - : std::format("{}@{}/{}", *this->username, *this->server, *this->resource); - } - [[nodiscard]] auto ToString() const -> std::string { - return static_cast(*this); + [[nodiscard]] friend auto ToString(const BareResourceJid& jid) -> std::string { + return std::format("{}/{}", jid.server, jid.resource); }; - std::optional server{}; - std::optional username{}; - std::optional resource{}; + template + [[nodiscard]] auto Server(this Self&& self, std::string server) -> BareResourceJid { + return {std::move(server), std::forward_like(self.resource)}; + }; + + template + [[nodiscard]] auto Resource(this Self&& self, std::string resource) -> BareResourceJid { + return {std::forward_like(self.server), std::move(resource)}; + }; + + [[nodiscard]] static auto Parse(std::string_view jid) -> BareResourceJid { + const auto slash = jid.find('/'); + if(slash == std::string_view::npos) { + throw std::invalid_argument("Invalid string for jid"); + }; + + return ParseBareResourceJid(jid, slash); + }; +}; + +auto ParseBareResourceJid(std::string_view jid, std::size_t slash) -> BareResourceJid { + return {static_cast(jid.substr(0, slash)), static_cast(jid.substr(slash + 1))}; +} + +export struct FullJid; + +auto ParseFullJid(std::string_view jid, std::size_t at, std::size_t slash) -> FullJid; + +export struct FullJid { + std::string username; + std::string server; + std::string resource; + + [[nodiscard]] friend auto ToString(const FullJid& jid) -> std::string { + return std::format("{}@{}/{}", jid.username, jid.server, jid.resource); + }; + + template + [[nodiscard]] auto Username(this Self&& self, std::string username) -> FullJid { + return {std::move(username), std::forward_like(self.server), std::forward_like(self.resource)}; + }; + + template + [[nodiscard]] auto Server(this Self&& self, std::string server) -> FullJid { + return {std::forward_like(self.username), std::move(server), std::forward_like(self.resource)}; + }; + + template + [[nodiscard]] auto Resource(this Self&& self, std::string resource) -> FullJid { + return {std::forward_like(self.username), std::forward_like(self.server), std::move(resource)}; + }; + + [[nodiscard]] static auto Parse(std::string_view jid) -> FullJid { + const auto at = jid.find('@'); + const auto slash = jid.find('/', at); + if(at == std::string_view::npos || slash == std::string_view::npos) { + throw std::invalid_argument("Invalid string for jid"); + }; + + return ParseFullJid(jid, at, slash); + }; +}; + +auto ParseFullJid(std::string_view jid, std::size_t at, std::size_t slash) -> FullJid { + return {static_cast(jid.substr(0, at)), + static_cast(jid.substr(at + 1, slash - at - 1)), + static_cast(jid.substr(slash + 1))}; +} +using JidVariant = std::variant; + +export struct Jid : JidVariant { + using JidVariant::variant; + [[nodiscard]] static auto Parse(std::string_view jid) -> Jid { + const auto at = jid.find('@'); + const auto slash = jid.find('/', at == std::string_view::npos ? 0 : at); + if(at == std::string_view::npos) { + return ParseBareResourceJid(jid, slash); + } + if(slash == std::string_view::npos) { + return ParseBareJid(jid, at); + } + return ParseFullJid(jid, at, slash); + } + [[nodiscard]] friend auto ToString(const Jid& jid) -> std::string { + return std::visit( + [](const auto& jid) -> std::string { + return ToString(jid); + }, + jid); + }; }; } // namespace larra::xmpp diff --git a/src/lib/library.cpp b/src/lib/library.cpp index dd919bf..ab7903a 100644 --- a/src/lib/library.cpp +++ b/src/lib/library.cpp @@ -1,2 +1,2 @@ export module larra.library; -export import larra.jid; +export import larra.library.jid; diff --git a/tests/jid.cpp b/tests/jid.cpp index 65f8383..49d108b 100644 --- a/tests/jid.cpp +++ b/tests/jid.cpp @@ -6,32 +6,42 @@ import larra.library.jid; namespace larra::xmpp { TEST(Jid, Basic) { - auto jid = Jid{.username = "user"}; - EXPECT_FALSE(jid.IsValid()); + auto jid = FullJid{.username = "user"}; const auto jid2 = std::move(jid).Server("server").Resource("resource"); - EXPECT_EQ(jid2.Server(), "server"); - EXPECT_EQ(jid2.Username(), "user"); - EXPECT_EQ(jid2.Resource(), "resource"); + EXPECT_EQ(jid2.server, "server"); + EXPECT_EQ(jid2.username, "user"); + EXPECT_EQ(jid2.resource, "resource"); } TEST(Jid, Parse) { const auto jid = Jid::Parse("user@server/resource"); - EXPECT_EQ(jid.Username(), "user"); - EXPECT_EQ(jid.Server(), "server"); - EXPECT_EQ(jid.Resource(), "resource"); + + EXPECT_TRUE(std::get_if(&jid)); + const auto& fullJid = std::get(jid); + EXPECT_EQ(fullJid.username, "user"); + EXPECT_EQ(fullJid.server, "server"); + EXPECT_EQ(fullJid.resource, "resource"); + const auto jid2 = Jid::Parse("server/resource"); - EXPECT_EQ(jid2.Server(), "server"); - EXPECT_EQ(jid2.Resource(), "resource"); + + EXPECT_TRUE(std::get_if(&jid2)); + const auto& resourceJid = std::get(jid2); + EXPECT_EQ(resourceJid.server, "server"); + EXPECT_EQ(resourceJid.resource, "resource"); + const auto jid3 = Jid::Parse("user@server"); - EXPECT_EQ(jid3.Username(), "user"); - EXPECT_EQ(jid3.Server(), "server"); + + EXPECT_TRUE(std::get_if(&jid3)); + const auto& bareJid = std::get(jid3); + EXPECT_EQ(bareJid.username, "user"); + EXPECT_EQ(bareJid.server, "server"); } TEST(Jid, ToString) { - EXPECT_EQ((Jid{.server = "server", .username = "user", .resource = "resource"}.ToString()), "user@server/resource"); - EXPECT_EQ((Jid{.server = "server", .username = "user"}.ToString()), "user@server"); - EXPECT_THROW(std::ignore = Jid{}.ToString(), std::invalid_argument); + EXPECT_EQ(ToString(FullJid{.username = "user", .server = "server", .resource = "resource"}), "user@server/resource"); + EXPECT_EQ(ToString(BareJid{.username = "user", .server = "server"}), "user@server"); + EXPECT_EQ(ToString(BareResourceJid{.server = "server", .resource = "resource"}), "server/resource"); } } // namespace larra::xmpp