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