| 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 | |
| 15 | namespace mtx { |
| 16 | namespace identifiers { |
| 17 | |
| 18 | //! Base class for all the identifiers. |
| 19 | // |
| 20 | //! Each identifier has the following format `(sigil)``(localpart)`:`(hostname)`. |
| 21 | class ID |
| 22 | { |
| 23 | public: |
| 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 | |
| 31 | protected: |
| 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. |
| 41 | class Event : public ID |
| 42 | { |
| 43 | public: |
| 44 | template<typename Identifier> |
| 45 | friend Identifier parse(const std::string &id); |
| 46 | |
| 47 | private: |
| 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. |
| 56 | class Room : public ID |
| 57 | { |
| 58 | public: |
| 59 | template<typename Identifier> |
| 60 | friend Identifier parse(const std::string &id); |
| 61 | |
| 62 | private: |
| 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. |
| 70 | class User : public ID |
| 71 | { |
| 72 | public: |
| 73 | template<typename Identifier> |
| 74 | friend Identifier parse(const std::string &id); |
| 75 | auto (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 | |
| 78 | private: |
| 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. |
| 89 | template<typename Identifier> |
| 90 | Identifier |
| 91 | parse(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 | |