1#pragma once
2
3/// @file
4/// @brief Identifiers used in the Matrix API.
5
6#if __has_include(<nlohmann/json_fwd.hpp>)
7#include <nlohmann/json_fwd.hpp>
8#else
9#include <nlohmann/json.hpp>
10#endif
11
12#include <compare>
13#include <stdexcept>
14
15namespace mtx {
16namespace identifiers {
17
18//! Base class for all the identifiers.
19//
20//! Each identifier has the following format `(sigil)``(localpart)`:`(hostname)`.
21class ID
22{
23public:
24 //! Returns the unique local part of the identifier.
25 [[nodiscard]] std::string localpart() const { return localpart_; }
26 //! Returns the name of the originating homeserver.
27 [[nodiscard]] std::string hostname() const { return hostname_; }
28 //! Returns the whole identifier (localpart + hostname).
29 [[nodiscard]] std::string to_string() const { return id_; }
30
31protected:
32 //! Local part of the identifier.
33 std::string localpart_;
34 //! The name of the originating homeserver.
35 std::string hostname_;
36 //! The whole identifier.
37 std::string id_;
38};
39
40//! An event id.
41class Event : public ID
42{
43public:
44 template<typename Identifier>
45 friend Identifier parse(const std::string &id);
46
47private:
48 //! The `sigil` used to represent an Event.
49 static constexpr std::string_view sigil = "$";
50
51 friend void from_json(const nlohmann::json &obj, Event &event);
52 friend void to_json(nlohmann::json &obj, const Event &event);
53};
54
55//! A room id.
56class Room : public ID
57{
58public:
59 template<typename Identifier>
60 friend Identifier parse(const std::string &id);
61
62private:
63 static constexpr std::string_view sigil = "!";
64
65 friend void from_json(const nlohmann::json &obj, Room &room);
66 friend void to_json(nlohmann::json &obj, const Room &room);
67};
68
69//! A user id.
70class User : public ID
71{
72public:
73 template<typename Identifier>
74 friend Identifier parse(const std::string &id);
75 auto operator<=>(User const &other) const noexcept { return id_.compare(str: other.id_) <=> 0; };
76 bool operator==(User const &other) const noexcept { return id_ == other.id_; };
77
78private:
79 static constexpr std::string_view sigil = "@";
80
81 friend void from_json(const nlohmann::json &obj, User &user);
82 friend void to_json(nlohmann::json &obj, const User &user);
83};
84
85//! Parses the given string into a @p Identifier.
86//! \param id String to parse.
87//! \returns The parsed @p Identifier.
88//! \throws std::invalid_argument in case of invalid input.
89template<typename Identifier>
90Identifier
91parse(const std::string &id)
92{
93 if (id.empty()) {
94 // FIXME: enable logging only in debug mode.
95 /* std::cout << "mtx::identifiers - Empty matrix identifier was given" << std::endl;
96 */
97 return {};
98 }
99
100 if (std::string(1, id.at(n: 0)) != Identifier::sigil)
101 throw std::invalid_argument(id + ": missing sigil " + std::string(Identifier::sigil));
102
103 const auto parts = id.find_first_of(c: ':');
104
105 // Split into localpart and server.
106 if (parts != std::string::npos) {
107 Identifier identifier{};
108 identifier.localpart_ = id.substr(pos: 1, n: parts - 1);
109 identifier.hostname_ = id.substr(pos: parts + 1);
110 identifier.id_ = id;
111 return identifier;
112 } else if (Identifier::sigil == "$") {
113 // V3 event ids don't use ':' at all, don't parse them the same way.
114 Identifier identifier{};
115 identifier.localpart_ = id;
116 identifier.hostname_ = id;
117 identifier.id_ = id;
118 return identifier;
119 } else {
120 throw std::invalid_argument(id + ": invalid id");
121 }
122}
123
124} // namespace identifiers
125} // namespace mtx
126