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
10namespace Quotient {
11
12class 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
18template <typename FnT, class EvT = std::decay_t<fn_arg_t<FnT>>>
19concept Keyed_State_Fn = EvT::needsStateKey;
20
21template <typename FnT, class EvT = std::decay_t<fn_arg_t<FnT>>>
22concept Keyless_State_Fn = !EvT::needsStateKey;
23
24class QUOTIENT_API RoomStateView
25 : private QHash<StateEventKey, const StateEvent*> {
26 Q_GADGET
27public:
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
207private:
208 friend class Room;
209};
210} // namespace Quotient
211