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 | |