diff --git a/Quotient/connection.cpp b/Quotient/connection.cpp index 9455a96ec..4facd61f1 100644 --- a/Quotient/connection.cpp +++ b/Quotient/connection.cpp @@ -243,45 +243,46 @@ bool Connection::capabilitiesReady() const QStringList Connection::supportedMatrixSpecVersions() const { return d->data->homeserverData().supportedSpecVersions; } +namespace { +QFuture runKeychainJob(QKeychain::Job* j, const QString& keychainId) +{ + j->setAutoDelete(true); + j->setKey(keychainId); + auto ft = QtFuture::connect(j, &QKeychain::Job::finished); + j->start(); + return ft; +} +} + void Connection::Private::saveAccessTokenToKeychain() const { qCDebug(MAIN) << "Saving access token to keychain for" << q->userId(); - auto job = new QKeychain::WritePasswordJob(qAppName()); - job->setKey(q->userId()); + using namespace QKeychain; + auto job = new WritePasswordJob(qAppName()); job->setBinaryData(data->accessToken()); - job->start(); - QObject::connect(job, &QKeychain::Job::finished, q, [job] { - if (job->error() == QKeychain::Error::NoError) + runKeychainJob(job, q->userId()).then([](const Job* j) { + if (j->error() == Error::NoError) return; - qWarning(MAIN).noquote() - << "Could not save access token to the keychain:" - << qUtf8Printable(job->errorString()); + qWarning(MAIN).noquote() << "Could not save access token to the keychain:" + << qUtf8Printable(j->errorString()); // TODO: emit a signal }); - } void Connection::Private::dropAccessToken() { // TODO: emit a signal on important (i.e. access denied) keychain errors using namespace QKeychain; - qCDebug(MAIN) << "Removing access token from keychain for" << q->userId(); - auto job = new DeletePasswordJob(qAppName()); - job->setKey(q->userId()); - job->start(); - QObject::connect(job, &Job::finished, q, [job] { - if (job->error() == Error::NoError - || job->error() == Error::EntryNotFound) + qCDebug(MAIN) << "Removing access token and pickle from keychain for" << q->userId(); + runKeychainJob(new DeletePasswordJob(qAppName()), q->userId()).then([](const Job* job) { + if (job->error() == Error::NoError || job->error() == Error::EntryNotFound) return; - qWarning(MAIN).noquote() - << "Could not delete access token from the keychain:" - << qUtf8Printable(job->errorString()); + qWarning(MAIN).noquote() << "Could not delete access token from the keychain:" + << qUtf8Printable(job->errorString()); }); - auto pickleJob = new DeletePasswordJob(qAppName()); - pickleJob->setKey(q->userId() + "-Pickle"_L1); - pickleJob->start(); - QObject::connect(job, &Job::finished, q, [job] { + runKeychainJob(new DeletePasswordJob(qAppName()), q->userId() + "-Pickle"_L1) + .then([](const Job* job) { if (job->error() == Error::NoError || job->error() == Error::EntryNotFound) return; diff --git a/Quotient/converters.h b/Quotient/converters.h index 074d23685..8a69a8baf 100644 --- a/Quotient/converters.h +++ b/Quotient/converters.h @@ -46,8 +46,8 @@ struct JsonObjectConverter; //static void dumpTo(QJsonObject&, const T&); // For toJson() and fillJson() to work //static void fillFrom(const QJsonObject&, T&); // For fromJson() and fillFromJson() to work -template -PodT fromJson(const JsonT&); +template +PodT fromJson(const auto&); template struct JsonObjectUnpacker { @@ -121,8 +121,8 @@ inline void fillJson(QJsonObject& json, const T& data) JsonObjectConverter::dumpTo(json, data); } -template -inline PodT fromJson(const JsonT& json) +template +inline PodT fromJson(const auto& json) { // JsonT here can be whatever the respective JsonConverter specialisation // accepts but by default it's QJsonValue, QJsonDocument, or QJsonObject @@ -133,8 +133,8 @@ inline PodT fromJson(const JsonT& json) // the coder to explicitly type it. It still enforces the // overwrite-everything semantics of fromJson(), unlike fillFromJson() -template -inline void fromJson(const JsonT& json, PodT& pod) +template +inline void fromJson(const auto& json, PodT& pod) { pod = fromJson(json); } @@ -161,8 +161,8 @@ namespace _impl { //! iterable container of string'y values (const char*, QLatin1String, etc.) //! matching respective enum values, 0-based. //! \sa enumToJsonString -template -inline std::optional enumFromJsonString(const QString& s, const EnumStringValuesT& enumValues) +template +inline std::optional enumFromJsonString(const QString& s, const auto& enumValues) { static_assert(std::is_unsigned_v>); if (const auto it = std::ranges::find(enumValues, s); it != cend(enumValues)) @@ -180,8 +180,8 @@ inline std::optional enumFromJsonString(const QString& s, const EnumStrin //! should be defined as { "", "Value1", "", "Value2", "", "Value3" //! } (mind the gap at value 0, in particular). //! \sa enumFromJsonString -template -inline QString enumToJsonString(EnumT v, const EnumStringValuesT& enumValues) +template +inline QString enumToJsonString(EnumT v, const auto& enumValues) { static_assert(std::is_unsigned_v>); if (v < size(enumValues)) @@ -202,8 +202,8 @@ inline QString enumToJsonString(EnumT v, const EnumStringValuesT& enumValues) //! \note Unlike enumFromJsonString, the values start from 1 and not from 0. //! \note This function does not support flag combinations. //! \sa QUO_DECLARE_FLAGS, QUO_DECLARE_FLAGS_NS -template -inline std::optional flagFromJsonString(const QString& s, const FlagStringValuesT& flagValues) +template +inline std::optional flagFromJsonString(const QString& s, const auto& flagValues) { // Enums based on signed integers don't make much sense for flag types static_assert(std::is_unsigned_v>); @@ -213,8 +213,8 @@ inline std::optional flagFromJsonString(const QString& s, const FlagStrin return std::nullopt; } -template -inline QString flagToJsonString(FlagT v, const FlagStringValuesT& flagValues) +template +inline QString flagToJsonString(FlagT v, const auto& flagValues) { static_assert(std::is_unsigned_v>); if (const auto offset = std::countr_zero(std::to_underlying(v)); offset < ssize(flagValues)) @@ -551,8 +551,8 @@ namespace _impl { * by default; passing IfNotEmpty or false for this parameter * enables emptiness checks as described above */ -template -inline void addParam(ContT& container, KeyT&& key, ValT&& value) +template +inline void addParam(auto& container, KeyT&& key, ValT&& value) { _impl::AddNode, Force>::impl(container, std::forward(key), std::forward(value)); diff --git a/Quotient/events/event.h b/Quotient/events/event.h index b146dfb90..4ab78e24f 100644 --- a/Quotient/events/event.h +++ b/Quotient/events/event.h @@ -514,9 +514,8 @@ inline bool is(const Event& e) //! can be either "dumb" (BaseEventT*) or "smart" (`event_ptr_tt<>`). This //! overload doesn't affect the event ownership - if the original pointer owns //! the event it must outlive the downcast pointer to keep it from dangling. -template -inline auto eventCast(const BasePtrT& eptr) - -> decltype(static_cast(std::to_address(eptr))) +template +inline auto eventCast(const auto& eptr) -> decltype(static_cast(std::to_address(eptr))) { return eptr && is>(*eptr) ? static_cast(std::to_address(eptr)) diff --git a/Quotient/events/stateevent.h b/Quotient/events/stateevent.h index 04f5e934f..778d3e73d 100644 --- a/Quotient/events/stateevent.h +++ b/Quotient/events/stateevent.h @@ -90,8 +90,7 @@ class EventTemplate const ContentT& content() const { return _content; } - template - void editContent(VisitorT&& visitor) + void editContent(auto&& visitor) { visitor(_content); editJson()[ContentKey] = toJson(_content); diff --git a/Quotient/jobs/requestdata.cpp b/Quotient/jobs/requestdata.cpp index ab249f6de..536d269df 100644 --- a/Quotient/jobs/requestdata.cpp +++ b/Quotient/jobs/requestdata.cpp @@ -12,7 +12,7 @@ using namespace Quotient; -auto fromData(const QByteArray& data) +auto bufferFromData(const QByteArray& data) { auto source = makeImpl(); source->setData(data); @@ -21,16 +21,17 @@ auto fromData(const QByteArray& data) } template -inline auto fromJson(const JsonDataT& jdata) + requires std::constructible_from +inline auto bufferFromJson(const JsonDataT& jdata) { - return fromData(QJsonDocument(jdata).toJson(QJsonDocument::Compact)); + return bufferFromData(QJsonDocument(jdata).toJson(QJsonDocument::Compact)); } -RequestData::RequestData(const QByteArray& a) : _source(fromData(a)) {} +RequestData::RequestData(const QByteArray& a) : _source(bufferFromData(a)) {} -RequestData::RequestData(const QJsonObject& jo) : _source(fromJson(jo)) {} +RequestData::RequestData(const QJsonObject& jo) : _source(bufferFromJson(jo)) {} -RequestData::RequestData(const QJsonArray& ja) : _source(fromJson(ja)) {} +RequestData::RequestData(const QJsonArray& ja) : _source(bufferFromJson(ja)) {} RequestData::RequestData(QIODevice* source) : _source(acquireImpl(source)) diff --git a/Quotient/ranges_extras.h b/Quotient/ranges_extras.h index 699c26e5c..8692294bf 100644 --- a/Quotient/ranges_extras.h +++ b/Quotient/ranges_extras.h @@ -13,7 +13,8 @@ namespace Quotient { template requires std::indirectly_comparable, const ValT*, std::ranges::equal_to, ProjT> -inline auto findIndex(const RangeT& range, const ValT& value, ProjT proj = {}) +[[nodiscard]] constexpr inline auto findIndex(const RangeT& range, const ValT& value, + ProjT proj = {}) { using namespace std::ranges; return distance(begin(range), find(range, value, std::move(proj))); @@ -27,37 +28,67 @@ inline auto findIndex(const RangeT& range, const ValT& value, ProjT proj = {}) //! available; otherwise, returns the result of calling //! `TargetT(ranges::begin(sourceRange), ranges::end(sourceRange))`. template -[[nodiscard]] constexpr auto rangeTo(SourceT&& sourceRange) +[[nodiscard]] constexpr inline auto rangeTo(SourceT&& sourceRange) { #if defined(__cpp_lib_ranges_to_container) return std::ranges::to(std::forward(sourceRange)); #else - using std::begin, std::end; - return TargetT(begin(sourceRange), end(sourceRange)); + // Provide the minimal necessary subset of what std::ranges::to() can do + using namespace std::ranges; + if constexpr (std::constructible_from) + return TargetT(std::forward(sourceRange)); + else { + using iter_t = iterator_t; + using iter_category_t = typename std::iterator_traits::iterator_category; + if constexpr (requires { + requires common_range; + typename iter_category_t; + requires std::derived_from; + requires std::constructible_from>; + }) + return TargetT(begin(sourceRange), end(sourceRange)); + else { + TargetT c{}; + if constexpr (sized_range + && requires(range_size_t n) { c.reserve(n); }) + c.reserve(static_cast>(size(sourceRange))); + using ValT = std::iter_value_t; + for (auto&& e : sourceRange) { + if constexpr (requires { c.emplace_back(std::forward(e)); }) + c.emplace_back(std::forward(e)); + else if constexpr (requires { c.push_back(std::forward(e)); }) + c.push_back(std::forward(e)); + else if constexpr (requires { c.emplace(c.end(), std::forward(e)); }) + c.emplace(c.end(), std::forward(e)); + else + c.insert(c.end(), std::forward(e)); + } + return c; + } + } #endif } //! An overload that accepts unspecialised container template template