| 1 | // SPDX-FileCopyrightText: 2021 Kitsune Ral <kitsune-ral@users.sf.net> |
| 2 | // SPDX-License-Identifier: LGPL-2.1-or-later |
| 3 | |
| 4 | #pragma once |
| 5 | |
| 6 | #include "events/stateevent.h" |
| 7 | |
| 8 | #include <QtCore/QHash> |
| 9 | |
| 10 | namespace Quotient { |
| 11 | |
| 12 | class Room; |
| 13 | |
| 14 | // NB: Both concepts below expect EvT::needsStateKey to exist so you can't |
| 15 | // express one via negation of the other (there's still an invalid case of |
| 16 | // a non-state event where needsStateKey is not even defined). |
| 17 | |
| 18 | template <typename FnT, class EvT = std::decay_t<fn_arg_t<FnT>>> |
| 19 | concept Keyed_State_Fn = EvT::needsStateKey; |
| 20 | |
| 21 | template <typename FnT, class EvT = std::decay_t<fn_arg_t<FnT>>> |
| 22 | concept Keyless_State_Fn = !EvT::needsStateKey; |
| 23 | |
| 24 | class QUOTIENT_API RoomStateView |
| 25 | : private QHash<StateEventKey, const StateEvent*> { |
| 26 | Q_GADGET |
| 27 | public: |
| 28 | const QHash<StateEventKey, const StateEvent*>& events() const |
| 29 | { |
| 30 | return *this; |
| 31 | } |
| 32 | |
| 33 | //! \brief Get a state event with the given event type and state key |
| 34 | //! \return A state event corresponding to the pair of event type |
| 35 | //! \p evtType and state key \p stateKey, or `nullptr` if there's |
| 36 | //! no such \p evtType / \p stateKey combination in the current |
| 37 | //! state. |
| 38 | //! \warning The returned value is not guaranteed to be non-`nullptr`; you |
| 39 | //! MUST check it before using or use other methods of this class |
| 40 | //! such as query() and content() to access state safely. |
| 41 | //! \sa content, contentJson, query |
| 42 | const StateEvent* get(const QString& evtType, |
| 43 | const QString& stateKey = {}) const; |
| 44 | |
| 45 | //! \brief Get a state event with the given event type and state key |
| 46 | //! |
| 47 | //! This is a typesafe overload that accepts a C++ event type instead of |
| 48 | //! its Matrix name. It is only defined for events with state key (i.e., |
| 49 | //! derived from KeyedStateEvent). |
| 50 | template <Keyed_State_Event EvT> |
| 51 | const EvT* get(const QString& stateKey = {}) const |
| 52 | { |
| 53 | if (const auto* evt = get(EvT::TypeId, stateKey)) { |
| 54 | Q_ASSERT(evt->matrixType() == EvT::TypeId |
| 55 | && evt->stateKey() == stateKey); |
| 56 | return eventCast<const EvT>(evt); |
| 57 | } |
| 58 | return nullptr; |
| 59 | } |
| 60 | |
| 61 | //! \brief Get a state event with the given event type |
| 62 | //! |
| 63 | //! This is a typesafe overload that accepts a C++ event type instead of |
| 64 | //! its Matrix name. This overload only defined for events that do not use |
| 65 | //! state key (i.e., derived from KeylessStateEvent). |
| 66 | template <Keyless_State_Event EvT> |
| 67 | const EvT* get() const |
| 68 | { |
| 69 | if (const auto* evt = get(EvT::TypeId)) { |
| 70 | Q_ASSERT(evt->matrixType() == EvT::TypeId); |
| 71 | return eventCast<const EvT>(evt); |
| 72 | } |
| 73 | return nullptr; |
| 74 | } |
| 75 | |
| 76 | using QHash::contains; |
| 77 | |
| 78 | bool contains(const QString& evtType, const QString& stateKey = {}) const; |
| 79 | |
| 80 | template <Keyed_State_Event EvT> |
| 81 | bool contains(const QString& stateKey = {}) const |
| 82 | { |
| 83 | return contains(EvT::TypeId, stateKey); |
| 84 | } |
| 85 | |
| 86 | template <Keyless_State_Event EvT> |
| 87 | bool contains() const |
| 88 | { |
| 89 | return contains(EvT::TypeId); |
| 90 | } |
| 91 | |
| 92 | template <Keyed_State_Event EvT> |
| 93 | auto content(const QString& stateKey, |
| 94 | typename EvT::content_type defaultValue = {}) const |
| 95 | { |
| 96 | // EventBase<>::content is special in that it returns a const-ref, |
| 97 | // and lift() inside queryOr() can't wrap that in a temporary Omittable. |
| 98 | if (const auto evt = get<EvT>(stateKey)) |
| 99 | return evt->content(); |
| 100 | return std::move(defaultValue); |
| 101 | } |
| 102 | |
| 103 | template <Keyless_State_Event EvT> |
| 104 | auto content(typename EvT::content_type defaultValue = {}) const |
| 105 | { |
| 106 | // Same as above |
| 107 | if (const auto evt = get<EvT>()) |
| 108 | return evt->content(); |
| 109 | return defaultValue; |
| 110 | } |
| 111 | |
| 112 | //! \brief Get the content of the current state event with the given |
| 113 | //! event type and state key |
| 114 | //! \return An empty object if there's no event in the current state with |
| 115 | //! this event type and state key; the contents of the event |
| 116 | //! <tt>'content'</tt> object otherwise |
| 117 | Q_INVOKABLE QJsonObject contentJson(const QString& evtType, |
| 118 | const QString& stateKey = {}) const; |
| 119 | |
| 120 | //! \brief Get all state events in the room of a certain type. |
| 121 | //! |
| 122 | //! This method returns all known state events that have occured in |
| 123 | //! the room of the given type. |
| 124 | const QVector<const StateEvent*> eventsOfType(const QString& evtType) const; |
| 125 | |
| 126 | //! \brief Run a function on a state event with the given type and key |
| 127 | //! |
| 128 | //! Use this overload when there's no predefined event type or the event |
| 129 | //! type is unknown at compile time. |
| 130 | //! \return an Omittable with either the result of the function call, or |
| 131 | //! with `none` if the event is not found or the function fails |
| 132 | template <typename FnT> |
| 133 | auto query(const QString& evtType, const QString& stateKey, FnT&& fn) const |
| 134 | { |
| 135 | return lift(std::forward<FnT>(fn), get(evtType, stateKey)); |
| 136 | } |
| 137 | |
| 138 | //! \brief Run a function on a state event with the given type and key |
| 139 | //! |
| 140 | //! This is an overload for keyed state events (those that have |
| 141 | //! `needsStateKey == true`) with type defined at compile time. |
| 142 | //! \return an Omittable with either the result of the function call, or |
| 143 | //! with `none` if the event is not found or the function fails |
| 144 | template <Keyed_State_Fn FnT> |
| 145 | auto query(const QString& stateKey, FnT&& fn) const |
| 146 | { |
| 147 | using EventT = std::decay_t<fn_arg_t<FnT>>; |
| 148 | return lift(std::forward<FnT>(fn), get<EventT>(stateKey)); |
| 149 | } |
| 150 | |
| 151 | //! \brief Run a function on a keyless state event with the given type |
| 152 | //! |
| 153 | //! This is an overload for keyless state events (those having |
| 154 | //! `needsStateKey == false`) with type defined at compile time. |
| 155 | //! \return an Omittable with either the result of the function call, or |
| 156 | //! with `none` if the event is not found or the function fails |
| 157 | template <Keyless_State_Fn FnT> |
| 158 | auto query(FnT&& fn) const |
| 159 | { |
| 160 | using EventT = std::decay_t<fn_arg_t<FnT>>; |
| 161 | return lift(std::forward<FnT>(fn), get<EventT>()); |
| 162 | } |
| 163 | |
| 164 | //! \brief Same as query() but with a fallback value |
| 165 | //! |
| 166 | //! This is a shortcut for `query().value_or()`, passing respective |
| 167 | //! arguments to the respective functions. This is an overload for the case |
| 168 | //! when the event type cannot be fixed at compile time. |
| 169 | //! \return the result of \p fn execution, or \p fallback if the requested |
| 170 | //! event doesn't exist or the function fails |
| 171 | template <typename FnT, typename FallbackT> |
| 172 | auto queryOr(const QString& evtType, const QString& stateKey, FnT&& fn, |
| 173 | FallbackT&& fallback) const |
| 174 | { |
| 175 | return query(evtType, stateKey, std::forward<FnT>(fn)) |
| 176 | .value_or(std::forward<FallbackT>(fallback)); |
| 177 | } |
| 178 | |
| 179 | //! \brief Same as query() but with a fallback value |
| 180 | //! |
| 181 | //! This is a shortcut for `query().value_or()`, passing respective |
| 182 | //! arguments to the respective functions. This is an overload for the case |
| 183 | //! when the event type cannot be fixed at compile time. |
| 184 | //! \return the result of \p fn execution, or \p fallback if the requested |
| 185 | //! event doesn't exist or the function fails |
| 186 | template <typename FnT, typename FallbackT> |
| 187 | auto queryOr(const QString& stateKey, FnT&& fn, FallbackT&& fallback) const |
| 188 | { |
| 189 | return query(stateKey, std::forward<FnT>(fn)) |
| 190 | .value_or(std::forward<FallbackT>(fallback)); |
| 191 | } |
| 192 | |
| 193 | //! \brief Same as query() but with a fallback value |
| 194 | //! |
| 195 | //! This is a shortcut for `query().value_or()`, passing respective |
| 196 | //! arguments to the respective functions. This is an overload for the case |
| 197 | //! when the event type cannot be fixed at compile time. |
| 198 | //! \return the result of \p fn execution, or \p fallback if the requested |
| 199 | //! event doesn't exist or the function fails |
| 200 | template <typename FnT, typename FallbackT> |
| 201 | auto queryOr(FnT&& fn, FallbackT&& fallback) const |
| 202 | { |
| 203 | return query(std::forward<FnT>(fn)) |
| 204 | .value_or(std::forward<FallbackT>(fallback)); |
| 205 | } |
| 206 | |
| 207 | private: |
| 208 | friend class Room; |
| 209 | }; |
| 210 | } // namespace Quotient |
| 211 | |