From: Christian Kamm Date: Thu, 15 Nov 2018 07:32:17 +0000 (+0100) Subject: Generalize Result<> class, add Optional<> X-Git-Tag: archive/raspbian/3.16.7-1_deb13u1+rpi1~1^2~12^2~21^2~468^2~391 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=f502a526fa53ef584ae5649858456ac070490d4d;p=nextcloud-desktop.git Generalize Result<> class, add Optional<> To make it nicer to use outside of HTTP results. --- diff --git a/src/common/result.h b/src/common/result.h new file mode 100644 index 000000000..5db087b54 --- /dev/null +++ b/src/common/result.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) by Olivier Goffart + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#pragma once + +#include "asserts.h" + +namespace OCC { + +/** + * A Result of type T, or an Error + */ +template +class Result +{ + union { + T _result; + Error _error; + }; + bool _isError; + +public: + Result(T value) + : _result(std::move(value)) + , _isError(false) + { + } + // TODO: This doesn't work if T and Error are too similar + Result(Error error) + : _error(std::move(error)) + , _isError(true) + { + } + + ~Result() + { + if (_isError) + _error.~Error(); + else + _result.~T(); + } + explicit operator bool() const { return !_isError; } + const T &operator*() const & + { + ASSERT(!_isError); + return _result; + } + T operator*() && + { + ASSERT(!_isError); + return std::move(_result); + } + const Error &error() const & + { + ASSERT(_isError); + return _error; + } + Error error() && + { + ASSERT(_isError); + return std::move(_error); + } +}; + +namespace detail { +struct OptionalNoErrorData{}; +} + +template +class Optional : public Result +{ +public: + using Result::Result; + + Optional() + : Optional(detail::OptionalNoErrorData{}) + { + } +}; + +} // namespace OCC diff --git a/src/libsync/discovery.cpp b/src/libsync/discovery.cpp index 8d6c5dcf9..1924187eb 100644 --- a/src/libsync/discovery.cpp +++ b/src/libsync/discovery.cpp @@ -522,10 +522,10 @@ void ProcessDirectoryJob::processFileAnalyzeRemoteInfo( // we need to make a request to the server to know that the original file is deleted on the server _pendingAsyncJobs++; auto job = new RequestEtagJob(_discoveryData->_account, originalPath, this); - connect(job, &RequestEtagJob::finishedWithResult, this, [=](const Result &etag) mutable { + connect(job, &RequestEtagJob::finishedWithResult, this, [=](const HttpResult &etag) mutable { _pendingAsyncJobs--; QTimer::singleShot(0, _discoveryData, &DiscoveryPhase::scheduleMoreJobs); - if (etag.errorCode() != 404 || + if (etag || etag.error().code != 404 || // Somehow another item claimed this original path, consider as if it existed _discoveryData->_renamedItems.contains(originalPath)) { // If the file exist or if there is another error, consider it is a new file. @@ -828,7 +828,7 @@ void ProcessDirectoryJob::processFileAnalyzeLocalInfo( if (base.isVirtualFile() && isVfsWithSuffix()) chopVirtualFileSuffix(serverOriginalPath); auto job = new RequestEtagJob(_discoveryData->_account, serverOriginalPath, this); - connect(job, &RequestEtagJob::finishedWithResult, this, [=](const Result &etag) mutable { + connect(job, &RequestEtagJob::finishedWithResult, this, [=](const HttpResult &etag) mutable { if (!etag || (*etag != base._etag && !item->isDirectory()) || _discoveryData->_renamedItems.contains(originalPath)) { qCInfo(lcDisco) << "Can't rename because the etag has changed or the directory is gone" << originalPath; // Can't be a rename, leave it as a new. @@ -1220,17 +1220,17 @@ DiscoverySingleDirectoryJob *ProcessDirectoryJob::startAsyncServerQuery() if (_localQueryDone) process(); } else { - if (results.errorCode() == 403) { + if (results.error().code == 403) { // 403 Forbidden can be sent by the server if the file firewall is active. // A file or directory should be ignored and sync must continue. See #3490 qCWarning(lcDisco, "Directory access Forbidden (File Firewall?)"); if (_dirItem) { _dirItem->_instruction = CSYNC_INSTRUCTION_IGNORE; - _dirItem->_errorString = results.errorMessage(); + _dirItem->_errorString = results.error().message; emit finished(); return; } - } else if (results.errorCode() == 503) { + } else if (results.error().code == 503) { // The server usually replies with the custom "503 Storage not available" // if some path is temporarily unavailable. But in some cases a standard 503 // is returned too. Thus we can't distinguish the two and will treat any @@ -1238,13 +1238,13 @@ DiscoverySingleDirectoryJob *ProcessDirectoryJob::startAsyncServerQuery() qCWarning(lcDisco(), "Storage was not available!"); if (_dirItem) { _dirItem->_instruction = CSYNC_INSTRUCTION_IGNORE; - _dirItem->_errorString = results.errorMessage(); + _dirItem->_errorString = results.error().message; emit finished(); return; } } emit _discoveryData->fatalError(tr("Server replied with an error while reading directory '%1' : %2") - .arg(_currentFolder._server, results.errorMessage())); + .arg(_currentFolder._server, results.error().message)); } }); connect(serverJob, &DiscoverySingleDirectoryJob::firstDirectoryPermissions, this, diff --git a/src/libsync/discoveryphase.cpp b/src/libsync/discoveryphase.cpp index e845c352d..e036f79f3 100644 --- a/src/libsync/discoveryphase.cpp +++ b/src/libsync/discoveryphase.cpp @@ -355,11 +355,11 @@ void DiscoverySingleDirectoryJob::lsJobFinishedWithoutErrorSlot() if (!_ignoredFirst) { // This is a sanity check, if we haven't _ignoredFirst then it means we never received any directoryListingIteratedSlot // which means somehow the server XML was bogus - emit finished({ 0, tr("Server error: PROPFIND reply is not XML formatted!") }); + emit finished(HttpError{ 0, tr("Server error: PROPFIND reply is not XML formatted!") }); deleteLater(); return; } else if (!_error.isEmpty()) { - emit finished({ 0, _error }); + emit finished(HttpError{ 0, _error }); deleteLater(); return; } @@ -379,7 +379,7 @@ void DiscoverySingleDirectoryJob::lsJobFinishedWithErrorSlot(QNetworkReply *r) && !contentType.contains("application/xml; charset=utf-8")) { msg = tr("Server error: PROPFIND reply is not XML formatted!"); } - emit finished({httpCode, msg}); + emit finished(HttpError{ httpCode, msg }); deleteLater(); } } diff --git a/src/libsync/discoveryphase.h b/src/libsync/discoveryphase.h index 1bd9dd06b..6f6b4ffd6 100644 --- a/src/libsync/discoveryphase.h +++ b/src/libsync/discoveryphase.h @@ -97,7 +97,7 @@ public: signals: void firstDirectoryPermissions(RemotePermissions); void etag(const QString &); - void finished(const Result> &result); + void finished(const HttpResult> &result); private slots: void directoryListingIteratedSlot(QString, const QMap &); void lsJobFinishedWithoutErrorSlot(); diff --git a/src/libsync/networkjobs.cpp b/src/libsync/networkjobs.cpp index 18f9097e3..fc953c3cb 100644 --- a/src/libsync/networkjobs.cpp +++ b/src/libsync/networkjobs.cpp @@ -107,7 +107,7 @@ bool RequestEtagJob::finished() emit etagRetrieved(etag); emit finishedWithResult(etag); } else { - emit finishedWithResult({ httpCode, errorString() }); + emit finishedWithResult(HttpError{ httpCode, errorString() }); } return true; } diff --git a/src/libsync/networkjobs.h b/src/libsync/networkjobs.h index 634e792a7..a29365c3a 100644 --- a/src/libsync/networkjobs.h +++ b/src/libsync/networkjobs.h @@ -18,7 +18,7 @@ #include "abstractnetworkjob.h" -#include "result.h" +#include "common/result.h" #include #include @@ -29,6 +29,15 @@ class QJsonObject; namespace OCC { +struct HttpError +{ + int code; // HTTP error code + QString message; +}; + +template +using HttpResult = Result; + /** * @brief The EntityExistsJob class * @ingroup libsync @@ -337,7 +346,7 @@ public: signals: void etagRetrieved(const QString &etag); - void finishedWithResult(const Result &etag); + void finishedWithResult(const HttpResult &etag); private slots: bool finished() override; diff --git a/src/libsync/result.h b/src/libsync/result.h deleted file mode 100644 index 58f70c4e7..000000000 --- a/src/libsync/result.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) by Olivier Goffart - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - */ - -#pragma once - -namespace OCC { - -/** - * A Result of type T, or an Error that contains a code and a string - * - * The code is an HTTP error code. - **/ -template -class Result -{ - struct Error - { - QString string; - int code; - }; - union { - T _result; - Error _error; - }; - bool _isError; - -public: - Result(T value) - : _result(std::move(value)) - , _isError(false){}; - Result(int code, QString str) - : _error({ std::move(str), code }) - , _isError(true) - { - } - ~Result() - { - if (_isError) - _error.~Error(); - else - _result.~T(); - } - explicit operator bool() const { return !_isError; } - const T &operator*() const & - { - ASSERT(!_isError); - return _result; - } - T operator*() && - { - ASSERT(!_isError); - return std::move(_result); - } - QString errorMessage() const - { - ASSERT(_isError); - return _error.string; - } - int errorCode() const - { - return _isError ? _error.code : 0; - } -}; - -} // namespace OCC