diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index aac3e9dea..b8225c396 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -126,25 +126,22 @@ to make the review easier. ### C++ feature set -As of Quotient 0.8, the C++ standard for newly written code is C++20. Since none -of the supported compilers (GCC 11, Clang 11, Apple Clang 12, MSVC 19.30 - see -also the pre-requisites in [README](./README.md)) can handle the entire C++20 -feature set, we have to stick with a subset. Most notably: - -- `std::bind_front`, ranges, `std::format`, `std::source_location` can't be used - yet; -- while concepts and constraints as a language feature are there, most library - concepts are not available as of Apple Clang 12; -- No `constexpr` containers except `std::array` (but you can, and should, - use `QLatin1String` and `Quotient::operator""_ls` that creates it, for - constexpr Latin-1 strings). - -The [compiler support page](https://en.cppreference.com/w/cpp/compiler_support#cpp20), -of cppreference.com, combined with the list of compiler versions above, can be -used to check whether a given feature is there. Be mindful that Clang build -configuration on Linux does not use Clang libc++ but rather the GNU standard -library (i.e. you should look at Clang column for core language features but -GCC libstdc++ for library features). +As of Quotient 0.9, the C++ standard for newly written code is C++20, save for a few exceptions +that the currently supported toolchains still don't have, most notably: +- template parameteres for type aliases and aggregates still cannot be deduced yet, you have + to explicitly specify those; +- modules support, while formally there, is missing standard library header units; sticking with + good old `#include`s in this cycle. + +You can also try to use some C++23 library features; libQuotient is compiled with C++23 flags. The +[compiler support page at cppreference](https://en.cppreference.com/w/cpp/compiler_support#cpp23) +is a nice tool to check whether you can try using a specific language or library feature; refer +to the list of toolchain versions in [README](./README.md) for the compatibility baseline. +Be mindful that Clang build configuration on Linux does not use LLVM project's libc++ but rather +the GNU C++ standard library (i.e. you should look at Clang column for core language features +but GCC libstdc++ for library features). Most of the limitations, however, are due to Apple's +standard library - they have their own spin of libc++ that is constantly behind vanilla and, as +of now, the poorest in terms of features. ### Code style and formatting diff --git a/Quotient/csapi/administrative_contact.cpp b/Quotient/csapi/administrative_contact.cpp index 4b42d4d0b..31ab20e41 100644 --- a/Quotient/csapi/administrative_contact.cpp +++ b/Quotient/csapi/administrative_contact.cpp @@ -55,7 +55,7 @@ Delete3pidFromAccountJob::Delete3pidFromAccountJob(const QString& medium, const addParam<>(_dataJson, "medium"_L1, medium); addParam<>(_dataJson, "address"_L1, address); setRequestData({ _dataJson }); - addExpectedKey("id_server_unbind_result"); + addExpectedKey(u"id_server_unbind_result"_s); } Unbind3pidFromAccountJob::Unbind3pidFromAccountJob(const QString& medium, const QString& address, @@ -68,7 +68,7 @@ Unbind3pidFromAccountJob::Unbind3pidFromAccountJob(const QString& medium, const addParam<>(_dataJson, "medium"_L1, medium); addParam<>(_dataJson, "address"_L1, address); setRequestData({ _dataJson }); - addExpectedKey("id_server_unbind_result"); + addExpectedKey(u"id_server_unbind_result"_s); } RequestTokenTo3PIDEmailJob::RequestTokenTo3PIDEmailJob(const EmailValidationData& data) diff --git a/Quotient/csapi/capabilities.cpp b/Quotient/csapi/capabilities.cpp index a8b8d2de8..d48cd6037 100644 --- a/Quotient/csapi/capabilities.cpp +++ b/Quotient/csapi/capabilities.cpp @@ -13,5 +13,5 @@ GetCapabilitiesJob::GetCapabilitiesJob() : BaseJob(HttpVerb::Get, u"GetCapabilitiesJob"_s, makePath("/_matrix/client/v3", "/capabilities")) { - addExpectedKey("capabilities"); + addExpectedKey(u"capabilities"_s); } diff --git a/Quotient/csapi/content-repo.cpp b/Quotient/csapi/content-repo.cpp index 03f8324a3..518cd65e4 100644 --- a/Quotient/csapi/content-repo.cpp +++ b/Quotient/csapi/content-repo.cpp @@ -18,7 +18,7 @@ UploadContentJob::UploadContentJob(QIODevice* content, const QString& filename, { setRequestHeader("Content-Type", contentType.toLatin1()); setRequestData({ content }); - addExpectedKey("content_uri"); + addExpectedKey(u"content_uri"_s); } auto queryToUploadContentToMXC(const QString& filename) @@ -47,7 +47,7 @@ QUrl CreateContentJob::makeRequestUrl(const HomeserverData& hsData) CreateContentJob::CreateContentJob() : BaseJob(HttpVerb::Post, u"CreateContentJob"_s, makePath("/_matrix", "/media/v1/create")) { - addExpectedKey("content_uri"); + addExpectedKey(u"content_uri"_s); } auto queryToGetContent(bool allowRemote, qint64 timeoutMs, bool allowRedirect) diff --git a/Quotient/csapi/create_room.cpp b/Quotient/csapi/create_room.cpp index 697f2b507..bc727e1f4 100644 --- a/Quotient/csapi/create_room.cpp +++ b/Quotient/csapi/create_room.cpp @@ -27,5 +27,5 @@ CreateRoomJob::CreateRoomJob(const QString& visibility, const QString& roomAlias addParam(_dataJson, "is_direct"_L1, isDirect); addParam(_dataJson, "power_level_content_override"_L1, powerLevelContentOverride); setRequestData({ _dataJson }); - addExpectedKey("room_id"); + addExpectedKey(u"room_id"_s); } diff --git a/Quotient/csapi/directory.cpp b/Quotient/csapi/directory.cpp index a0c9a50f7..84be071a2 100644 --- a/Quotient/csapi/directory.cpp +++ b/Quotient/csapi/directory.cpp @@ -45,5 +45,5 @@ GetLocalAliasesJob::GetLocalAliasesJob(const QString& roomId) : BaseJob(HttpVerb::Get, u"GetLocalAliasesJob"_s, makePath("/_matrix/client/v3", "/rooms/", roomId, "/aliases")) { - addExpectedKey("aliases"); + addExpectedKey(u"aliases"_s); } diff --git a/Quotient/csapi/filter.cpp b/Quotient/csapi/filter.cpp index e00eea3fc..d900ca208 100644 --- a/Quotient/csapi/filter.cpp +++ b/Quotient/csapi/filter.cpp @@ -9,7 +9,7 @@ DefineFilterJob::DefineFilterJob(const QString& userId, const Filter& filter) makePath("/_matrix/client/v3", "/user/", userId, "/filter")) { setRequestData({ toJson(filter) }); - addExpectedKey("filter_id"); + addExpectedKey(u"filter_id"_s); } QUrl GetFilterJob::makeRequestUrl(const HomeserverData& hsData, const QString& userId, diff --git a/Quotient/csapi/joining.cpp b/Quotient/csapi/joining.cpp index 613443e6f..df4000cbb 100644 --- a/Quotient/csapi/joining.cpp +++ b/Quotient/csapi/joining.cpp @@ -14,7 +14,7 @@ JoinRoomByIdJob::JoinRoomByIdJob(const QString& roomId, addParam(_dataJson, "third_party_signed"_L1, thirdPartySigned); addParam(_dataJson, "reason"_L1, reason); setRequestData({ _dataJson }); - addExpectedKey("room_id"); + addExpectedKey(u"room_id"_s); } auto queryToJoinRoom(const QStringList& serverName) @@ -34,5 +34,5 @@ JoinRoomJob::JoinRoomJob(const QString& roomIdOrAlias, const QStringList& server addParam(_dataJson, "third_party_signed"_L1, thirdPartySigned); addParam(_dataJson, "reason"_L1, reason); setRequestData({ _dataJson }); - addExpectedKey("room_id"); + addExpectedKey(u"room_id"_s); } diff --git a/Quotient/csapi/key_backup.cpp b/Quotient/csapi/key_backup.cpp index f7bd4ab1f..fbef09a82 100644 --- a/Quotient/csapi/key_backup.cpp +++ b/Quotient/csapi/key_backup.cpp @@ -12,7 +12,7 @@ PostRoomKeysVersionJob::PostRoomKeysVersionJob(const QString& algorithm, const Q addParam<>(_dataJson, "algorithm"_L1, algorithm); addParam<>(_dataJson, "auth_data"_L1, authData); setRequestData({ _dataJson }); - addExpectedKey("version"); + addExpectedKey(u"version"_s); } QUrl GetRoomKeysVersionCurrentJob::makeRequestUrl(const HomeserverData& hsData) @@ -24,11 +24,11 @@ GetRoomKeysVersionCurrentJob::GetRoomKeysVersionCurrentJob() : BaseJob(HttpVerb::Get, u"GetRoomKeysVersionCurrentJob"_s, makePath("/_matrix/client/v3", "/room_keys/version")) { - addExpectedKey("algorithm"); - addExpectedKey("auth_data"); - addExpectedKey("count"); - addExpectedKey("etag"); - addExpectedKey("version"); + addExpectedKey(u"algorithm"_s); + addExpectedKey(u"auth_data"_s); + addExpectedKey(u"count"_s); + addExpectedKey(u"etag"_s); + addExpectedKey(u"version"_s); } QUrl GetRoomKeysVersionJob::makeRequestUrl(const HomeserverData& hsData, const QString& version) @@ -41,11 +41,11 @@ GetRoomKeysVersionJob::GetRoomKeysVersionJob(const QString& version) : BaseJob(HttpVerb::Get, u"GetRoomKeysVersionJob"_s, makePath("/_matrix/client/v3", "/room_keys/version/", version)) { - addExpectedKey("algorithm"); - addExpectedKey("auth_data"); - addExpectedKey("count"); - addExpectedKey("etag"); - addExpectedKey("version"); + addExpectedKey(u"algorithm"_s); + addExpectedKey(u"auth_data"_s); + addExpectedKey(u"count"_s); + addExpectedKey(u"etag"_s); + addExpectedKey(u"version"_s); } PutRoomKeysVersionJob::PutRoomKeysVersionJob(const QString& version, const QString& algorithm, @@ -84,8 +84,8 @@ PutRoomKeyBySessionIdJob::PutRoomKeyBySessionIdJob(const QString& roomId, const queryToPutRoomKeyBySessionId(version)) { setRequestData({ toJson(data) }); - addExpectedKey("etag"); - addExpectedKey("count"); + addExpectedKey(u"etag"_s); + addExpectedKey(u"count"_s); } auto queryToGetRoomKeyBySessionId(const QString& version) @@ -134,8 +134,8 @@ DeleteRoomKeyBySessionIdJob::DeleteRoomKeyBySessionIdJob(const QString& roomId, makePath("/_matrix/client/v3", "/room_keys/keys/", roomId, "/", sessionId), queryToDeleteRoomKeyBySessionId(version)) { - addExpectedKey("etag"); - addExpectedKey("count"); + addExpectedKey(u"etag"_s); + addExpectedKey(u"count"_s); } auto queryToPutRoomKeysByRoomId(const QString& version) @@ -152,8 +152,8 @@ PutRoomKeysByRoomIdJob::PutRoomKeysByRoomIdJob(const QString& roomId, const QStr queryToPutRoomKeysByRoomId(version)) { setRequestData({ toJson(backupData) }); - addExpectedKey("etag"); - addExpectedKey("count"); + addExpectedKey(u"etag"_s); + addExpectedKey(u"count"_s); } auto queryToGetRoomKeysByRoomId(const QString& version) @@ -197,8 +197,8 @@ DeleteRoomKeysByRoomIdJob::DeleteRoomKeysByRoomIdJob(const QString& roomId, cons makePath("/_matrix/client/v3", "/room_keys/keys/", roomId), queryToDeleteRoomKeysByRoomId(version)) { - addExpectedKey("etag"); - addExpectedKey("count"); + addExpectedKey(u"etag"_s); + addExpectedKey(u"count"_s); } auto queryToPutRoomKeys(const QString& version) @@ -215,8 +215,8 @@ PutRoomKeysJob::PutRoomKeysJob(const QString& version, const QHash(_dataJson, "rooms"_L1, rooms); setRequestData({ _dataJson }); - addExpectedKey("etag"); - addExpectedKey("count"); + addExpectedKey(u"etag"_s); + addExpectedKey(u"count"_s); } auto queryToGetRoomKeys(const QString& version) @@ -236,7 +236,7 @@ GetRoomKeysJob::GetRoomKeysJob(const QString& version) : BaseJob(HttpVerb::Get, u"GetRoomKeysJob"_s, makePath("/_matrix/client/v3", "/room_keys/keys"), queryToGetRoomKeys(version)) { - addExpectedKey("rooms"); + addExpectedKey(u"rooms"_s); } auto queryToDeleteRoomKeys(const QString& version) @@ -256,6 +256,6 @@ DeleteRoomKeysJob::DeleteRoomKeysJob(const QString& version) : BaseJob(HttpVerb::Delete, u"DeleteRoomKeysJob"_s, makePath("/_matrix/client/v3", "/room_keys/keys"), queryToDeleteRoomKeys(version)) { - addExpectedKey("etag"); - addExpectedKey("count"); + addExpectedKey(u"etag"_s); + addExpectedKey(u"count"_s); } diff --git a/Quotient/csapi/keys.cpp b/Quotient/csapi/keys.cpp index 134e54b0f..939981195 100644 --- a/Quotient/csapi/keys.cpp +++ b/Quotient/csapi/keys.cpp @@ -13,7 +13,7 @@ UploadKeysJob::UploadKeysJob(const std::optional& deviceKeys, addParam(_dataJson, "one_time_keys"_L1, oneTimeKeys); addParam(_dataJson, "fallback_keys"_L1, fallbackKeys); setRequestData({ _dataJson }); - addExpectedKey("one_time_key_counts"); + addExpectedKey(u"one_time_key_counts"_s); } QueryKeysJob::QueryKeysJob(const QHash& deviceKeys, std::optional timeout) @@ -33,7 +33,7 @@ ClaimKeysJob::ClaimKeysJob(const QHash>& oneTime addParam(_dataJson, "timeout"_L1, timeout); addParam<>(_dataJson, "one_time_keys"_L1, oneTimeKeys); setRequestData({ _dataJson }); - addExpectedKey("one_time_keys"); + addExpectedKey(u"one_time_keys"_s); } auto queryToGetKeysChanges(const QString& from, const QString& to) diff --git a/Quotient/csapi/knocking.cpp b/Quotient/csapi/knocking.cpp index 7deca21c6..9660e0173 100644 --- a/Quotient/csapi/knocking.cpp +++ b/Quotient/csapi/knocking.cpp @@ -19,5 +19,5 @@ KnockRoomJob::KnockRoomJob(const QString& roomIdOrAlias, const QStringList& serv QJsonObject _dataJson; addParam(_dataJson, "reason"_L1, reason); setRequestData({ _dataJson }); - addExpectedKey("room_id"); + addExpectedKey(u"room_id"_s); } diff --git a/Quotient/csapi/list_joined_rooms.cpp b/Quotient/csapi/list_joined_rooms.cpp index 20dbf9d86..d85cb6604 100644 --- a/Quotient/csapi/list_joined_rooms.cpp +++ b/Quotient/csapi/list_joined_rooms.cpp @@ -12,5 +12,5 @@ QUrl GetJoinedRoomsJob::makeRequestUrl(const HomeserverData& hsData) GetJoinedRoomsJob::GetJoinedRoomsJob() : BaseJob(HttpVerb::Get, u"GetJoinedRoomsJob"_s, makePath("/_matrix/client/v3", "/joined_rooms")) { - addExpectedKey("joined_rooms"); + addExpectedKey(u"joined_rooms"_s); } diff --git a/Quotient/csapi/list_public_rooms.cpp b/Quotient/csapi/list_public_rooms.cpp index fc7de648e..471a3b22d 100644 --- a/Quotient/csapi/list_public_rooms.cpp +++ b/Quotient/csapi/list_public_rooms.cpp @@ -47,7 +47,7 @@ GetPublicRoomsJob::GetPublicRoomsJob(std::optional limit, const QString& si : BaseJob(HttpVerb::Get, u"GetPublicRoomsJob"_s, makePath("/_matrix/client/v3", "/publicRooms"), queryToGetPublicRooms(limit, since, server), {}, false) { - addExpectedKey("chunk"); + addExpectedKey(u"chunk"_s); } auto queryToQueryPublicRooms(const QString& server) @@ -71,5 +71,5 @@ QueryPublicRoomsJob::QueryPublicRoomsJob(const QString& server, std::optional(_dataJson, "include_all_networks"_L1, includeAllNetworks); addParam(_dataJson, "third_party_instance_id"_L1, thirdPartyInstanceId); setRequestData({ _dataJson }); - addExpectedKey("chunk"); + addExpectedKey(u"chunk"_s); } diff --git a/Quotient/csapi/login.cpp b/Quotient/csapi/login.cpp index e45d768aa..b2ae2d8ef 100644 --- a/Quotient/csapi/login.cpp +++ b/Quotient/csapi/login.cpp @@ -27,7 +27,7 @@ LoginJob::LoginJob(const QString& type, const std::optional& ide addParam(_dataJson, "initial_device_display_name"_L1, initialDeviceDisplayName); addParam(_dataJson, "refresh_token"_L1, refreshToken); setRequestData({ _dataJson }); - addExpectedKey("user_id"); - addExpectedKey("access_token"); - addExpectedKey("device_id"); + addExpectedKey(u"user_id"_s); + addExpectedKey(u"access_token"_s); + addExpectedKey(u"device_id"_s); } diff --git a/Quotient/csapi/login_token.cpp b/Quotient/csapi/login_token.cpp index f04ae3840..3ad217734 100644 --- a/Quotient/csapi/login_token.cpp +++ b/Quotient/csapi/login_token.cpp @@ -11,6 +11,6 @@ GenerateLoginTokenJob::GenerateLoginTokenJob(const std::optional(_dataJson, "auth"_L1, auth); setRequestData({ _dataJson }); - addExpectedKey("login_token"); - addExpectedKey("expires_in_ms"); + addExpectedKey(u"login_token"_s); + addExpectedKey(u"expires_in_ms"_s); } diff --git a/Quotient/csapi/message_pagination.cpp b/Quotient/csapi/message_pagination.cpp index 1216c5e98..36c9bcb54 100644 --- a/Quotient/csapi/message_pagination.cpp +++ b/Quotient/csapi/message_pagination.cpp @@ -32,6 +32,6 @@ GetRoomEventsJob::GetRoomEventsJob(const QString& roomId, const QString& dir, co makePath("/_matrix/client/v3", "/rooms/", roomId, "/messages"), queryToGetRoomEvents(from, to, dir, limit, filter)) { - addExpectedKey("start"); - addExpectedKey("chunk"); + addExpectedKey(u"start"_s); + addExpectedKey(u"chunk"_s); } diff --git a/Quotient/csapi/notifications.cpp b/Quotient/csapi/notifications.cpp index 21f344d77..bef2049f9 100644 --- a/Quotient/csapi/notifications.cpp +++ b/Quotient/csapi/notifications.cpp @@ -26,5 +26,5 @@ GetNotificationsJob::GetNotificationsJob(const QString& from, std::optional makePath("/_matrix/client/v3", "/notifications"), queryToGetNotifications(from, limit, only)) { - addExpectedKey("notifications"); + addExpectedKey(u"notifications"_s); } diff --git a/Quotient/csapi/presence.cpp b/Quotient/csapi/presence.cpp index 37fb1f8cb..604d4a69e 100644 --- a/Quotient/csapi/presence.cpp +++ b/Quotient/csapi/presence.cpp @@ -25,5 +25,5 @@ GetPresenceJob::GetPresenceJob(const QString& userId) : BaseJob(HttpVerb::Get, u"GetPresenceJob"_s, makePath("/_matrix/client/v3", "/presence/", userId, "/status")) { - addExpectedKey("presence"); + addExpectedKey(u"presence"_s); } diff --git a/Quotient/csapi/pushrules.cpp b/Quotient/csapi/pushrules.cpp index f39693a22..0375cae04 100644 --- a/Quotient/csapi/pushrules.cpp +++ b/Quotient/csapi/pushrules.cpp @@ -12,7 +12,7 @@ QUrl GetPushRulesJob::makeRequestUrl(const HomeserverData& hsData) GetPushRulesJob::GetPushRulesJob() : BaseJob(HttpVerb::Get, u"GetPushRulesJob"_s, makePath("/_matrix/client/v3", "/pushrules")) { - addExpectedKey("global"); + addExpectedKey(u"global"_s); } QUrl GetPushRuleJob::makeRequestUrl(const HomeserverData& hsData, const QString& scope, @@ -76,7 +76,7 @@ IsPushRuleEnabledJob::IsPushRuleEnabledJob(const QString& scope, const QString& makePath("/_matrix/client/v3", "/pushrules/", scope, "/", kind, "/", ruleId, "/enabled")) { - addExpectedKey("enabled"); + addExpectedKey(u"enabled"_s); } SetPushRuleEnabledJob::SetPushRuleEnabledJob(const QString& scope, const QString& kind, @@ -103,7 +103,7 @@ GetPushRuleActionsJob::GetPushRuleActionsJob(const QString& scope, const QString makePath("/_matrix/client/v3", "/pushrules/", scope, "/", kind, "/", ruleId, "/actions")) { - addExpectedKey("actions"); + addExpectedKey(u"actions"_s); } SetPushRuleActionsJob::SetPushRuleActionsJob(const QString& scope, const QString& kind, diff --git a/Quotient/csapi/refresh.cpp b/Quotient/csapi/refresh.cpp index b7243e3ec..1e863af17 100644 --- a/Quotient/csapi/refresh.cpp +++ b/Quotient/csapi/refresh.cpp @@ -10,5 +10,5 @@ RefreshJob::RefreshJob(const QString& refreshToken) QJsonObject _dataJson; addParam<>(_dataJson, "refresh_token"_L1, refreshToken); setRequestData({ _dataJson }); - addExpectedKey("access_token"); + addExpectedKey(u"access_token"_s); } diff --git a/Quotient/csapi/registration.cpp b/Quotient/csapi/registration.cpp index f12b093c5..a45072270 100644 --- a/Quotient/csapi/registration.cpp +++ b/Quotient/csapi/registration.cpp @@ -27,7 +27,7 @@ RegisterJob::RegisterJob(const QString& kind, const std::optional(_dataJson, "inhibit_login"_L1, inhibitLogin); addParam(_dataJson, "refresh_token"_L1, refreshToken); setRequestData({ _dataJson }); - addExpectedKey("user_id"); + addExpectedKey(u"user_id"_s); } RequestTokenToRegisterEmailJob::RequestTokenToRegisterEmailJob(const EmailValidationData& data) @@ -82,7 +82,7 @@ DeactivateAccountJob::DeactivateAccountJob(const std::optional(_dataJson, "id_server"_L1, idServer); addParam(_dataJson, "erase"_L1, erase); setRequestData({ _dataJson }); - addExpectedKey("id_server_unbind_result"); + addExpectedKey(u"id_server_unbind_result"_s); } auto queryToCheckUsernameAvailability(const QString& username) diff --git a/Quotient/csapi/registration_tokens.cpp b/Quotient/csapi/registration_tokens.cpp index b9c42d126..3606966ed 100644 --- a/Quotient/csapi/registration_tokens.cpp +++ b/Quotient/csapi/registration_tokens.cpp @@ -24,5 +24,5 @@ RegistrationTokenValidityJob::RegistrationTokenValidityJob(const QString& token) makePath("/_matrix/client/v1", "/register/m.login.registration_token/validity"), queryToRegistrationTokenValidity(token), {}, false) { - addExpectedKey("valid"); + addExpectedKey(u"valid"_s); } diff --git a/Quotient/csapi/relations.cpp b/Quotient/csapi/relations.cpp index 755983357..ab850c079 100644 --- a/Quotient/csapi/relations.cpp +++ b/Quotient/csapi/relations.cpp @@ -35,7 +35,7 @@ GetRelatingEventsJob::GetRelatingEventsJob(const QString& roomId, const QString& makePath("/_matrix/client/v1", "/rooms/", roomId, "/relations/", eventId), queryToGetRelatingEvents(from, to, limit, dir, recurse)) { - addExpectedKey("chunk"); + addExpectedKey(u"chunk"_s); } auto queryToGetRelatingEventsWithRelType(const QString& from, const QString& to, @@ -71,7 +71,7 @@ GetRelatingEventsWithRelTypeJob::GetRelatingEventsWithRelTypeJob( relType), queryToGetRelatingEventsWithRelType(from, to, limit, dir, recurse)) { - addExpectedKey("chunk"); + addExpectedKey(u"chunk"_s); } auto queryToGetRelatingEventsWithRelTypeAndEventType(const QString& from, const QString& to, @@ -108,5 +108,5 @@ GetRelatingEventsWithRelTypeAndEventTypeJob::GetRelatingEventsWithRelTypeAndEven relType, "/", eventType), queryToGetRelatingEventsWithRelTypeAndEventType(from, to, limit, dir, recurse)) { - addExpectedKey("chunk"); + addExpectedKey(u"chunk"_s); } diff --git a/Quotient/csapi/room_event_by_timestamp.cpp b/Quotient/csapi/room_event_by_timestamp.cpp index d472fd71e..d3fae2a8c 100644 --- a/Quotient/csapi/room_event_by_timestamp.cpp +++ b/Quotient/csapi/room_event_by_timestamp.cpp @@ -26,6 +26,6 @@ GetEventByTimestampJob::GetEventByTimestampJob(const QString& roomId, int ts, co makePath("/_matrix/client/v1", "/rooms/", roomId, "/timestamp_to_event"), queryToGetEventByTimestamp(ts, dir)) { - addExpectedKey("event_id"); - addExpectedKey("origin_server_ts"); + addExpectedKey(u"event_id"_s); + addExpectedKey(u"origin_server_ts"_s); } diff --git a/Quotient/csapi/room_send.cpp b/Quotient/csapi/room_send.cpp index 2880cd514..8f8da7c0d 100644 --- a/Quotient/csapi/room_send.cpp +++ b/Quotient/csapi/room_send.cpp @@ -10,5 +10,5 @@ SendMessageJob::SendMessageJob(const QString& roomId, const QString& eventType, makePath("/_matrix/client/v3", "/rooms/", roomId, "/send/", eventType, "/", txnId)) { setRequestData({ toJson(content) }); - addExpectedKey("event_id"); + addExpectedKey(u"event_id"_s); } diff --git a/Quotient/csapi/room_state.cpp b/Quotient/csapi/room_state.cpp index 158bdf3b5..d2da130c4 100644 --- a/Quotient/csapi/room_state.cpp +++ b/Quotient/csapi/room_state.cpp @@ -10,5 +10,5 @@ SetRoomStateWithKeyJob::SetRoomStateWithKeyJob(const QString& roomId, const QStr makePath("/_matrix/client/v3", "/rooms/", roomId, "/state/", eventType, "/", stateKey)) { setRequestData({ toJson(content) }); - addExpectedKey("event_id"); + addExpectedKey(u"event_id"_s); } diff --git a/Quotient/csapi/room_upgrades.cpp b/Quotient/csapi/room_upgrades.cpp index 8efc4422c..28fe34361 100644 --- a/Quotient/csapi/room_upgrades.cpp +++ b/Quotient/csapi/room_upgrades.cpp @@ -11,5 +11,5 @@ UpgradeRoomJob::UpgradeRoomJob(const QString& roomId, const QString& newVersion) QJsonObject _dataJson; addParam<>(_dataJson, "new_version"_L1, newVersion); setRequestData({ _dataJson }); - addExpectedKey("replacement_room"); + addExpectedKey(u"replacement_room"_s); } diff --git a/Quotient/csapi/search.cpp b/Quotient/csapi/search.cpp index a31093482..b176ee4d6 100644 --- a/Quotient/csapi/search.cpp +++ b/Quotient/csapi/search.cpp @@ -18,5 +18,5 @@ SearchJob::SearchJob(const Categories& searchCategories, const QString& nextBatc QJsonObject _dataJson; addParam<>(_dataJson, "search_categories"_L1, searchCategories); setRequestData({ _dataJson }); - addExpectedKey("search_categories"); + addExpectedKey(u"search_categories"_s); } diff --git a/Quotient/csapi/space_hierarchy.cpp b/Quotient/csapi/space_hierarchy.cpp index 95405105b..ca13d191f 100644 --- a/Quotient/csapi/space_hierarchy.cpp +++ b/Quotient/csapi/space_hierarchy.cpp @@ -32,5 +32,5 @@ GetSpaceHierarchyJob::GetSpaceHierarchyJob(const QString& roomId, std::optional< makePath("/_matrix/client/v1", "/rooms/", roomId, "/hierarchy"), queryToGetSpaceHierarchy(suggestedOnly, limit, maxDepth, from)) { - addExpectedKey("rooms"); + addExpectedKey(u"rooms"_s); } diff --git a/Quotient/csapi/threads_list.cpp b/Quotient/csapi/threads_list.cpp index dec3f3a48..65edc5ab4 100644 --- a/Quotient/csapi/threads_list.cpp +++ b/Quotient/csapi/threads_list.cpp @@ -28,5 +28,5 @@ GetThreadRootsJob::GetThreadRootsJob(const QString& roomId, const QString& inclu makePath("/_matrix/client/v1", "/rooms/", roomId, "/threads"), queryToGetThreadRoots(include, limit, from)) { - addExpectedKey("chunk"); + addExpectedKey(u"chunk"_s); } diff --git a/Quotient/csapi/users.cpp b/Quotient/csapi/users.cpp index 2a32d03d4..1995b7abb 100644 --- a/Quotient/csapi/users.cpp +++ b/Quotient/csapi/users.cpp @@ -12,6 +12,6 @@ SearchUserDirectoryJob::SearchUserDirectoryJob(const QString& searchTerm, std::o addParam<>(_dataJson, "search_term"_L1, searchTerm); addParam(_dataJson, "limit"_L1, limit); setRequestData({ _dataJson }); - addExpectedKey("results"); - addExpectedKey("limited"); + addExpectedKey(u"results"_s); + addExpectedKey(u"limited"_s); } diff --git a/Quotient/csapi/versions.cpp b/Quotient/csapi/versions.cpp index 5010d1d77..c8817d57c 100644 --- a/Quotient/csapi/versions.cpp +++ b/Quotient/csapi/versions.cpp @@ -12,5 +12,5 @@ QUrl GetVersionsJob::makeRequestUrl(const HomeserverData& hsData) GetVersionsJob::GetVersionsJob() : BaseJob(HttpVerb::Get, u"GetVersionsJob"_s, makePath("/_matrix/client", "/versions")) { - addExpectedKey("versions"); + addExpectedKey(u"versions"_s); } diff --git a/Quotient/csapi/whoami.cpp b/Quotient/csapi/whoami.cpp index 265d505b3..5eaac35cf 100644 --- a/Quotient/csapi/whoami.cpp +++ b/Quotient/csapi/whoami.cpp @@ -13,5 +13,5 @@ GetTokenOwnerJob::GetTokenOwnerJob() : BaseJob(HttpVerb::Get, u"GetTokenOwnerJob"_s, makePath("/_matrix/client/v3", "/account/whoami")) { - addExpectedKey("user_id"); + addExpectedKey(u"user_id"_s); } diff --git a/Quotient/jobs/basejob.cpp b/Quotient/jobs/basejob.cpp index 4529d46bb..3b1025186 100644 --- a/Quotient/jobs/basejob.cpp +++ b/Quotient/jobs/basejob.cpp @@ -17,6 +17,8 @@ #include #include +#include + using namespace Quotient; using std::chrono::seconds, std::chrono::milliseconds; using namespace std::chrono_literals; @@ -122,7 +124,7 @@ class Q_DECL_HIDDEN BaseJob::Private { // type QMimeType is of little help with MIME type globs (`text/*` etc.) QByteArrayList expectedContentTypes { "application/json" }; - QByteArrayList expectedKeys; + QStringList expectedKeys; // When the QNetworkAccessManager is destroyed it destroys all pending replies. // Using QPointer allows us to know when that happend. @@ -257,11 +259,11 @@ void BaseJob::setExpectedContentTypes(const QByteArrayList& contentTypes) d->expectedContentTypes = contentTypes; } -QByteArrayList BaseJob::expectedKeys() const { return d->expectedKeys; } +QStringList BaseJob::expectedKeys() const { return d->expectedKeys; } -void BaseJob::addExpectedKey(const QByteArray& key) { d->expectedKeys << key; } +void BaseJob::addExpectedKey(QString key) { d->expectedKeys << std::move(key); } -void BaseJob::setExpectedKeys(const QByteArrayList& keys) +void BaseJob::setExpectedKeys(const QStringList& keys) { d->expectedKeys = keys; } @@ -410,21 +412,19 @@ void BaseJob::gotReply() { // Defer actually updating the status until it's finalised auto statusSoFar = checkReply(reply()); - if (statusSoFar.good() - && d->expectedContentTypes == QByteArrayList { "application/json" }) // - { + if (statusSoFar.good() && d->expectedContentTypes == QByteArrayList{ "application/json"_ba }) { d->rawResponse = reply()->readAll(); statusSoFar = d->parseJson(); - if (statusSoFar.good() && !expectedKeys().empty()) { - const auto& responseObject = jsonData(); - QByteArrayList missingKeys; - for (const auto& k: expectedKeys()) - if (!responseObject.contains(QString::fromLatin1(k))) - missingKeys.push_back(k); - if (!missingKeys.empty()) + if (statusSoFar.good()) { + auto filteredView = + std::views::filter(expectedKeys(), [responseObject = jsonData()](const QString& k) { + return !responseObject.contains(k); + }); + if (const auto missingKeys = + QStringList(filteredView.begin(), filteredView.end()).join(u','); + !missingKeys.isEmpty()) [[unlikely]] statusSoFar = { IncorrectResponse, - tr("Required JSON keys missing: ") - + QString::fromLatin1(missingKeys.join()) }; + tr("Required JSON keys missing: ") + missingKeys }; } setStatus(statusSoFar); if (!status().good()) // Bad JSON in a "good" reply: bail out diff --git a/Quotient/jobs/basejob.h b/Quotient/jobs/basejob.h index bb0f278b4..c0ab961df 100644 --- a/Quotient/jobs/basejob.h +++ b/Quotient/jobs/basejob.h @@ -322,9 +322,9 @@ public Q_SLOTS: const QByteArrayList& expectedContentTypes() const; void addExpectedContentType(const QByteArray& contentType); void setExpectedContentTypes(const QByteArrayList& contentTypes); - QByteArrayList expectedKeys() const; - void addExpectedKey(const QByteArray &key); - void setExpectedKeys(const QByteArrayList &keys); + QStringList expectedKeys() const; + void addExpectedKey(QString key); + void setExpectedKeys(const QStringList& keys); const QNetworkReply* reply() const; QNetworkReply* reply(); diff --git a/Quotient/room.h b/Quotient/room.h index 7cb96df52..f2d86c8e0 100644 --- a/Quotient/room.h +++ b/Quotient/room.h @@ -707,17 +707,8 @@ class QUOTIENT_API Room : public QObject { //! \brief Set a state event of the given type with the given arguments //! - //! This typesafe overload attempts to send a state event with the type - //! \p EvT and the content defined by \p args. Specifically, the function - //! constructs a temporary object of type \p EvT with its content - //! list-initialised from \p args, and sends a request to the homeserver - //! using the Matrix event type defined by \p EvT and the event content - //! produced via EvT::contentJson(). - //! - //! \note This call is not suitable for events that assume non-empty - //! stateKey, such as member events; for those you have to create - //! a temporary event object yourself and use the setState() overload - //! that accepts StateEvent const-ref. + //! This type-safe overload attempts to send a state event of the type \p EvT constructed from + //! \p args. template auto setState(ArgTs&&... args) { diff --git a/Quotient/roommember.h b/Quotient/roommember.h index 9d81f9a9b..34fb63b93 100644 --- a/Quotient/roommember.h +++ b/Quotient/roommember.h @@ -13,20 +13,22 @@ namespace Quotient { class Room; class RoomMemberEvent; -//! This class is for visualizing a user in a room context. +//! \brief Representation of a user state in a room //! -//! The class is intentionally a read-only data object that is effectively a wrapper -//! around an m.room.member event for the desired user. This is designed provide the -//! data in a format ready for visualizing a user (avatar or name) in the context -//! of the room it was generated in. This means that if a user has set a unique -//! name or avatar for a particular room that is what will be returned. +//! The class is intentionally a read-only data object that is effectively a wrapper around an +//! `m.room.member` event for the desired user. This is designed to provide the data in a format +//! ready for visualizing a user (avatar or name) in the context of the room it was generated in. +//! This means that if a user has set a unique name or avatar for a particular room that is what +//! will be returned. //! -//! \note The RoomMember class is not intended for interacting with a User's profile. -//! For that a Quotient::User object should be obtained from a -//! Quotient::Connection as that has the support functions for modifying profile -//! information. -//! -//! \sa Quotient::User +//! \note The RoomMember class is not intended for interacting with the user's profile. +//! For that a Quotient::User object should be obtained from a Quotient::Connection as that +//! has the support functions for modifying profile information. +//! \warning RoomMember is a gadget class and should not be kept between syncs. It does not track +//! changes of the member state therefore some member changes (i.e. leaving the room) may +//! render a RoomMember dangling, when calling any of its methods leads to undefined +//! behaviour. +//! \sa User class QUOTIENT_API RoomMember { Q_GADGET Q_PROPERTY(bool isEmpty READ isEmpty CONSTANT) diff --git a/gtad/operation.cpp.mustache b/gtad/operation.cpp.mustache index fcce7c2d0..46ab8bc3e 100644 --- a/gtad/operation.cpp.mustache +++ b/gtad/operation.cpp.mustache @@ -50,7 +50,7 @@ QUrl {{>titleCaseOperationId}}Job::makeRequestUrl(const HomeserverData& hsData{{ setExpectedContentTypes({ {{#produces}}"{{_}}"{{>cjoin}}{{/produces}} }); {{/producesNonJson?}}{{^producesNonJson? }}{{#responses}}{{#normalResponse?}}{{#properties}}{{#required?}} - addExpectedKey("{{baseName}}"); + addExpectedKey(u"{{baseName}}"_s); {{/required?}}{{/properties}}{{/normalResponse?}}{{/responses }}{{/producesNonJson?}} }