From 843f7a0ac2619a2f2863d457cdeaa03707255793 Mon Sep 17 00:00:00 2001 From: Alexey Rusakov Date: Wed, 4 May 2022 21:12:29 +0200 Subject: basic*EventJson() -> *Event::basicJson() This makes it easier and more intuitive to build a minimal JSON payload for a given event type. A common basicJson() call point is also convenient in template contexts (see next commits). --- lib/events/stateevent.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/events/stateevent.cpp') diff --git a/lib/events/stateevent.cpp b/lib/events/stateevent.cpp index e53d47d4..cdf3c449 100644 --- a/lib/events/stateevent.cpp +++ b/lib/events/stateevent.cpp @@ -16,7 +16,7 @@ StateEventBase::StateEventBase(Type type, const QJsonObject& json) StateEventBase::StateEventBase(Event::Type type, event_mtype_t matrixType, const QString& stateKey, const QJsonObject& contentJson) - : RoomEvent(type, basicStateEventJson(matrixType, contentJson, stateKey)) + : RoomEvent(type, basicJson(type, contentJson, stateKey)) {} bool StateEventBase::repeatsState() const -- cgit v1.2.3 From 41f630cf7be6e806e498cb31711f403bf6919ff8 Mon Sep 17 00:00:00 2001 From: Alexey Rusakov Date: Sat, 7 May 2022 19:23:18 +0200 Subject: Pass matrixType(QString), not Event::Type, to basicJson() Not that it was very important, as the two are basically the same thing (and matrixTypeId() is about to be obsoleted); but formally basicJson() is supposed to get the former, not the latter. --- lib/events/roomevent.cpp | 8 ++++---- lib/events/stateevent.cpp | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'lib/events/stateevent.cpp') diff --git a/lib/events/roomevent.cpp b/lib/events/roomevent.cpp index ebe72149..707cf4fd 100644 --- a/lib/events/roomevent.cpp +++ b/lib/events/roomevent.cpp @@ -105,15 +105,15 @@ QJsonObject CallEventBase::basicJson(const QString& matrixType, const QString& callId, int version, QJsonObject content) { - content.insert(QStringLiteral("call_id"), callId); - content.insert(QStringLiteral("version"), version); - return RoomEvent::basicJson(matrixType, content); + contentJson.insert(QStringLiteral("call_id"), callId); + contentJson.insert(QStringLiteral("version"), version); + return RoomEvent::basicJson(matrixType, contentJson); } CallEventBase::CallEventBase(Type type, event_mtype_t matrixType, const QString& callId, int version, const QJsonObject& contentJson) - : RoomEvent(type, basicJson(type, callId, version, contentJson)) + : RoomEvent(type, basicJson(matrixType, callId, version, contentJson)) {} CallEventBase::CallEventBase(Event::Type type, const QJsonObject& json) diff --git a/lib/events/stateevent.cpp b/lib/events/stateevent.cpp index cdf3c449..0fd489d1 100644 --- a/lib/events/stateevent.cpp +++ b/lib/events/stateevent.cpp @@ -16,7 +16,7 @@ StateEventBase::StateEventBase(Type type, const QJsonObject& json) StateEventBase::StateEventBase(Event::Type type, event_mtype_t matrixType, const QString& stateKey, const QJsonObject& contentJson) - : RoomEvent(type, basicJson(type, contentJson, stateKey)) + : RoomEvent(type, basicJson(matrixType, contentJson, stateKey)) {} bool StateEventBase::repeatsState() const -- cgit v1.2.3 From f4a20cc3710ee8f4b1788f73d05466aa0e660d61 Mon Sep 17 00:00:00 2001 From: Alexey Rusakov Date: Sat, 7 May 2022 19:02:35 +0200 Subject: More cleanup --- lib/events/directchatevent.cpp | 2 -- lib/events/eventcontent.h | 2 +- lib/events/roomevent.cpp | 4 ++-- lib/events/stateevent.cpp | 4 ++-- lib/room.cpp | 9 +++++---- lib/room.h | 3 ++- 6 files changed, 12 insertions(+), 12 deletions(-) (limited to 'lib/events/stateevent.cpp') diff --git a/lib/events/directchatevent.cpp b/lib/events/directchatevent.cpp index 0ee1f7b0..83bb1e32 100644 --- a/lib/events/directchatevent.cpp +++ b/lib/events/directchatevent.cpp @@ -3,8 +3,6 @@ #include "directchatevent.h" -#include - using namespace Quotient; QMultiHash DirectChatEvent::usersToDirectChats() const diff --git a/lib/events/eventcontent.h b/lib/events/eventcontent.h index 6aee333d..bbd35618 100644 --- a/lib/events/eventcontent.h +++ b/lib/events/eventcontent.h @@ -40,7 +40,7 @@ namespace EventContent { Base(const Base&) = default; Base(Base&&) = default; - virtual void fillJson(QJsonObject& o) const = 0; + virtual void fillJson(QJsonObject&) const = 0; }; // The below structures fairly follow CS spec 11.2.1.6. The overall diff --git a/lib/events/roomevent.cpp b/lib/events/roomevent.cpp index 707cf4fd..3ddf5ac4 100644 --- a/lib/events/roomevent.cpp +++ b/lib/events/roomevent.cpp @@ -103,7 +103,7 @@ void RoomEvent::dumpTo(QDebug dbg) const QJsonObject CallEventBase::basicJson(const QString& matrixType, const QString& callId, int version, - QJsonObject content) + QJsonObject contentJson) { contentJson.insert(QStringLiteral("call_id"), callId); contentJson.insert(QStringLiteral("version"), version); @@ -116,7 +116,7 @@ CallEventBase::CallEventBase(Type type, event_mtype_t matrixType, : RoomEvent(type, basicJson(matrixType, callId, version, contentJson)) {} -CallEventBase::CallEventBase(Event::Type type, const QJsonObject& json) +CallEventBase::CallEventBase(Type type, const QJsonObject& json) : RoomEvent(type, json) { if (callId().isEmpty()) diff --git a/lib/events/stateevent.cpp b/lib/events/stateevent.cpp index 0fd489d1..c343e37f 100644 --- a/lib/events/stateevent.cpp +++ b/lib/events/stateevent.cpp @@ -6,9 +6,9 @@ using namespace Quotient; StateEventBase::StateEventBase(Type type, const QJsonObject& json) - : RoomEvent(json.contains(StateKeyKeyL) ? type : unknownEventTypeId(), json) + : RoomEvent(json.contains(StateKeyKeyL) ? type : UnknownEventTypeId, json) { - if (Event::type() == unknownEventTypeId() && !json.contains(StateKeyKeyL)) + if (Event::type() == UnknownEventTypeId && !json.contains(StateKeyKeyL)) qWarning(EVENTS) << "Attempt to create a state event with no stateKey -" "forcing the event type to unknown to avoid damage"; } diff --git a/lib/room.cpp b/lib/room.cpp index 183e242a..4d9f952c 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -3064,15 +3064,16 @@ Room::Changes Room::processEphemeralEvent(EventPtr&& event) QElapsedTimer et; et.start(); if (auto* evt = eventCast(event)) { + const auto& users = evt->users(); d->usersTyping.clear(); - d->usersTyping.reserve(evt->users().size()); // Assume all are members - for (const auto& userId : evt->users()) + d->usersTyping.reserve(users.size()); // Assume all are members + for (const auto& userId : users) if (isMember(userId)) d->usersTyping.append(user(userId)); - if (evt->users().size() > 3 || et.nsecsElapsed() >= profilerMinNsecs()) + if (users.size() > 3 || et.nsecsElapsed() >= profilerMinNsecs()) qCDebug(PROFILER) - << "Processing typing events from" << evt->users().size() + << "Processing typing events from" << users.size() << "user(s) in" << objectName() << "took" << et; emit typingChanged(); } diff --git a/lib/room.h b/lib/room.h index 6ba7feac..7e53aed0 100644 --- a/lib/room.h +++ b/lib/room.h @@ -758,7 +758,8 @@ public: [[deprecated("Use currentState().get() instead; " "make sure to check its result for nullptrs")]] // const Quotient::StateEventBase* - getCurrentState(const QString& evtType, const QString& stateKey = {}) const; + getCurrentState(const QString& evtType, + const QString& stateKey = {}) const; /// 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 -- cgit v1.2.3 From ab2d78de6a9d33e1470ae9db2ed6ed9565c817e5 Mon Sep 17 00:00:00 2001 From: Alexey Rusakov Date: Mon, 11 Jul 2022 10:08:44 +0200 Subject: fromJson()/toJson() refactoring fromJson() is generalised to accept any JSON-like type while passing QJsonObject to JsonConverter<>::load (instead of doLoad). This allows to (still) rely on JsonConverter<> as a customisation point while providing an opportunity to overload fromJson for custom types in a pointed way (specifically, by providing the overload for `fromJson(const QJsonObject&)`), instead of having to go with full-blown JsonConverter<> specialisation. This will be used in a further commit to simplify ReceiptEvent definition. Using if constexpr in combination with constraints (`requires()`) - the first such case in Quotient codebase - allowed to put the entire logic in a single JsonConverter<>::load() body instead of having a facility JsonExporter<> class for SFINAE. Aside from that, fromJson is entirely dropped because it's not supposed to be used that way (it's no-op after all); reflecting that, Event::unsignedPart() and Event::contentPart() no more default to QJsonValue as the expected return type, you have to explicitly provide the type instead (and as one can see from the changes in the commit, it's actually better that way since it's better to validate the value inside JSON - e.g. check QString or QJsonObject for emptiness - than the QJsonValue envelope which may still wrap an empty value). toJson() is also generalised, replacing 3 functions with one that has a constexpr if to discern between two kinds of types. --- lib/converters.h | 89 ++++++++++++++++++------------------------- lib/events/encryptedevent.cpp | 10 +++-- lib/events/event.h | 4 +- lib/events/roomevent.cpp | 6 +-- lib/events/stateevent.cpp | 2 +- 5 files changed, 50 insertions(+), 61 deletions(-) (limited to 'lib/events/stateevent.cpp') diff --git a/lib/converters.h b/lib/converters.h index c445442c..9f42d712 100644 --- a/lib/converters.h +++ b/lib/converters.h @@ -28,23 +28,8 @@ struct JsonObjectConverter { static void fillFrom(const QJsonObject&, T&) = delete; }; -namespace _impl { - template - struct JsonExporter { - static QJsonObject dump(const T& data) - { - QJsonObject jo; - JsonObjectConverter::dumpTo(jo, data); - return jo; - } - }; - - template - struct JsonExporter< - T, std::enable_if_t>> { - static auto dump(const T& data) { return data.toJson(); } - }; -} +template +PodT fromJson(const JsonT&); //! \brief The switchboard for extra conversion algorithms behind from/toJson //! @@ -62,13 +47,23 @@ namespace _impl { //! that they are not supported and it's not feasible to support those by means //! of overloading toJson() and specialising fromJson(). template -struct JsonConverter : _impl::JsonExporter { +struct JsonConverter { // Unfortunately, if constexpr doesn't work with dump() and T::toJson // because trying to check invocability of T::toJson hits a hard // (non-SFINAE) compilation error if the member is not there. Hence a bit // more verbose SFINAE construct in _impl::JsonExporter. + static auto dump(const T& data) + { + if constexpr (requires() { data.toJson(); }) + return data.toJson(); + else { + QJsonObject jo; + JsonObjectConverter::dumpTo(jo, data); + return jo; + } + } - static T doLoad(const QJsonObject& jo) + static T load(const QJsonObject& jo) { // 'else' below are required to suppress code generation for unused // branches - 'return' is not enough @@ -82,65 +77,57 @@ struct JsonConverter : _impl::JsonExporter { return pod; } } - static T load(const QJsonValue& jv) { return doLoad(jv.toObject()); } - static T load(const QJsonDocument& jd) { return doLoad(jd.object()); } + // By default, revert to fromJson() so that one could provide a single + // fromJson specialisation instead of specialising + // the entire JsonConverter; if a different type of JSON value is needed + // (e.g., an array), specialising JsonConverter is inevitable + static T load(QJsonValueRef jvr) { return fromJson(QJsonValue(jvr)); } + static T load(const QJsonValue& jv) { return fromJson(jv.toObject()); } + static T load(const QJsonDocument& jd) { return fromJson(jd.object()); } }; template - requires (!std::is_constructible_v) inline auto toJson(const T& pod) // -> can return anything from which QJsonValue or, in some cases, QJsonDocument // is constructible { - return JsonConverter::dump(pod); + if constexpr (std::is_constructible_v) + return pod; // No-op if QJsonValue can be directly constructed + else + return JsonConverter::dump(pod); } -inline auto toJson(const QJsonObject& jo) { return jo; } -inline auto toJson(const QJsonValue& jv) { return jv; } - template inline void fillJson(QJsonObject& json, const T& data) { JsonObjectConverter::dumpTo(json, data); } -template -inline T fromJson(const QJsonValue& jv) +template +inline PodT fromJson(const JsonT& json) { - return JsonConverter::load(jv); + // JsonT here can be whatever the respective JsonConverter specialisation + // accepts but by default it's QJsonValue, QJsonDocument, or QJsonObject + return JsonConverter::load(json); } -template<> -inline QJsonValue fromJson(const QJsonValue& jv) { return jv; } - -template -inline T fromJson(const QJsonDocument& jd) -{ - return JsonConverter::load(jd); -} - -// Convenience fromJson() overloads that deduce T instead of requiring -// the coder to explicitly type it. They still enforce the +// Convenience fromJson() overload that deduces PodT instead of requiring +// the coder to explicitly type it. It still enforces the // overwrite-everything semantics of fromJson(), unlike fillFromJson() -template -inline void fromJson(const QJsonValue& jv, T& pod) -{ - pod = jv.isUndefined() ? T() : fromJson(jv); -} - -template -inline void fromJson(const QJsonDocument& jd, T& pod) +template +inline void fromJson(const JsonT& json, PodT& pod) { - pod = fromJson(jd); + pod = fromJson(json); } template inline void fillFromJson(const QJsonValue& jv, T& pod) { - if (jv.isObject()) + if constexpr (requires() { JsonObjectConverter::fillFrom({}, pod); }) { JsonObjectConverter::fillFrom(jv.toObject(), pod); - else if (!jv.isUndefined()) + return; + } else if (!jv.isUndefined()) pod = fromJson(jv); } diff --git a/lib/events/encryptedevent.cpp b/lib/events/encryptedevent.cpp index c97ccc16..ec00ad4c 100644 --- a/lib/events/encryptedevent.cpp +++ b/lib/events/encryptedevent.cpp @@ -49,14 +49,16 @@ RoomEventPtr EncryptedEvent::createDecrypted(const QString &decrypted) const eventObject["event_id"] = id(); eventObject["sender"] = senderId(); eventObject["origin_server_ts"] = originTimestamp().toMSecsSinceEpoch(); - if (const auto relatesToJson = contentPart("m.relates_to"_ls); !relatesToJson.isUndefined()) { + if (const auto relatesToJson = contentPart("m.relates_to"_ls); + !relatesToJson.isEmpty()) { auto content = eventObject["content"].toObject(); - content["m.relates_to"] = relatesToJson.toObject(); + content["m.relates_to"] = relatesToJson; eventObject["content"] = content; } - if (const auto redactsJson = unsignedPart("redacts"_ls); !redactsJson.isUndefined()) { + if (const auto redactsJson = unsignedPart("redacts"_ls); + !redactsJson.isEmpty()) { auto unsign = eventObject["unsigned"].toObject(); - unsign["redacts"] = redactsJson.toString(); + unsign["redacts"] = redactsJson; eventObject["unsigned"] = unsign; } return loadEvent(eventObject); diff --git a/lib/events/event.h b/lib/events/event.h index 05eb51e9..da6cf3c7 100644 --- a/lib/events/event.h +++ b/lib/events/event.h @@ -212,7 +212,7 @@ public: const QJsonObject contentJson() const; - template + template const T contentPart(KeyT&& key) const { return fromJson(contentJson()[std::forward(key)]); @@ -227,7 +227,7 @@ public: const QJsonObject unsignedJson() const; - template + template const T unsignedPart(KeyT&& key) const { return fromJson(unsignedJson()[std::forward(key)]); diff --git a/lib/events/roomevent.cpp b/lib/events/roomevent.cpp index 3ddf5ac4..e695e0ec 100644 --- a/lib/events/roomevent.cpp +++ b/lib/events/roomevent.cpp @@ -15,9 +15,9 @@ RoomEvent::RoomEvent(Type type, event_mtype_t matrixType, RoomEvent::RoomEvent(Type type, const QJsonObject& json) : Event(type, json) { - if (const auto redaction = unsignedPart(RedactedCauseKeyL); - redaction.isObject()) - _redactedBecause = makeEvent(redaction.toObject()); + if (const auto redaction = unsignedPart(RedactedCauseKeyL); + !redaction.isEmpty()) + _redactedBecause = makeEvent(redaction); } RoomEvent::~RoomEvent() = default; // Let the smart pointer do its job diff --git a/lib/events/stateevent.cpp b/lib/events/stateevent.cpp index c343e37f..43dfd6e8 100644 --- a/lib/events/stateevent.cpp +++ b/lib/events/stateevent.cpp @@ -21,7 +21,7 @@ StateEventBase::StateEventBase(Event::Type type, event_mtype_t matrixType, bool StateEventBase::repeatsState() const { - const auto prevContentJson = unsignedPart(PrevContentKeyL); + const auto prevContentJson = unsignedPart(PrevContentKeyL); return fullJson().value(ContentKeyL) == prevContentJson; } -- cgit v1.2.3 From 3214feeb031fa231c7c42c21c53410302966e32e Mon Sep 17 00:00:00 2001 From: Alexey Rusakov Date: Fri, 29 Jul 2022 18:13:14 +0200 Subject: eventloader.h: use basicJson() in a uniform way There's no particular reason the order of parameters in StateEventBase::basicJson() should be as it was, and (the only) loadStateEvent() usage in room.cpp suggests the unified order is more convenient. Besides, this order is aligned with that in the StateEventBase constructor. --- lib/events/eventloader.h | 35 ++++++++++------------------------- lib/events/stateevent.cpp | 2 +- lib/events/stateevent.h | 8 ++++---- lib/room.cpp | 5 +++-- 4 files changed, 18 insertions(+), 32 deletions(-) (limited to 'lib/events/stateevent.cpp') diff --git a/lib/events/eventloader.h b/lib/events/eventloader.h index 15271ab1..4c639efa 100644 --- a/lib/events/eventloader.h +++ b/lib/events/eventloader.h @@ -19,32 +19,17 @@ inline event_ptr_tt loadEvent(const QJsonObject& fullJson) return doLoadEvent(fullJson, fullJson[TypeKeyL].toString()); } -/*! Create an event from a type string and content JSON - * - * Use this factory template to resolve the C++ type from the Matrix - * type string in \p matrixType and create an event of that type that has - * its content part set to \p content. - */ -template -inline event_ptr_tt loadEvent(const QString& matrixType, - const QJsonObject& content) -{ - return doLoadEvent(Event::basicJson(matrixType, content), - matrixType); -} - -/*! Create a state event from a type string, content JSON and state key - * - * Use this factory to resolve the C++ type from the Matrix type string - * in \p matrixType and create a state event of that type with content part - * set to \p content and state key set to \p stateKey (empty by default). - */ -inline StateEventPtr loadStateEvent(const QString& matrixType, - const QJsonObject& content, - const QString& stateKey = {}) +//! \brief Create an event from a type string and content JSON +//! +//! Use this template to resolve the C++ type from the Matrix type string in +//! \p matrixType and create an event of that type by passing all parameters +//! to BaseEventT::basicJson(). +template +inline event_ptr_tt loadEvent( + const QString& matrixType, const BasicJsonParamTs&... basicJsonParams) { - return doLoadEvent( - StateEventBase::basicJson(matrixType, content, stateKey), matrixType); + return doLoadEvent( + BaseEventT::basicJson(matrixType, basicJsonParams...), matrixType); } template diff --git a/lib/events/stateevent.cpp b/lib/events/stateevent.cpp index 43dfd6e8..1df24df0 100644 --- a/lib/events/stateevent.cpp +++ b/lib/events/stateevent.cpp @@ -16,7 +16,7 @@ StateEventBase::StateEventBase(Type type, const QJsonObject& json) StateEventBase::StateEventBase(Event::Type type, event_mtype_t matrixType, const QString& stateKey, const QJsonObject& contentJson) - : RoomEvent(type, basicJson(matrixType, contentJson, stateKey)) + : RoomEvent(type, basicJson(type, stateKey, contentJson)) {} bool StateEventBase::repeatsState() const diff --git a/lib/events/stateevent.h b/lib/events/stateevent.h index 343e87a5..9f1d7118 100644 --- a/lib/events/stateevent.h +++ b/lib/events/stateevent.h @@ -19,12 +19,12 @@ public: //! Make a minimal correct Matrix state event JSON static QJsonObject basicJson(const QString& matrixTypeId, - const QJsonObject& content, - const QString& stateKey = {}) + const QString& stateKey = {}, + const QJsonObject& contentJson = {}) { return { { TypeKey, matrixTypeId }, { StateKeyKey, stateKey }, - { ContentKey, content } }; + { ContentKey, contentJson } }; } bool isStateEvent() const override { return true; } @@ -41,7 +41,7 @@ inline QJsonObject basicStateEventJson(const QString& matrixTypeId, const QJsonObject& content, const QString& stateKey = {}) { - return StateEventBase::basicJson(matrixTypeId, content, stateKey); + return StateEventBase::basicJson(matrixTypeId, stateKey, content); } //! \brief Override RoomEvent factory with that from StateEventBase if JSON has diff --git a/lib/room.cpp b/lib/room.cpp index f5d8709b..7fd41a4f 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -219,8 +219,9 @@ public: // In the absence of a real event, make a stub as-if an event // with empty content has been received. Event classes should be // prepared for empty/invalid/malicious content anyway. - stubbedState.emplace(evtKey, loadStateEvent(evtKey.first, {}, - evtKey.second)); + stubbedState.emplace(evtKey, + loadEvent(evtKey.first, + evtKey.second)); qCDebug(STATE) << "A new stub event created for key {" << evtKey.first << evtKey.second << "}"; qCDebug(STATE) << "Stubbed state size:" << stubbedState.size(); -- cgit v1.2.3